From 12d467a5d380f99d02bf7f15e6be83e284e95a7d Mon Sep 17 00:00:00 2001
From: buhuazhen <hua100783@gmail.com>
Date: 星期二, 26 五月 2026 17:43:55 +0800
Subject: [PATCH] feat: 办公用品(日常用品),领用归还。领用归还记录 完成 ps:如果pro使用直接引入该commit
---
src/main/java/com/ruoyi/stock/service/StockInventoryService.java | 5
src/main/java/com/ruoyi/stock/controller/ProductBorrowReturnController.java | 51 +
src/main/resources/mapper/stock/ProductBorrowMapper.xml | 95 ++
doc/产品领用归还模块-前端对接文档.md | 696 +++++++++++++++
src/main/java/com/ruoyi/stock/service/ProductBorrowReturnService.java | 30
src/main/java/com/ruoyi/stock/service/impl/ProductBorrowReturnServiceImpl.java | 109 ++
src/main/java/com/ruoyi/stock/controller/ProductBorrowController.java | 96 ++
src/main/java/com/ruoyi/stock/pojo/ProductBorrow.java | 96 ++
src/main/java/com/ruoyi/stock/pojo/ProductBorrowReturn.java | 72 +
doc/产品模块与库存模块分析文档.md | 714 ++++++++++++++++
src/main/java/com/ruoyi/basic/service/impl/ProductServiceImpl.java | 28
src/main/java/com/ruoyi/common/enums/StockOutQualifiedRecordTypeEnum.java | 4
src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java | 15
src/main/java/com/ruoyi/common/enums/StockInQualifiedRecordTypeEnum.java | 3
src/main/java/com/ruoyi/stock/dto/ProductBorrowDto.java | 49 +
src/main/java/com/ruoyi/stock/mapper/ProductBorrowReturnMapper.java | 28
src/main/java/com/ruoyi/stock/dto/StockInventoryDto.java | 12
src/main/resources/mapper/stock/ProductBorrowReturnMapper.xml | 80 +
src/main/resources/mapper/stock/StockInventoryMapper.xml | 54 +
doc/20260526_product_borrow_tables.sql | 49 +
src/main/java/com/ruoyi/stock/controller/StockInventoryController.java | 6
src/main/java/com/ruoyi/stock/service/ProductBorrowService.java | 52 +
src/main/java/com/ruoyi/stock/mapper/ProductBorrowMapper.java | 28
src/main/java/com/ruoyi/stock/dto/ProductBorrowReturnDto.java | 38
src/main/java/com/ruoyi/stock/mapper/StockInventoryMapper.java | 5
src/main/java/com/ruoyi/stock/service/impl/ProductBorrowServiceImpl.java | 214 ++++
26 files changed, 2,615 insertions(+), 14 deletions(-)
diff --git a/doc/20260526_product_borrow_tables.sql b/doc/20260526_product_borrow_tables.sql
new file mode 100644
index 0000000..efe9598
--- /dev/null
+++ b/doc/20260526_product_borrow_tables.sql
@@ -0,0 +1,49 @@
+-- 浜у搧棰嗙敤琛�
+CREATE TABLE IF NOT EXISTS `product_borrow` (
+ `id` bigint NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+ `borrow_no` varchar(50) DEFAULT NULL COMMENT '棰嗙敤鍗曞彿',
+ `product_model_id` bigint NOT NULL COMMENT '浜у搧瑙勬牸ID',
+ `batch_no` varchar(100) DEFAULT NULL COMMENT '鎵瑰彿',
+ `borrow_quantity` decimal(18,4) NOT NULL COMMENT '棰嗙敤鏁伴噺',
+ `returned_quantity` decimal(18,4) DEFAULT 0 COMMENT '宸插綊杩樻暟閲�',
+ `borrower_id` bigint DEFAULT NULL COMMENT '棰嗙敤浜篒D',
+ `borrower_name` varchar(100) DEFAULT NULL COMMENT '棰嗙敤浜哄鍚�',
+ `borrow_time` datetime NOT NULL COMMENT '棰嗙敤鏃堕棿',
+ `expected_return_time` datetime DEFAULT NULL COMMENT '棰勮褰掕繕鏃堕棿',
+ `approval_status` tinyint DEFAULT 0 COMMENT '瀹℃壒鐘舵�侊紙0-寰呭鎵癸紝1-宸查�氳繃锛�2-宸查┏鍥烇級',
+ `status` tinyint DEFAULT 0 COMMENT '褰掕繕鐘舵�侊紙0-鏈綊杩橈紝1-閮ㄥ垎褰掕繕锛�2-宸插叏閮ㄥ綊杩橈級',
+ `remark` varchar(500) DEFAULT NULL COMMENT '澶囨敞',
+ `tenant_id` bigint DEFAULT NULL COMMENT '绉熸埛ID',
+ `dept_id` bigint DEFAULT NULL COMMENT '閮ㄩ棬ID',
+ `create_user` int DEFAULT NULL COMMENT '鍒涘缓浜�',
+ `create_time` datetime DEFAULT NULL COMMENT '鍒涘缓鏃堕棿',
+ `update_user` int DEFAULT NULL COMMENT '鏇存柊浜�',
+ `update_time` datetime DEFAULT NULL COMMENT '鏇存柊鏃堕棿',
+ PRIMARY KEY (`id`),
+ KEY `idx_borrow_no` (`borrow_no`),
+ KEY `idx_product_model_id` (`product_model_id`),
+ KEY `idx_borrower_id` (`borrower_id`),
+ KEY `idx_approval_status` (`approval_status`),
+ KEY `idx_status` (`status`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='浜у搧棰嗙敤琛�';
+
+-- 浜у搧褰掕繕璁板綍琛�
+CREATE TABLE IF NOT EXISTS `product_borrow_return` (
+ `id` bigint NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+ `borrow_id` bigint NOT NULL COMMENT '棰嗙敤璁板綍ID',
+ `product_model_id` bigint NOT NULL COMMENT '浜у搧瑙勬牸ID',
+ `batch_no` varchar(100) DEFAULT NULL COMMENT '鎵瑰彿',
+ `return_quantity` decimal(18,4) NOT NULL COMMENT '褰掕繕鏁伴噺',
+ `returner_id` bigint DEFAULT NULL COMMENT '褰掕繕浜篒D',
+ `returner_name` varchar(100) DEFAULT NULL COMMENT '褰掕繕浜哄鍚�',
+ `return_time` datetime NOT NULL COMMENT '褰掕繕鏃堕棿',
+ `remark` varchar(500) DEFAULT NULL COMMENT '澶囨敞',
+ `tenant_id` bigint DEFAULT NULL COMMENT '绉熸埛ID',
+ `dept_id` bigint DEFAULT NULL COMMENT '閮ㄩ棬ID',
+ `create_user` int DEFAULT NULL COMMENT '鍒涘缓浜�',
+ `create_time` datetime DEFAULT NULL COMMENT '鍒涘缓鏃堕棿',
+ PRIMARY KEY (`id`),
+ KEY `idx_borrow_id` (`borrow_id`),
+ KEY `idx_product_model_id` (`product_model_id`),
+ KEY `idx_returner_id` (`returner_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='浜у搧褰掕繕璁板綍琛�';
diff --git "a/doc/\344\272\247\345\223\201\346\250\241\345\235\227\344\270\216\345\272\223\345\255\230\346\250\241\345\235\227\345\210\206\346\236\220\346\226\207\346\241\243.md" "b/doc/\344\272\247\345\223\201\346\250\241\345\235\227\344\270\216\345\272\223\345\255\230\346\250\241\345\235\227\345\210\206\346\236\220\346\226\207\346\241\243.md"
new file mode 100644
index 0000000..49550cb
--- /dev/null
+++ "b/doc/\344\272\247\345\223\201\346\250\241\345\235\227\344\270\216\345\272\223\345\255\230\346\250\241\345\235\227\345\210\206\346\236\220\346\226\207\346\241\243.md"
@@ -0,0 +1,714 @@
+# 浜у搧妯″潡涓庡簱瀛樻ā鍧楄缁嗗垎鏋愭枃妗�
+
+## 涓�銆佷骇鍝佹ā鍧� (Product Module)
+
+### 1. 妯″潡姒傝堪
+
+浜у搧妯″潡璐熻矗绠$悊浜у搧鐨勫熀纭�淇℃伅锛岄噰鐢ㄦ爲褰㈢粨鏋勭粍缁囦骇鍝佸垎绫伙紝鏀寔浜у搧瑙勬牸鍨嬪彿鐨勭鐞嗐��
+
+### 2. 鏍稿績瀹炰綋
+
+#### 2.1 Product (浜у搧)
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| id | Long | 涓婚敭ID锛岃嚜澧� |
+| parentId | Long | 鐖朵骇鍝両D锛堟敮鎸佹爲褰㈢粨鏋勶級 |
+| productName | String | 浜у搧鍚嶇О |
+| tenantId | Long | 绉熸埛ID锛堣嚜鍔ㄥ~鍏咃級 |
+| createUser | Integer | 鍒涘缓鐢ㄦ埛锛堣嚜鍔ㄥ~鍏咃級 |
+| deptId | Long | 閮ㄩ棬ID锛堣嚜鍔ㄥ~鍏咃級 |
+| deptIds | Long[] | 閮ㄩ棬ID鏁扮粍锛堥潪鏁版嵁搴撳瓧娈碉級 |
+
+**鏁版嵁琛�**: `product`
+
+#### 2.2 ProductModel (浜у搧瑙勬牸鍨嬪彿)
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| id | Long | 涓婚敭ID锛岃嚜澧� |
+| productId | Long | 鍏宠仈浜у搧ID |
+| model | String | 瑙勬牸鍨嬪彿 |
+| productCode | String | 浜у搧缂栫爜 |
+| unit | String | 鍗曚綅 |
+| tenantId | Long | 绉熸埛ID |
+| createUser | Integer | 鍒涘缓鐢ㄦ埛 |
+| deptId | Long | 閮ㄩ棬ID |
+| inboundNum | BigDecimal | 鍏ュ簱鏁伴噺锛堥潪鏁版嵁搴撳瓧娈碉級 |
+| outboundNum | BigDecimal | 鍑哄簱鏁伴噺锛堥潪鏁版嵁搴撳瓧娈碉級 |
+| stockQuantity | BigDecimal | 鍓╀綑搴撳瓨锛堥潪鏁版嵁搴撳瓧娈碉級 |
+
+**鏁版嵁琛�**: `product_model`
+
+### 3. DTO 鏁版嵁浼犺緭瀵硅薄
+
+#### 3.1 ProductDto
+缁ф壙鑷� `Product`锛屾墿灞曞瓧娈碉細
+- `productModelList`: List<ProductModel> - 浜у搧瑙勬牸鍨嬪彿鍒楄〃
+
+#### 3.2 ProductModelDto
+缁ф壙鑷� `ProductModel`锛屾墿灞曞瓧娈碉細
+- `productStructureList`: List<ProductStructureDto> - 浜у搧缁撴瀯鍒楄〃锛堢敤浜嶣OM锛�
+
+#### 3.3 ProductTreeDto
+鐢ㄤ簬鏍戝舰缁撴瀯灞曠ず锛�
+- `id`, `parentId`, `productName`, `label`
+- `children`: List<ProductTreeDto> - 瀛愯妭鐐瑰垪琛�
+
+#### 3.4 ProductModelVo
+缁ф壙鑷� `ProductModel`锛屾墿灞曞瓧娈碉細
+- `batchNoList`: List<String> - 鎵瑰彿鍒楄〃
+
+### 4. Controller API 鎺ュ彛
+
+**ProductController** (`/basic/product`)
+
+| 鏂规硶 | HTTP | 璺緞 | 璇存槑 |
+|------|------|------|------|
+| selectProductList | GET | /list | 鏌ヨ浜у搧鍒楄〃锛堟爲褰㈢粨鏋勶級 |
+| selectModelList | GET | /modelList | 鏍规嵁浜у搧ID鏌ヨ瑙勬牸鍨嬪彿鍒楄〃 |
+| addOrEditProduct | POST | /addOrEditProduct | 鏂板/鏇存柊浜у搧 |
+| addOrEditProductModel | POST | /addOrEditProductModel | 鏂板/鏇存柊浜у搧瑙勬牸鍨嬪彿 |
+| remove | DELETE | /delProduct | 鍒犻櫎浜у搧锛堟壒閲忥級 |
+| delProductModel | DELETE | /delProductModel | 鍒犻櫎浜у搧瑙勬牸鍨嬪彿锛堟壒閲忥級 |
+| selectModelListPage | GET | /modelListPage | 浜у搧瑙勬牸鍒嗛〉鏌ヨ |
+| listPageProductModel | GET | /pageModel | 鍒嗛〉鏌ヨ鎵�鏈変骇鍝佸瀷鍙� |
+| importProductModel | POST | /import | 瀵煎叆浜у搧瑙勬牸鍨嬪彿 |
+| importProduct | GET | /export | 涓嬭浇浜у搧瀵煎叆妯℃澘 |
+
+### 5. Service 涓氬姟閫昏緫
+
+#### 5.1 IProductService
+
+```java
+// 鏂板鎴栫紪杈戜骇鍝�
+int addOrEditProduct(ProductDto productDto)
+ - 楠岃瘉鐖惰妭鐐规槸鍚﹀瓨鍦�
+ - 楠岃瘉浜у搧鍚嶇О鍦ㄥ悓涓�鐖惰妭鐐逛笅鍞竴
+ - 鏂板/鏇存柊浜у搧
+
+// 鍒犻櫎浜у搧锛堟壒閲忥級
+int delProductByIds(Long[] ids)
+ - 绾ц仈鍒犻櫎鍏宠仈鐨勪骇鍝佽鏍煎瀷鍙�
+ - 鍒犻櫎浜у搧鏈韩
+
+// 鏌ヨ浜у搧鍒楄〃锛堟爲褰級
+List<ProductTreeDto> selectProductList(ProductDto productDto)
+ - 鏌ヨ鏍硅妭鐐癸紙parentId涓簄ull锛�
+ - 閫掑綊鏋勫缓瀛愭爲
+
+// 鍒嗛〉鏌ヨ浜у搧鍨嬪彿
+IPage<ProductModelVo> listPageProductModel(Page<ProductModelVo> page, ProductModel productModel)
+```
+
+#### 5.2 IProductModelService
+
+```java
+// 鏂板鎴栫紪杈戜骇鍝佽鏍煎瀷鍙�
+int addOrEditProductModel(ProductModelDto productModelDto)
+ - 楠岃瘉瑙勬牸鍨嬪彿+浜у搧缂栫爜鍞竴鎬�
+ - 鏂板/鏇存柊瑙勬牸鍨嬪彿
+
+// 鍒犻櫎浜у搧瑙勬牸鍨嬪彿
+int delProductModel(Long[] ids)
+ - 妫�鏌ユ槸鍚﹀瓨鍦ㄩ攢鍞彴璐�/閲囪喘鍙拌处鍏宠仈
+ - 妫�鏌ユ槸鍚﹀瓨鍦˙OM鏁版嵁鍏宠仈
+ - 鎵ц鍒犻櫎
+
+// 鏌ヨ瑙勬牸鍨嬪彿鍒楄〃
+List<ProductModel> selectModelList(ProductDto productDto)
+
+// 鍒嗛〉鏌ヨ
+IPage<ProductModel> modelListPage(Page page, ProductDto productDto)
+
+// 瀵煎叆浜у搧瑙勬牸鍨嬪彿
+AjaxResult importProductModel(MultipartFile file, Integer productId)
+ - 楠岃瘉浜у搧鏄惁瀛樺湪
+ - 楠岃瘉瀵煎叆鏁版嵁锛堜骇鍝佺紪鐮併�佽鏍煎瀷鍙枫�佸崟浣嶅繀濉級
+ - 鍘婚噸澶勭悊锛堣烦杩囧凡瀛樺湪鐨勫瀷鍙凤級
+ - 鎵归噺淇濆瓨
+```
+
+### 6. 涓氬姟绾︽潫
+
+1. **浜у搧鍒犻櫎绾︽潫**:
+ - 瀛樺湪閿�鍞彴璐�/閲囪喘鍙拌处鍏宠仈鐨勪骇鍝佷笉鑳藉垹闄�
+
+2. **浜у搧瑙勬牸鍨嬪彿鍒犻櫎绾︽潫**:
+ - 瀛樺湪閿�鍞彴璐�/閲囪喘鍙拌处鍏宠仈涓嶈兘鍒犻櫎
+ - 瀛樺湪BOM鏁版嵁鍏宠仈涓嶈兘鍒犻櫎
+
+3. **鍞竴鎬х害鏉�**:
+ - 鍚屼竴鐖惰妭鐐逛笅浜у搧鍚嶇О鍞竴
+ - 瑙勬牸鍨嬪彿+浜у搧缂栫爜缁勫悎鍞竴
+
+### 7. 鏁版嵁娴佺▼鍥�
+
+```
+浜у搧绠$悊娴佺▼锛�
+鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+鈹� 浜у搧鍒嗙被 鈹傗攢鈹�鈹�鈹�>鈹� 浜у搧 鈹傗攢鈹�鈹�鈹�>鈹� 浜у搧瑙勬牸鍨嬪彿 鈹�
+鈹� (鐖惰妭鐐�) 鈹� 鈹� (瀛愯妭鐐�) 鈹� 鈹� (鍏蜂綋鍨嬪彿) 鈹�
+鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+ 鈹�
+ 鈻�
+ 鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+ 鈹� 閿�鍞彴璐� 鈹�
+ 鈹� 閲囪喘鍙拌处 鈹�
+ 鈹� BOM鏁版嵁 鈹�
+ 鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+```
+
+---
+
+## 浜屻�佸簱瀛樻ā鍧� (Stock Module)
+
+### 1. 妯″潡姒傝堪
+
+搴撳瓨妯″潡绠$悊浜у搧鐨勫簱瀛樹俊鎭紝鏀寔鍏ュ簱銆佸嚭搴撱�佸簱瀛樻煡璇€�佸喕缁�/瑙e喕绛夊姛鑳斤紝閲囩敤鎵瑰彿绠$悊搴撳瓨銆�
+
+### 2. 鏍稿績瀹炰綋
+
+#### 2.1 StockInventory (搴撳瓨琛�)
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| id | Long | 涓婚敭ID |
+| productModelId | Long | 浜у搧瑙勬牸ID锛堝繀濉級 |
+| batchNo | String | 鎵瑰彿 |
+| qualitity | BigDecimal | 搴撳瓨鏁伴噺 |
+| lockedQuantity | BigDecimal | 鍐荤粨/閿佸畾鏁伴噺 |
+| warnNum | BigDecimal | 棰勮鏁伴噺 |
+| version | Integer | 鐗堟湰鍙� |
+| remark | String | 澶囨敞 |
+| createTime | LocalDateTime | 鍒涘缓鏃堕棿 |
+| updateTime | LocalDateTime | 鏇存柊鏃堕棿 |
+| createUser | Integer | 鍒涘缓鐢ㄦ埛 |
+| deptId | Long | 閮ㄩ棬ID |
+
+**鏁版嵁琛�**: `stock_inventory`
+
+#### 2.2 StockInRecord (鍏ュ簱璁板綍)
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| id | Long | 涓婚敭ID |
+| inboundBatches | String | 鍏ュ簱鎵规 |
+| stockInNum | BigDecimal | 鍏ュ簱鏁伴噺 |
+| batchNo | String | 鎵瑰彿 |
+| recordType | String | 璁板綍绫诲瀷锛堟灇涓撅級 |
+| recordId | Long | 鍏宠仈璁板綍ID |
+| productModelId | Long | 浜у搧瑙勬牸ID |
+| type | String | 绫诲瀷锛�0鍚堟牸/1涓嶅悎鏍硷級 |
+| warnNum | BigDecimal | 棰勮鏁伴噺 |
+| approvalStatus | Integer | 瀹℃壒鐘舵�侊紙0寰呭鎵�/1閫氳繃/2椹冲洖锛� |
+| remark | String | 澶囨敞 |
+
+**鏁版嵁琛�**: `stock_in_record`
+
+#### 2.3 StockOutRecord (鍑哄簱璁板綍)
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| id | Long | 涓婚敭ID |
+| outboundBatches | String | 鍑哄簱鎵规 |
+| stockOutNum | BigDecimal | 鍑哄簱鏁伴噺 |
+| batchNo | String | 鎵瑰彿 |
+| recordType | String | 璁板綍绫诲瀷锛堟灇涓撅級 |
+| recordId | Long | 鍏宠仈璁板綍ID |
+| productModelId | Long | 浜у搧瑙勬牸ID |
+| type | String | 绫诲瀷锛�0鍚堟牸/1涓嶅悎鏍硷級 |
+| approvalStatus | Integer | 瀹℃壒鐘舵�侊紙0寰呭鎵�/1閫氳繃/2椹冲洖/3閿�鍞嚭搴撳緟纭锛� |
+| remark | String | 澶囨敞 |
+
+**鏁版嵁琛�**: `stock_out_record`
+
+### 3. DTO 鏁版嵁浼犺緭瀵硅薄
+
+#### 3.1 StockInventoryDto
+缁ф壙鑷� `StockInventory`锛屾墿灞曞瓧娈碉細
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| productName | String | 浜у搧鍚嶇О |
+| model | String | 瑙勬牸鍨嬪彿 |
+| unit | String | 鍗曚綅 |
+| recordType | String | 鍏ュ簱绫诲瀷 |
+| recordId | Long | 鍏ュ簱绫诲瀷瀵瑰簲ID |
+| reportDate | LocalDate | 鎶ヨ〃鏃ユ湡 |
+| startMonth | endMonth | 搴撳瓨鏈堟姤鏌ヨ瀛楁 |
+| totalStockIn | BigDecimal | 鎬诲叆搴撻噺 |
+| totalStockOut | BigDecimal | 鎬诲嚭搴撻噺 |
+| currentStock | BigDecimal | 褰撳墠搴撳瓨 |
+| stockType | String | 搴撳瓨绫诲瀷锛坬ualified/unqualified锛� |
+| qualifiedQuantity | BigDecimal | 鍚堟牸搴撳瓨鏁伴噺 |
+| unQualifiedQuantity | BigDecimal | 涓嶅悎鏍煎簱瀛樻暟閲� |
+| qualifiedLockedQuantity | BigDecimal | 鍚堟牸搴撳瓨鍐荤粨鏁伴噺 |
+| unQualifiedLockedQuantity | BigDecimal | 涓嶅悎鏍煎簱瀛樺喕缁撴暟閲� |
+| qualifiedPendingOutQuantity | BigDecimal | 鍚堟牸搴撳瓨寰呭鏍稿嚭搴撴暟閲� |
+| productId | Long | 浜у搧ID |
+| topParentProductId | Long | 椤堕儴鐖朵骇鍝両D |
+
+### 4. Controller API 鎺ュ彛
+
+**StockInventoryController** (`/stockInventory`)
+
+| 鏂规硶 | HTTP | 璺緞 | 璇存槑 |
+|------|------|------|------|
+| pagestockInventory | GET | /pagestockInventory | 鍒嗛〉鏌ヨ搴撳瓨 |
+| pageListCombinedStockInventory | GET | /pageListCombinedStockInventory | 鍒嗛〉鏌ヨ鑱斿悎搴撳瓨鍒楄〃 |
+| getBatchNoQty | GET | /getBatchNoQty | 鏌ヨ瀵瑰簲鎵瑰彿鍜屾暟閲� |
+| addstockInventory | POST | /addstockInventory | 鏂板搴撳瓨锛堝叆搴擄級 |
+| subtractStockInventory | POST | /subtractStockInventory | 鎵e噺搴撳瓨锛堝嚭搴擄級 |
+| addStockInRecordOnly | POST | /addStockInRecordOnly | 鏂板鍏ュ簱璁板綍锛堜笉璋冩暣搴撳瓨锛� |
+| addStockOutRecordOnly | POST | /addStockOutRecordOnly | 鏂板鍑哄簱璁板綍锛堜笉璋冩暣搴撳瓨锛� |
+| importStockInventory | POST | /importStockInventory | 瀵煎叆搴撳瓨 |
+| downloadStockInventory | POST | /downloadStockInventory | 涓嬭浇搴撳瓨瀵煎叆妯℃澘 |
+| exportStockInventory | POST | /exportStockInventory | 瀵煎嚭搴撳瓨 |
+| stockInventoryPage | GET | /stockInventoryPage | 搴撳瓨鎶ヨ〃鏌ヨ |
+| stockInAndOutRecord | GET | /stockInAndOutRecord | 缁熻鍏ュ簱鍑哄簱璁板綍 |
+| frozenStock | POST | /frozenStock | 鍐荤粨搴撳瓨 |
+| thawStock | POST | /thawStock | 瑙e喕搴撳瓨 |
+| getByModelId | GET | /getByModelId | 鏍规嵁浜у搧瑙勬牸ID鑾峰彇鍏ュ簱璁板綍 |
+
+**StockInRecordController** (`/stockInRecord`)
+
+| 鏂规硶 | HTTP | 璺緞 | 璇存槑 |
+|------|------|------|------|
+| listPage | GET | /listPage | 鍏ュ簱绠$悊鍒楄〃 |
+| delete | DELETE | / | 鍒犻櫎鍏ュ簱璁板綍 |
+| deletePending | DELETE | /pending | 鍒犻櫎寰呭鎵瑰叆搴撹褰� |
+| exportStockInRecord | POST | /exportStockInRecord | 瀵煎嚭鍏ュ簱璁板綍 |
+| approve | POST | /approve | 鎵归噺瀹℃壒鍏ュ簱璁板綍 |
+| reAudit | POST | /reAudit | 鎵归噺鍙嶅鍏ュ簱璁板綍 |
+
+**StockOutRecordController** (`/stockOutRecord`)
+
+| 鏂规硶 | HTTP | 璺緞 | 璇存槑 |
+|------|------|------|------|
+| listPage | GET | /listPage | 鍑哄簱绠$悊鍒楄〃 |
+| add | POST | / | 鏂板鍑哄簱璁板綍 |
+| update | PUT | /{id} | 鏇存柊鍑哄簱璁板綍 |
+| delete | DELETE | / | 鍒犻櫎鍑哄簱璁板綍 |
+| deletePending | DELETE | /pending | 鍒犻櫎寰呭鎵瑰嚭搴撹褰� |
+| exportStockOutRecord | POST | /exportStockOutRecord | 瀵煎嚭鍑哄簱璁板綍 |
+| approve | POST | /approve | 鎵归噺瀹℃壒鍑哄簱璁板綍 |
+
+### 5. Service 鏍稿績涓氬姟閫昏緫
+
+#### 5.1 StockInventoryService
+
+```java
+// 鍏ュ簱鎿嶄綔锛堣褰�+搴撳瓨璋冩暣锛�
+Boolean addstockInventory(StockInventoryDto dto)
+ 1. 鐢熸垚鎴栭獙璇佹壒鍙�
+ 2. 鍒涘缓鍏ュ簱璁板綍(StockInRecord)
+ 3. 妫�鏌ュ簱瀛樻槸鍚﹀瓨鍦�
+ - 涓嶅瓨鍦細鏂板搴撳瓨璁板綍
+ - 瀛樺湪锛氭洿鏂板簱瀛樻暟閲�
+
+// 鍑哄簱鎿嶄綔锛堣褰�+搴撳瓨璋冩暣锛�
+Boolean subtractStockInventory(StockInventoryDto dto)
+ 1. 鏌ヨ搴撳瓨璁板綍
+ 2. 楠岃瘉搴撳瓨鍏呰冻锛堟墸闄ゅ喕缁撴暟閲忥級
+ 3. 鍒涘缓鍑哄簱璁板綍(StockOutRecord)
+ 4. 鎵e噺搴撳瓨鏁伴噺
+
+// 浠呭垱寤哄叆搴撹褰曪紙涓嶈皟鏁村簱瀛橈級
+Boolean addStockInRecordOnly(StockInventoryDto dto)
+ - 鐢ㄤ簬鎵嬪伐褰曞叆鍘嗗彶鏁版嵁
+
+// 浠呭垱寤哄嚭搴撹褰曪紙涓嶈皟鏁村簱瀛橈級
+Boolean addStockOutRecordOnly(StockInventoryDto dto)
+ - 楠岃瘉鍙敤搴撳瓨锛堝簱瀛�-鍐荤粨-寰呭鏍稿嚭搴擄級
+ - 鍒涘缓鍑哄簱璁板綍
+
+// 鎵瑰彿鑷姩鐢熸垚瑙勫垯
+// 鏍煎紡: 鏃ユ湡-浜у搧缂栫爜-搴忓彿
+// 绀轰緥: 20260526-ABC001-001
+String generateAutoBatchNo(Long productModelId)
+
+// 鍐荤粨搴撳瓨
+Boolean frozenStock(StockInventoryDto dto)
+ - 楠岃瘉鍐荤粨鏁伴噺涓嶈秴杩囧簱瀛樻暟閲�
+ - 绱姞鍐荤粨鏁伴噺
+
+// 瑙e喕搴撳瓨
+Boolean thawStock(StockInventoryDto dto)
+ - 楠岃瘉瑙e喕鏁伴噺涓嶈秴杩囧喕缁撴暟閲�
+ - 鎵e噺鍐荤粨鏁伴噺
+```
+
+### 6. 鏋氫妇绫诲瀷
+
+#### 6.1 鍏ュ簱璁板綍绫诲瀷 (StockInQualifiedRecordTypeEnum)
+
+| 浠g爜 | 璇存槑 |
+|------|------|
+| CUSTOMIZATION_STOCK_IN | 鑷畾涔夊叆搴擄紙鎵嬪伐鍏ュ簱锛� |
+
+#### 6.2 鍑哄簱璁板綍绫诲瀷 (StockOutQualifiedRecordTypeEnum)
+
+| 浠g爜 | 璇存槑 |
+|------|------|
+| CUSTOMIZATION_STOCK_OUT | 鑷畾涔夊嚭搴擄紙鎵嬪伐鍑哄簱锛� |
+
+#### 6.3 瀹℃壒鐘舵�� (ReviewStatusEnum)
+
+| 鍊� | 璇存槑 |
+|------|------|
+| 0 | 寰呭鎵� |
+| 1 | 閫氳繃 |
+| 2 | 椹冲洖 |
+| 3 | 閿�鍞嚭搴撳緟纭 |
+
+### 7. 涓氬姟娴佺▼鍥�
+
+```
+鍏ュ簱娴佺▼锛�
+鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+鈹� 鍏ュ簱鐢宠 鈹傗攢鈹�鈹�鈹�>鈹� 鍒涘缓鍏ュ簱 鈹傗攢鈹�鈹�鈹�>鈹� 鏇存柊搴撳瓨 鈹�
+鈹� (鏉ユ簮) 鈹� 鈹� 璁板綍 鈹� 鈹� 鏁伴噺 鈹�
+鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+ 鈹�
+ 鈻�
+ 鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+ 鈹� 鐢熸垚鎵瑰彿 鈹�
+ 鈹� (鑷姩/鎵嬪伐)鈹�
+ 鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+
+鍑哄簱娴佺▼锛�
+鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+鈹� 鍑哄簱鐢宠 鈹傗攢鈹�鈹�鈹�>鈹� 楠岃瘉搴撳瓨 鈹傗攢鈹�鈹�鈹�>鈹� 鍒涘缓鍑哄簱 鈹�
+鈹� 鈹� 鈹� 鏄惁鍏呰冻 鈹� 鈹� 璁板綍 鈹�
+鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+ 鈹�
+ 鈻�
+ 鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+ 鈹� 鎵e噺搴撳瓨 鈹�
+ 鈹� 鏁伴噺 鈹�
+ 鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+
+搴撳瓨鍐荤粨/瑙e喕锛�
+鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+鈹� 鍐荤粨鐢宠 鈹傗攢鈹�鈹�鈹�>鈹� 绱姞鍐荤粨 鈹�
+鈹� 鈹� 鈹� 鏁伴噺 鈹�
+鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+
+鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+鈹� 瑙e喕鐢宠 鈹傗攢鈹�鈹�鈹�>鈹� 鎵e噺鍐荤粨 鈹�
+鈹� 鈹� 鈹� 鏁伴噺 鈹�
+鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+```
+
+### 8. 搴撳瓨璁$畻鍏紡
+
+```
+鍙敤搴撳瓨 = 搴撳瓨鏁伴噺 - 鍐荤粨鏁伴噺 - 寰呭鏍稿嚭搴撴暟閲�
+
+鍑哄簱楠岃瘉鏉′欢:
+鐢宠鍑哄簱鏁伴噺 <= 鍙敤搴撳瓨
+
+鍐荤粨楠岃瘉鏉′欢:
+鍐荤粨鏁伴噺 <= 搴撳瓨鏁伴噺
+
+瑙e喕楠岃瘉鏉′欢:
+瑙e喕鏁伴噺 <= 宸插喕缁撴暟閲�
+```
+
+---
+
+## 涓夈�佹ā鍧楀叧鑱斿叧绯�
+
+### 1. 鏁版嵁鍏宠仈
+
+```
+Product (浜у搧)
+ 鈹�
+ 鈹溾攢鈹� productId 鈹�鈹�> ProductModel (浜у搧瑙勬牸鍨嬪彿)
+ 鈹� 鈹�
+ 鈹� 鈹溾攢鈹� productModelId 鈹�鈹�> StockInventory (鍚堟牸搴撳瓨)
+ 鈹� 鈹�
+ 鈹� 鈹溾攢鈹� productModelId 鈹�鈹�> StockUninventory (涓嶅悎鏍煎簱瀛�)
+ 鈹� 鈹�
+ 鈹� 鈹溾攢鈹� productModelId 鈹�鈹�> StockInRecord (鍏ュ簱璁板綍)
+ 鈹� 鈹�
+ 鈹� 鈹斺攢鈹� productModelId 鈹�鈹�> StockOutRecord (鍑哄簱璁板綍)
+ 鈹�
+ 鈹斺攢鈹� 琚垹闄ゆ椂绾ц仈鍒犻櫎 ProductModel
+```
+
+### 2. 鍒犻櫎绾︽潫
+
+| 妯″潡 | 鍒犻櫎鍓嶆鏌� |
+|------|-----------|
+| Product | 妫�鏌ラ攢鍞彴璐﹀叧鑱� |
+| ProductModel | 妫�鏌ラ攢鍞彴璐︺�丅OM鏁版嵁鍏宠仈 |
+| StockInventory | 涓嶅厑璁哥洿鎺ュ垹闄わ紝闇�閫氳繃鍑哄簱鎵e噺 |
+
+### 3. 鍏抽敭涓氬姟鍦烘櫙
+
+#### 3.1 浜у搧鍏ュ簱鍦烘櫙
+1. 閲囪喘鍏ュ簱 鈫� 鐢熸垚鍏ュ簱璁板綍 鈫� 澧炲姞搴撳瓨
+2. 鐢熶骇鍏ュ簱 鈫� 鐢熸垚鍏ュ簱璁板綍 鈫� 澧炲姞搴撳瓨
+3. 鎵嬪伐鍏ュ簱 鈫� 鑷畾涔夊叆搴撶被鍨�
+
+#### 3.2 浜у搧鍑哄簱鍦烘櫙
+1. 閿�鍞嚭搴� 鈫� 妫�鏌ュ簱瀛� 鈫� 鍒涘缓鍑哄簱璁板綍 鈫� 鎵e噺搴撳瓨
+2. 鐢熶骇棰嗘枡 鈫� 妫�鏌ュ簱瀛� 鈫� 鍒涘缓鍑哄簱璁板綍 鈫� 鎵e噺搴撳瓨
+3. 鎵嬪伐鍑哄簱 鈫� 鑷畾涔夊嚭搴撶被鍨�
+
+#### 3.3 搴撳瓨鍐荤粨鍦烘櫙
+1. 璁㈠崟閿佸畾 鈫� 鍐荤粨搴撳瓨
+2. 璐ㄩ噺闂 鈫� 鍐荤粨搴撳瓨
+3. 闂瑙e喅 鈫� 瑙e喕搴撳瓨
+
+---
+
+## 鍥涖�佹枃浠剁粨鏋�
+
+### 浜у搧妯″潡鏂囦欢
+
+```
+com.ruoyi.basic/
+鈹溾攢鈹� controller/
+鈹� 鈹斺攢鈹� ProductController.java
+鈹溾攢鈹� service/
+鈹� 鈹溾攢鈹� IProductService.java
+鈹� 鈹溾攢鈹� IProductModelService.java
+鈹� 鈹斺攢鈹� impl/
+鈹� 鈹溾攢鈹� ProductServiceImpl.java
+鈹� 鈹斺攢鈹� ProductModelServiceImpl.java
+鈹溾攢鈹� mapper/
+鈹� 鈹溾攢鈹� ProductMapper.java
+鈹� 鈹斺攢鈹� ProductModelMapper.java
+鈹溾攢鈹� pojo/
+鈹� 鈹溾攢鈹� Product.java
+鈹� 鈹斺攢鈹� ProductModel.java
+鈹溾攢鈹� dto/
+鈹� 鈹溾攢鈹� ProductDto.java
+鈹� 鈹溾攢鈹� ProductModelDto.java
+鈹� 鈹斺攢鈹� ProductTreeDto.java
+鈹溾攢鈹� vo/
+鈹� 鈹斺攢鈹� ProductModelVo.java
+鈹斺攢鈹� excel/
+ 鈹斺攢鈹� (瀵煎叆瀵煎嚭鐩稿叧)
+```
+
+### 搴撳瓨妯″潡鏂囦欢
+
+```
+com.ruoyi.stock/
+鈹溾攢鈹� controller/
+鈹� 鈹溾攢鈹� StockInventoryController.java
+鈹� 鈹溾攢鈹� StockInRecordController.java
+鈹� 鈹溾攢鈹� StockOutRecordController.java
+鈹� 鈹斺攢鈹� StockUninventoryController.java
+鈹溾攢鈹� service/
+鈹� 鈹溾攢鈹� StockInventoryService.java
+鈹� 鈹溾攢鈹� StockInRecordService.java
+鈹� 鈹溾攢鈹� StockOutRecordService.java
+鈹� 鈹斺攢鈹� impl/
+鈹� 鈹溾攢鈹� StockInventoryServiceImpl.java
+鈹� 鈹溾攢鈹� StockInRecordServiceImpl.java
+鈹� 鈹溾攢鈹� StockOutRecordServiceImpl.java
+鈹� 鈹斺攢鈹� StockUninventoryServiceImpl.java
+鈹溾攢鈹� mapper/
+鈹� 鈹溾攢鈹� StockInventoryMapper.java
+鈹� 鈹溾攢鈹� StockInRecordMapper.java
+鈹� 鈹斺攢鈹� StockOutRecordMapper.java
+鈹溾攢鈹� pojo/
+鈹� 鈹溾攢鈹� StockInventory.java
+鈹� 鈹溾攢鈹� StockInRecord.java
+鈹� 鈹斺攢鈹� StockOutRecord.java
+鈹溾攢鈹� dto/
+鈹� 鈹溾攢鈹� StockInventoryDto.java
+鈹� 鈹溾攢鈹� StockInRecordDto.java
+鈹� 鈹斺攢鈹� StockOutRecordDto.java
+鈹斺攢鈹� excel/
+ 鈹溾攢鈹� StockInventoryExportData.java
+ 鈹溾攢鈹� StockInRecordExportData.java
+ 鈹斺攢鈹� StockOutRecordExportData.java
+```
+
+---
+
+## 浜斻�佹敞鎰忎簨椤�
+
+1. **浜嬪姟绠$悊**: 鍏ュ簱/鍑哄簱鎿嶄綔浣跨敤 `@Transactional(rollbackFor = Exception.class)` 淇濊瘉鏁版嵁涓�鑷存��
+
+2. **鎵瑰彿绠$悊**:
+ - 鑷姩鐢熸垚鏍煎紡: `YYYYMMDD-浜у搧缂栫爜-搴忓彿`
+ - 鏀寔鎵嬪伐鎸囧畾鎵瑰彿
+ - 鍚屼竴鎵瑰彿鍚屼竴浜у搧瑙勬牸鍚堝苟搴撳瓨
+
+3. **搴撳瓨瀹夊叏**:
+ - 鍑哄簱鍓嶆鏌ュ彲鐢ㄥ簱瀛�
+ - 鍐荤粨鏁伴噺涓嶅彲鐢ㄤ簬鍑哄簱
+ - 寰呭鏍稿嚭搴撴暟閲忚鍏ュ彲鐢ㄥ簱瀛樿绠�
+
+4. **澶氱鎴锋敮鎸�**: 閫氳繃 `tenantId` 瀛楁瀹炵幇鏁版嵁闅旂
+
+5. **閮ㄩ棬闅旂**: 閫氳繃 `deptId` 瀛楁瀹炵幇閮ㄩ棬鏁版嵁闅旂
+
+---
+
+## 鍏�佷骇鍝侀鐢ㄥ綊杩樻ā鍧� (Product Borrow Module)
+
+### 1. 妯″潡姒傝堪
+
+浜у搧棰嗙敤褰掕繕妯″潡绠$悊浜у搧鐨勯鐢ㄥ拰褰掕繕娴佺▼锛屾敮鎸佸鎵规祦绋嬪拰搴撳瓨鑱斿姩銆�
+
+### 2. 鏍稿績瀹炰綋
+
+#### 2.1 ProductBorrow (浜у搧棰嗙敤琛�)
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| id | Long | 涓婚敭ID |
+| borrowNo | String | 棰嗙敤鍗曞彿锛堣嚜鍔ㄧ敓鎴愶級 |
+| productModelId | Long | 浜у搧瑙勬牸ID |
+| batchNo | String | 鎵瑰彿 |
+| borrowQuantity | BigDecimal | 棰嗙敤鏁伴噺 |
+| returnedQuantity | BigDecimal | 宸插綊杩樻暟閲� |
+| borrowerId | Long | 棰嗙敤浜篒D锛堢郴缁熺敤鎴凤級 |
+| borrowerName | String | 棰嗙敤浜哄鍚� |
+| borrowTime | LocalDateTime | 棰嗙敤鏃堕棿 |
+| expectedReturnTime | LocalDateTime | 棰勮褰掕繕鏃堕棿 |
+| approvalStatus | Integer | 瀹℃壒鐘舵�侊紙0寰呭鎵�/1宸查�氳繃/2宸查┏鍥烇級 |
+| status | Integer | 褰掕繕鐘舵�侊紙0鏈綊杩�/1閮ㄥ垎褰掕繕/2宸插叏閮ㄥ綊杩橈級 |
+| remark | String | 澶囨敞 |
+
+**鏁版嵁琛�**: `product_borrow`
+
+#### 2.2 ProductBorrowReturn (浜у搧褰掕繕璁板綍琛�)
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| id | Long | 涓婚敭ID |
+| borrowId | Long | 棰嗙敤璁板綍ID |
+| productModelId | Long | 浜у搧瑙勬牸ID |
+| batchNo | String | 鎵瑰彿 |
+| returnQuantity | BigDecimal | 褰掕繕鏁伴噺 |
+| returnerId | Long | 褰掕繕浜篒D锛堢郴缁熺敤鎴凤級 |
+| returnerName | String | 褰掕繕浜哄鍚� |
+| returnTime | LocalDateTime | 褰掕繕鏃堕棿 |
+| remark | String | 澶囨敞 |
+
+**鏁版嵁琛�**: `product_borrow_return`
+
+### 3. Controller API 鎺ュ彛
+
+**ProductBorrowController** (`/productBorrow`)
+
+| 鏂规硶 | HTTP | 璺緞 | 璇存槑 |
+|------|------|------|------|
+| listPage | GET | /listPage | 鍒嗛〉鏌ヨ棰嗙敤璁板綍 |
+| getDetail | GET | /{id} | 鏌ヨ棰嗙敤璁板綍璇︽儏 |
+| add | POST | / | 鏂板棰嗙敤璁板綍锛堝緟瀹℃壒锛� |
+| update | PUT | /{id} | 淇敼棰嗙敤璁板綍 |
+| delete | DELETE | / | 鍒犻櫎棰嗙敤璁板綍 |
+| approve | POST | /approve | 鎵归噺瀹℃壒棰嗙敤璁板綍 |
+| reAudit | POST | /reAudit | 鎵归噺鍙嶅棰嗙敤璁板綍 |
+
+**ProductBorrowReturnController** (`/productBorrowReturn`)
+
+| 鏂规硶 | HTTP | 璺緞 | 璇存槑 |
+|------|------|------|------|
+| listPage | GET | /listPage | 鍒嗛〉鏌ヨ褰掕繕璁板綍 |
+| listByBorrowId | GET | /listByBorrowId/{borrowId} | 鏌ヨ鏌愰鐢ㄨ褰曠殑褰掕繕璁板綍 |
+| add | POST | / | 鏂板褰掕繕璁板綍锛堢洿鎺ュ鍔犲簱瀛橈級 |
+
+### 4. 涓氬姟娴佺▼
+
+```
+棰嗙敤娴佺▼锛�
+鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+鈹� 鏂板棰嗙敤 鈹傗攢鈹�鈹�鈹�>鈹� 寰呭鎵� 鈹傗攢鈹�鈹�鈹�>鈹� 瀹℃壒閫氳繃 鈹�
+鈹� 璁板綍 鈹� 鈹� 鐘舵�� 鈹� 鈹� 鈹�
+鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+ 鈹�
+ 鈻�
+ 鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+ 鈹� 鎵e噺搴撳瓨 鈹�
+ 鈹� (鍑哄簱璁板綍) 鈹�
+ 鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+
+褰掕繕娴佺▼锛�
+鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+鈹� 閫夋嫨棰嗙敤 鈹傗攢鈹�鈹�鈹�>鈹� 濉啓褰掕繕 鈹傗攢鈹�鈹�鈹�>鈹� 楠岃瘉褰掕繕 鈹�
+鈹� 璁板綍 鈹� 鈹� 淇℃伅 鈹� 鈹� 鏁伴噺 鈹�
+鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+ 鈹� 鈹�
+ 鈻� 鈻�
+ 鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+ 鈹� 澧炲姞搴撳瓨 鈹� 鈹� 鏇存柊棰嗙敤鐘舵�� 鈹�
+ 鈹� (鍏ュ簱) 鈹� 鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+ 鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+```
+
+### 5. 涓氬姟绾︽潫
+
+1. **棰嗙敤绾︽潫**:
+ - 棰嗙敤浜轰粠绯荤粺鐢ㄦ埛琛ㄩ�夋嫨
+ - 鏂板棰嗙敤璁板綍鐘舵�佷负"寰呭鎵�"
+ - 鍙湁寰呭鎵圭姸鎬佹墠鑳戒慨鏀�/鍒犻櫎
+
+2. **瀹℃壒绾︽潫**:
+ - 鍙湁寰呭鎵圭姸鎬佹墠鑳藉鎵�
+ - 瀹℃壒閫氳繃鍚庤嚜鍔ㄦ墸鍑忓簱瀛�
+ - 瀹℃壒椹冲洖涓嶆墸鍑忓簱瀛�
+
+3. **褰掕繕绾︽潫**:
+ - 褰掕繕鏁伴噺涓嶈兘瓒呰繃鍓╀綑鍙綊杩樻暟閲�
+ - 棰嗙敤璁板綍蹇呴』瀹℃壒閫氳繃鎵嶈兘褰掕繕
+ - 褰掕繕鐩存帴澧炲姞搴撳瓨锛屾棤闇�瀹℃壒
+ - 褰掕繕浜轰粠绯荤粺鐢ㄦ埛琛ㄩ�夋嫨
+
+### 6. 棰嗙敤鍗曞彿鐢熸垚瑙勫垯
+
+```
+鏍煎紡锛歀Y + 骞存湀鏃� + 4浣嶅簭鍙�
+绀轰緥锛歀Y202605260001
+```
+
+### 7. 鏋氫妇绫诲瀷鎵╁睍
+
+**鍏ュ簱绫诲瀷** (StockInQualifiedRecordTypeEnum):
+- `PRODUCT_BORROW_RETURN("23", "浜у搧褰掕繕鍏ュ簱")`
+
+**鍑哄簱绫诲瀷** (StockOutQualifiedRecordTypeEnum):
+- `PRODUCT_BORROW("16", "浜у搧棰嗙敤鍑哄簱")`
+- `PRODUCT_RETURN("17", "浜у搧褰掕繕鍏ュ簱")`
+
+### 8. 鏂囦欢缁撴瀯
+
+```
+com.ruoyi.stock/
+鈹溾攢鈹� controller/
+鈹� 鈹溾攢鈹� ProductBorrowController.java
+鈹� 鈹斺攢鈹� ProductBorrowReturnController.java
+鈹溾攢鈹� service/
+鈹� 鈹溾攢鈹� ProductBorrowService.java
+鈹� 鈹溾攢鈹� ProductBorrowReturnService.java
+鈹� 鈹斺攢鈹� impl/
+鈹� 鈹溾攢鈹� ProductBorrowServiceImpl.java
+鈹� 鈹斺攢鈹� ProductBorrowReturnServiceImpl.java
+鈹溾攢鈹� mapper/
+鈹� 鈹溾攢鈹� ProductBorrowMapper.java
+鈹� 鈹斺攢鈹� ProductBorrowReturnMapper.java
+鈹溾攢鈹� pojo/
+鈹� 鈹溾攢鈹� ProductBorrow.java
+鈹� 鈹斺攢鈹� ProductBorrowReturn.java
+鈹斺攢鈹� dto/
+ 鈹溾攢鈹� ProductBorrowDto.java
+ 鈹斺攢鈹� ProductBorrowReturnDto.java
+
+resources/mapper/stock/
+鈹溾攢鈹� ProductBorrowMapper.xml
+鈹斺攢鈹� ProductBorrowReturnMapper.xml
+```
+
+### 9. 鏁版嵁搴撹〃
+
+SQL鑴氭湰浣嶇疆: `doc/20260526_product_borrow_tables.sql`
diff --git "a/doc/\344\272\247\345\223\201\351\242\206\347\224\250\345\275\222\350\277\230\346\250\241\345\235\227-\345\211\215\347\253\257\345\257\271\346\216\245\346\226\207\346\241\243.md" "b/doc/\344\272\247\345\223\201\351\242\206\347\224\250\345\275\222\350\277\230\346\250\241\345\235\227-\345\211\215\347\253\257\345\257\271\346\216\245\346\226\207\346\241\243.md"
new file mode 100644
index 0000000..e22c8f2
--- /dev/null
+++ "b/doc/\344\272\247\345\223\201\351\242\206\347\224\250\345\275\222\350\277\230\346\250\241\345\235\227-\345\211\215\347\253\257\345\257\271\346\216\245\346\226\207\346\241\243.md"
@@ -0,0 +1,696 @@
+# 浜у搧棰嗙敤褰掕繕妯″潡 - 鍓嶇瀵规帴鏂囨。
+
+## 涓�銆佹ā鍧楁杩�
+
+浜у搧棰嗙敤褰掕繕妯″潡鐢ㄤ簬绠$悊浜у搧鐨勯鐢ㄥ拰褰掕繕娴佺▼锛�
+- **棰嗙敤**锛氭柊澧為鐢ㄦ椂鑷姩瀹屾垚鍑哄簱鎿嶄綔锛岃嚜鍔ㄥ鎵归�氳繃锛屽彇娑堝鎵规祦绋�
+- **褰掕繕**锛氭柊澧炲綊杩樻椂鑷姩瀹屾垚鍏ュ簱鎿嶄綔锛岃嚜鍔ㄥ鎵归�氳繃
+
+**娉ㄦ剰**锛氭墍鏈夋帴鍙g粺涓�浣跨敤 POST 鏂瑰紡
+
+---
+
+## 浜屻�丄PI 鎺ュ彛璇存槑
+
+### 1. 浜у搧棰嗙敤鎺ュ彛
+
+#### 1.1 鍒嗛〉鏌ヨ棰嗙敤璁板綍
+
+**璇锋眰**
+```
+POST /productBorrow/listPage
+Content-Type: application/json
+```
+
+**璇锋眰浣�**
+```json
+{
+ "current": 1, // 褰撳墠椤电爜锛岄粯璁�1
+ "size": 10, // 姣忛〉鏉℃暟锛岄粯璁�10
+ "borrowNo": "LY202605260001", // 閫夊~锛岄鐢ㄥ崟鍙凤紝妯$硦鏌ヨ
+ "productModelId": 100, // 閫夊~锛屼骇鍝佽鏍糏D
+ "topParentProductId": 277, // 閫夊~锛岄《閮ㄧ埗浜у搧ID锛堜骇鍝佸垎绫籌D锛夛紝鐢ㄤ簬绛涢�変骇鍝佸垎绫绘爲
+ "model": "M6", // 閫夊~锛岃鏍煎瀷鍙凤紝妯$硦鏌ヨ
+ "borrowerId": 1, // 閫夊~锛岄鐢ㄤ汉ID
+ "borrowerName": "寮犱笁", // 閫夊~锛岄鐢ㄤ汉濮撳悕锛屾ā绯婃煡璇�
+ "approvalStatus": 0, // 閫夊~锛屽鎵圭姸鎬侊細0寰呭鎵�/1宸查�氳繃/2宸查┏鍥�
+ "status": 0 // 閫夊~锛屽綊杩樼姸鎬侊細0鏈綊杩�/1閮ㄥ垎褰掕繕/2宸插叏閮ㄥ綊杩�
+}
+```
+
+**鍝嶅簲绀轰緥**
+```json
+{
+ "code": 200,
+ "msg": "鎿嶄綔鎴愬姛",
+ "data": {
+ "records": [
+ {
+ "id": 1,
+ "borrowNo": "LY202605260001",
+ "productModelId": 100,
+ "batchNo": "20260526-ABC001-001",
+ "borrowQuantity": 10.0000,
+ "returnedQuantity": 5.0000,
+ "borrowerId": 1,
+ "borrowerName": "寮犱笁",
+ "borrowTime": "2026-05-26 10:00:00",
+ "expectedReturnTime": "2026-06-26 10:00:00",
+ "approvalStatus": 1,
+ "approvalStatusName": "宸查�氳繃",
+ "status": 1,
+ "statusName": "閮ㄥ垎褰掕繕",
+ "remark": "椤圭洰浣跨敤",
+ "productName": "铻轰笣鍒�",
+ "model": "M6",
+ "productCode": "LSD-M6",
+ "unit": "鎶�",
+ "remainingQuantity": 5.0000,
+ "createTime": "2026-05-26 09:00:00"
+ }
+ ],
+ "total": 100,
+ "size": 10,
+ "current": 1,
+ "pages": 10
+ }
+}
+```
+
+#### 1.2 鏌ヨ棰嗙敤璁板綍璇︽儏
+
+**璇锋眰**
+```
+POST /productBorrow/getDetail
+Content-Type: application/json
+```
+
+**璇锋眰浣�**
+```json
+{
+ "id": 1 // 蹇呭~锛岄鐢ㄨ褰旾D
+}
+```
+
+**鍝嶅簲绀轰緥**
+```json
+{
+ "code": 200,
+ "msg": "鎿嶄綔鎴愬姛",
+ "data": {
+ "id": 1,
+ "borrowNo": "LY202605260001",
+ "productModelId": 100,
+ "batchNo": "20260526-ABC001-001",
+ "borrowQuantity": 10.0000,
+ "returnedQuantity": 5.0000,
+ "remainingQuantity": 5.0000,
+ "borrowerId": 1,
+ "borrowerName": "寮犱笁",
+ "borrowTime": "2026-05-26 10:00:00",
+ "expectedReturnTime": "2026-06-26 10:00:00",
+ "approvalStatus": 1,
+ "approvalStatusName": "宸查�氳繃",
+ "status": 1,
+ "statusName": "閮ㄥ垎褰掕繕",
+ "remark": "椤圭洰浣跨敤",
+ "productName": "铻轰笣鍒�",
+ "model": "M6",
+ "productCode": "LSD-M6",
+ "unit": "鎶�"
+ }
+}
+```
+
+#### 1.3 鏂板棰嗙敤璁板綍
+
+**璇锋眰**
+```
+POST /productBorrow/add
+Content-Type: application/json
+```
+
+**璇锋眰浣�**
+```json
+{
+ "productModelId": 100, // 蹇呭~锛屼骇鍝佽鏍糏D
+ "batchNo": "20260526-ABC001-001", // 閫夊~锛屾壒鍙凤紝涓嶅~鍒欎娇鐢ㄩ粯璁ゆ壒鍙�
+ "borrowQuantity": 10.0000, // 蹇呭~锛岄鐢ㄦ暟閲�
+ "borrowerId": 1, // 蹇呭~锛岄鐢ㄤ汉ID锛堢郴缁熺敤鎴稩D锛�
+ "borrowerName": "寮犱笁", // 蹇呭~锛岄鐢ㄤ汉濮撳悕
+ "expectedReturnTime": "2026-06-26 10:00:00", // 閫夊~锛岄璁″綊杩樻椂闂�
+ "remark": "椤圭洰浣跨敤" // 閫夊~锛屽娉�
+}
+```
+
+**鍝嶅簲绀轰緥**
+```json
+{
+ "code": 200,
+ "msg": "鎿嶄綔鎴愬姛"
+}
+```
+
+**鑷姩鎵ц鐨勬搷浣�**锛�
+- 鑷姩鐢熸垚棰嗙敤鍗曞彿锛堟牸寮忥細LY + 骞存湀鏃� + 4浣嶅簭鍙凤級
+- 鑷姩鍒涘缓鍑哄簱璁板綍
+- 鑷姩瀹℃牳閫氳繃
+- 鑷姩鎵e噺搴撳瓨
+- 鐘舵�佺洿鎺ヨ涓�"宸查�氳繃"
+
+#### 1.4 淇敼棰嗙敤璁板綍
+
+**璇锋眰**
+```
+POST /productBorrow/update
+Content-Type: application/json
+```
+
+**璇锋眰浣�**
+```json
+{
+ "id": 1, // 蹇呭~锛岄鐢ㄨ褰旾D
+ "productModelId": 100,
+ "batchNo": "20260526-ABC001-001",
+ "borrowQuantity": 15.0000,
+ "borrowerId": 1,
+ "borrowerName": "寮犱笁",
+ "expectedReturnTime": "2026-06-26 10:00:00",
+ "remark": "椤圭洰浣跨敤"
+}
+```
+
+**娉ㄦ剰**锛氬彧鏈夊緟瀹℃壒鐘舵�侊紙approvalStatus=0锛夌殑璁板綍鎵嶈兘淇敼
+
+#### 1.5 鍒犻櫎棰嗙敤璁板綍
+
+**璇锋眰**
+```
+POST /productBorrow/delete
+Content-Type: application/json
+```
+
+**璇锋眰浣�**
+```json
+[1, 2, 3] // 瑕佸垹闄ょ殑璁板綍ID鏁扮粍
+```
+
+**娉ㄦ剰**锛氬彧鏈夊緟瀹℃壒鐘舵�佺殑璁板綍鎵嶈兘鍒犻櫎
+
+#### 1.6 鎵归噺瀹℃壒棰嗙敤璁板綍
+
+**璇锋眰**
+```
+POST /productBorrow/approve
+Content-Type: application/json
+```
+
+**璇锋眰浣�**
+```json
+{
+ "ids": [1, 2, 3], // 蹇呭~锛岃瀹℃壒鐨勮褰旾D鏁扮粍
+ "approvalStatusParam": 1 // 蹇呭~锛屽鎵圭姸鎬侊細1閫氳繃/2椹冲洖
+}
+```
+
+**鍝嶅簲绀轰緥**
+```json
+{
+ "code": 200,
+ "msg": "鎿嶄綔鎴愬姛"
+}
+```
+
+---
+
+### 2. 浜у搧褰掕繕鎺ュ彛
+
+#### 2.1 鍒嗛〉鏌ヨ褰掕繕璁板綍
+
+**璇锋眰**
+```
+POST /productBorrowReturn/listPage
+Content-Type: application/json
+```
+
+**璇锋眰浣�**
+```json
+{
+ "current": 1, // 褰撳墠椤电爜
+ "size": 10, // 姣忛〉鏉℃暟
+ "borrowId": 1, // 閫夊~锛岄鐢ㄨ褰旾D
+ "productModelId": 100, // 閫夊~锛屼骇鍝佽鏍糏D
+ "returnerId": 2, // 閫夊~锛屽綊杩樹汉ID
+ "returnerName": "鏉庡洓" // 閫夊~锛屽綊杩樹汉濮撳悕
+}
+```
+
+**鍝嶅簲绀轰緥**
+```json
+{
+ "code": 200,
+ "msg": "鎿嶄綔鎴愬姛",
+ "data": {
+ "records": [
+ {
+ "id": 1,
+ "borrowId": 1,
+ "borrowNo": "LY202605260001",
+ "productModelId": 100,
+ "batchNo": "20260526-ABC001-001",
+ "returnQuantity": 5.0000,
+ "returnerId": 2,
+ "returnerName": "鏉庡洓",
+ "returnTime": "2026-05-28 14:00:00",
+ "remark": "椤圭洰缁撴潫褰掕繕",
+ "productName": "铻轰笣鍒�",
+ "model": "M6",
+ "productCode": "LSD-M6",
+ "unit": "鎶�",
+ "borrowQuantity": 10.0000,
+ "borrowerName": "寮犱笁"
+ }
+ ],
+ "total": 50,
+ "size": 10,
+ "current": 1,
+ "pages": 5
+ }
+}
+```
+
+#### 2.2 鏌ヨ鏌愪釜棰嗙敤璁板綍鐨勫綊杩樿褰�
+
+**璇锋眰**
+```
+POST /productBorrowReturn/listByBorrowId
+Content-Type: application/json
+```
+
+**璇锋眰浣�**
+```json
+{
+ "current": 1, // 褰撳墠椤电爜
+ "size": 10, // 姣忛〉鏉℃暟
+ "borrowId": 1 // 蹇呭~锛岄鐢ㄨ褰旾D
+}
+```
+
+**鍝嶅簲绀轰緥**
+```json
+{
+ "code": 200,
+ "msg": "鎿嶄綔鎴愬姛",
+ "data": {
+ "records": [...],
+ "total": 5,
+ "size": 10,
+ "current": 1,
+ "pages": 1
+ }
+}
+```
+
+#### 2.3 鏂板褰掕繕璁板綍
+
+**璇锋眰**
+```
+POST /productBorrowReturn/add
+Content-Type: application/json
+```
+
+**璇锋眰浣�**
+```json
+{
+ "borrowId": 1, // 蹇呭~锛岄鐢ㄨ褰旾D
+ "returnQuantity": 5.0000, // 蹇呭~锛屽綊杩樻暟閲�
+ "returnerId": 2, // 蹇呭~锛屽綊杩樹汉ID锛堢郴缁熺敤鎴稩D锛�
+ "returnerName": "鏉庡洓", // 蹇呭~锛屽綊杩樹汉濮撳悕
+ "remark": "椤圭洰缁撴潫褰掕繕" // 閫夊~锛屽娉�
+}
+```
+
+**鍝嶅簲绀轰緥**
+```json
+{
+ "code": 200,
+ "msg": "鎿嶄綔鎴愬姛"
+}
+```
+
+**鑷姩鎵ц鐨勬搷浣�**锛�
+- 鑷姩鍒涘缓鍏ュ簱璁板綍
+- 鑷姩瀹℃牳閫氳繃
+- 鑷姩澧炲姞搴撳瓨
+- 鑷姩鏇存柊棰嗙敤璁板綍鐨勫凡褰掕繕鏁伴噺鍜岀姸鎬�
+
+**閿欒鎯呭喌**锛�
+```json
+{
+ "code": 500,
+ "msg": "褰掕繕鏁伴噺涓嶈兘澶т簬鍓╀綑鍙綊杩樻暟閲忥細5.0000"
+}
+```
+
+---
+
+### 3. 搴撳瓨鍜岄鐢ㄩ噺鏌ヨ鎺ュ彛
+
+#### 3.1 鍒嗛〉鏌ヨ浜у搧搴撳瓨鍜岄鐢ㄩ噺
+
+**鎺ュ彛璇存槑**锛�
+- 浠�"浜у搧瑙勬牸 + 鎵瑰彿"涓轰竴鏉¤褰�
+- 鏄剧ず姣忎釜鎵瑰彿鐨勫簱瀛樻暟閲忓拰琚鐢ㄩ噺
+- 鐢ㄤ簬棰嗙敤鏃堕�夋嫨浜у搧鍜屾壒鍙凤紝鏌ョ湅鍙鐢ㄦ暟閲�
+
+**璇锋眰**
+```
+POST /stockInventory/pageStockAndBorrow
+Content-Type: application/json
+```
+
+**璇锋眰浣�**
+```json
+{
+ "current": 1, // 褰撳墠椤电爜
+ "size": 10, // 姣忛〉鏉℃暟
+ "topParentProductId": 277, // 蹇呭~锛岄《閮ㄧ埗浜у搧ID锛堜骇鍝佸垎绫籌D锛�
+ "productName": "铻轰笣鍒�", // 閫夊~锛屼骇鍝佸悕绉帮紝妯$硦鏌ヨ
+ "model": "M6", // 閫夊~锛岃鏍煎瀷鍙凤紝妯$硦鏌ヨ
+ "batchNo": "20260526" // 閫夊~锛屾壒鍙凤紝妯$硦鏌ヨ
+}
+```
+
+**鍙傛暟璇存槑**锛�
+
+| 鍙傛暟鍚� | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|--------|------|------|------|
+| current | Integer | 鍚� | 褰撳墠椤电爜锛岄粯璁�1 |
+| size | Integer | 鍚� | 姣忛〉鏉℃暟锛岄粯璁�10 |
+| topParentProductId | Long | 鏄� | 椤堕儴鐖朵骇鍝両D锛堜骇鍝佸垎绫绘爲鏍硅妭鐐笽D锛夛紝鐢ㄤ簬绛涢�変骇鍝佸垎绫� |
+| productName | String | 鍚� | 浜у搧鍚嶇О锛屾ā绯婃煡璇� |
+| model | String | 鍚� | 瑙勬牸鍨嬪彿锛屾ā绯婃煡璇� |
+| batchNo | String | 鍚� | 鎵瑰彿锛屾ā绯婃煡璇� |
+
+**鍝嶅簲绀轰緥**
+```json
+{
+ "code": 200,
+ "msg": "鎿嶄綔鎴愬姛",
+ "data": {
+ "records": [
+ {
+ "productModelId": 100,
+ "model": "M6",
+ "productCode": "LSD-M6",
+ "unit": "鎶�",
+ "productName": "铻轰笣鍒�",
+ "productId": 50,
+ "batchNo": "20260526-ABC001-001",
+ "qualitity": 100.0000,
+ "lockedQuantity": 5.0000,
+ "borrowedQuantity": 10.0000,
+ "availableQuantity": 90.0000
+ },
+ {
+ "productModelId": 100,
+ "model": "M6",
+ "productCode": "LSD-M6",
+ "unit": "鎶�",
+ "productName": "铻轰笣鍒�",
+ "productId": 50,
+ "batchNo": "20260527-ABC001-001",
+ "qualitity": 50.0000,
+ "lockedQuantity": 0,
+ "borrowedQuantity": 0,
+ "availableQuantity": 50.0000
+ }
+ ],
+ "total": 20,
+ "size": 10,
+ "current": 1,
+ "pages": 2
+ }
+}
+```
+
+**鍝嶅簲瀛楁璇存槑**锛�
+
+| 瀛楁鍚� | 绫诲瀷 | 璇存槑 |
+|--------|------|------|
+| productModelId | Long | 浜у搧瑙勬牸ID锛堢敤浜庨鐢ㄦ椂浼犻�掞級 |
+| model | String | 瑙勬牸鍨嬪彿 |
+| productCode | String | 浜у搧缂栫爜 |
+| unit | String | 鍗曚綅 |
+| productName | String | 浜у搧鍚嶇О |
+| productId | Long | 浜у搧ID |
+| batchNo | String | 鎵瑰彿锛堢敤浜庨鐢ㄦ椂浼犻�掞紝鍙负null锛� |
+| qualitity | BigDecimal | 褰撳墠搴撳瓨鏁伴噺 |
+| lockedQuantity | BigDecimal | 鍐荤粨/閿佸畾鏁伴噺 |
+| borrowedQuantity | BigDecimal | 琚鐢ㄤ笖鏈綊杩樼殑鏁伴噺 |
+| availableQuantity | BigDecimal | 鍙鐢ㄦ暟閲� = 搴撳瓨鏁伴噺 - 琚鐢ㄦ暟閲� |
+
+**璁$畻鍏紡**锛�
+```
+鍙鐢ㄦ暟閲�(availableQuantity) = 搴撳瓨鏁伴噺(qualitity) - 琚鐢ㄩ噺(borrowedQuantity)
+```
+
+**琚鐢ㄩ噺璁$畻閫昏緫**锛�
+- 鍙粺璁″鎵归�氳繃鐨勯鐢ㄨ褰曪紙approval_status = 1锛�
+- 鍙粺璁℃湭鍏ㄩ儴褰掕繕鐨勮褰曪紙status != 2锛�
+- 璁$畻鍏紡锛氶鐢ㄦ暟閲� - 宸插綊杩樻暟閲�
+- 鎸変骇鍝佽鏍糏D + 鎵瑰彿鍒嗙粍缁熻
+
+**浣跨敤鍦烘櫙**锛�
+
+1. **棰嗙敤閫夋嫨浜у搧**锛�
+ - 璋冪敤姝ゆ帴鍙f煡璇㈠簱瀛�
+ - 鐢ㄦ埛閫夋嫨浜у搧鍜屾壒鍙�
+ - 鏄剧ず鍙鐢ㄦ暟閲忎緵鍙傝��
+ - 棰嗙敤鏃朵紶閫� `productModelId` 鍜� `batchNo`
+
+2. **鍓嶇鏄剧ず寤鸿**锛�
+ - 鍒楄〃鏄剧ず锛氫骇鍝佸悕绉般�佽鏍煎瀷鍙枫�佹壒鍙枫�佸簱瀛樻暟閲忋�佽棰嗙敤閲忋�佸彲棰嗙敤閲�
+ - 鍙鐢ㄦ暟閲忎负0鎴栬礋鏁扮殑璁板綍鍙互鏍囩孩鎴栫鐢ㄩ�夋嫨
+ - 鏀寔鎸変骇鍝佸悕绉般�佽鏍煎瀷鍙枫�佹壒鍙锋悳绱�
+
+**鍓嶇椤甸潰绀轰緥**锛�
+
+```
+鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+鈹� 浜у搧搴撳瓨鍜岄鐢ㄩ噺鏌ヨ 鈹�
+鈹溾攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+鈹� 浜у搧鍒嗙被: [閫夋嫨鍒嗙被 鈻糫 浜у搧鍚嶇О: [____] 瑙勬牸鍨嬪彿: [____] [鏌ヨ] 鈹�
+鈹溾攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+鈹� 鈹�
+鈹� 鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹�
+鈹� 鈹備骇鍝佸悕绉扳攤瑙勬牸鍨嬪彿鈹� 鎵瑰彿 鈹傚簱瀛樻暟閲忊攤琚鐢ㄩ噺鈹傚彲棰嗙敤閲忊攤 鎿嶄綔 鈹� 鈹�
+鈹� 鈹溾攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹尖攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹尖攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹尖攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹尖攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹尖攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹尖攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹�
+鈹� 鈹傝灪涓濆垁 鈹� M6 鈹�20260526- 鈹� 100 鈹� 10 鈹� 90 鈹俒棰嗙敤] 鈹� 鈹�
+鈹� 鈹� 鈹� 鈹侫BC001-001鈹� 鈹� 鈹� 鈹� 鈹� 鈹�
+鈹� 鈹溾攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹尖攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹尖攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹尖攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹尖攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹尖攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹尖攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹�
+鈹� 鈹傝灪涓濆垁 鈹� M6 鈹�20260527- 鈹� 50 鈹� 0 鈹� 50 鈹俒棰嗙敤] 鈹� 鈹�
+鈹� 鈹� 鈹� 鈹侫BC001-001鈹� 鈹� 鈹� 鈹� 鈹� 鈹�
+鈹� 鈹溾攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹尖攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹尖攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹尖攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹尖攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹尖攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹尖攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹�
+鈹� 鈹傛壋鎵� 鈹� 10瀵� 鈹� NULL 鈹� 30 鈹� 5 鈹� 25 鈹俒棰嗙敤] 鈹� 鈹�
+鈹� 鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹粹攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹粹攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹粹攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹粹攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹粹攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹粹攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹�
+鈹� 鈹�
+鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+```
+
+---
+
+## 涓夈�佹帴鍙f眹鎬昏〃
+
+### 浜у搧棰嗙敤鎺ュ彛
+
+| 鎺ュ彛鍚嶇О | 璇锋眰璺緞 | 璇锋眰鏂瑰紡 | 璇存槑 |
+|----------|----------|----------|------|
+| 鍒嗛〉鏌ヨ | /productBorrow/listPage | POST | 鏌ヨ棰嗙敤璁板綍鍒楄〃 |
+| 鏌ヨ璇︽儏 | /productBorrow/getDetail | POST | 鏍规嵁ID鏌ヨ璇︽儏 |
+| 鏂板棰嗙敤 | /productBorrow/add | POST | 鏂板棰嗙敤锛堣嚜鍔ㄥ嚭搴擄級 |
+| 淇敼棰嗙敤 | /productBorrow/update | POST | 淇敼棰嗙敤璁板綍 |
+| 鍒犻櫎棰嗙敤 | /productBorrow/delete | POST | 鎵归噺鍒犻櫎棰嗙敤璁板綍 |
+| 鎵归噺瀹℃壒 | /productBorrow/approve | POST | 鎵归噺瀹℃壒锛堥�氳繃/椹冲洖锛� |
+
+### 浜у搧褰掕繕鎺ュ彛
+
+| 鎺ュ彛鍚嶇О | 璇锋眰璺緞 | 璇锋眰鏂瑰紡 | 璇存槑 |
+|----------|----------|----------|------|
+| 鍒嗛〉鏌ヨ | /productBorrowReturn/listPage | POST | 鏌ヨ褰掕繕璁板綍鍒楄〃 |
+| 鎸夐鐢ㄦ煡璇� | /productBorrowReturn/listByBorrowId | POST | 鏌ヨ鏌愰鐢ㄧ殑褰掕繕璁板綍 |
+| 鏂板褰掕繕 | /productBorrowReturn/add | POST | 鏂板褰掕繕锛堣嚜鍔ㄥ叆搴擄級 |
+
+### 搴撳瓨鏌ヨ鎺ュ彛
+
+| 鎺ュ彛鍚嶇О | 璇锋眰璺緞 | 璇锋眰鏂瑰紡 | 璇存槑 |
+|----------|----------|----------|------|
+| 搴撳瓨鍜岄鐢ㄩ噺 | /stockInventory/pageStockAndBorrow | POST | 鏌ヨ搴撳瓨鍜岃棰嗙敤閲� |
+
+---
+
+## 鍥涖�佺姸鎬佽鏄�
+
+### 1. 瀹℃壒鐘舵�� (approvalStatus)
+
+| 鍊� | 鍚嶇О | 璇存槑 |
+|----|------|------|
+| 0 | 寰呭鎵� | 鏂板鍚庣殑榛樿鐘舵�侊紙浠呮壒閲忓鎵瑰満鏅娇鐢級 |
+| 1 | 宸查�氳繃 | 瀹℃壒閫氳繃锛屽凡鎵e噺搴撳瓨锛屽彲杩涜褰掕繕鎿嶄綔 |
+| 2 | 宸查┏鍥� | 瀹℃壒鏈�氳繃锛屼笉鎵e噺搴撳瓨 |
+
+**娉ㄦ剰**锛氭柊澧為鐢ㄦ椂鐩存帴鑷姩瀹℃壒閫氳繃锛岀姸鎬佷负1
+
+### 2. 褰掕繕鐘舵�� (status)
+
+| 鍊� | 鍚嶇О | 璇存槑 |
+|----|------|------|
+| 0 | 鏈綊杩� | 灏氭湭褰掕繕浠讳綍鏁伴噺 |
+| 1 | 閮ㄥ垎褰掕繕 | 宸插綊杩橀儴鍒嗘暟閲� |
+| 2 | 宸插叏閮ㄥ綊杩� | 宸插綊杩樺叏閮ㄦ暟閲忥紝涓嶈兘鍐嶅綊杩� |
+
+---
+
+## 浜斻�佷笟鍔℃祦绋嬪浘
+
+```
+鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+鈹� 棰嗙敤娴佺▼锛堣嚜鍔ㄥ嚭搴擄級 鈹�
+鈹溾攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+鈹� 鈹�
+鈹� 鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹�
+鈹� 鈹� 鏂板棰嗙敤 鈹傗攢鈹�鈹�>鈹� 鑷姩鍑哄簱 鈹傗攢鈹�鈹�>鈹� 鐘舵�佽涓� 鈹� 鈹�
+鈹� 鈹� 璁板綍 鈹� 鈹� 鎵e噺搴撳瓨 鈹� 鈹� 宸查�氳繃 鈹� 鈹�
+鈹� 鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹�
+鈹� 鈹� 鈹�
+鈹� 鈻� 鈹�
+鈹� 鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹�
+鈹� 鈹� 鍒涘缓鍑哄簱 鈹� 鈹�
+鈹� 鈹� 璁板綍 鈹� 鈹�
+鈹� 鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹�
+鈹� 鈹�
+鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+
+鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+鈹� 褰掕繕娴佺▼锛堣嚜鍔ㄥ叆搴擄級 鈹�
+鈹溾攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+鈹� 鈹�
+鈹� 鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹�
+鈹� 鈹� 閫夋嫨棰嗙敤 鈹傗攢鈹�鈹�>鈹� 濉啓褰掕繕 鈹傗攢鈹�鈹�>鈹� 鑷姩鍏ュ簱 鈹� 鈹�
+鈹� 鈹� 璁板綍 鈹� 鈹� 淇℃伅 鈹� 鈹� 澧炲姞搴撳瓨 鈹� 鈹�
+鈹� 鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹�
+鈹� 鈹� 鈹�
+鈹� 鈻� 鈹�
+鈹� 鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹�
+鈹� 鈹� 鏇存柊棰嗙敤 鈹� 鈹�
+鈹� 鈹� 鐘舵�� 鈹� 鈹�
+鈹� 鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹�
+鈹� 鈹�
+鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+```
+
+---
+
+## 鍏�佹敞鎰忎簨椤�
+
+### 1. 鏁版嵁鏍¢獙
+
+**鍓嶇鏍¢獙**锛�
+- 棰嗙敤鏁伴噺蹇呴』澶т簬0
+- 褰掕繕鏁伴噺涓嶈兘瓒呰繃鍓╀綑鍙綊杩樻暟閲�
+- 棰嗙敤浜恒�佸綊杩樹汉蹇呴』閫夋嫨绯荤粺鐢ㄦ埛
+
+**鍚庣鏍¢獙**锛�
+- 棰嗙敤鏃舵鏌ュ簱瀛樻槸鍚﹀厖瓒�
+- 褰掕繕鏁伴噺涓嶈兘瓒呰繃鍓╀綑鏁伴噺
+- 宸插叏閮ㄥ綊杩樼殑璁板綍涓嶈兘鍐嶅綊杩�
+
+### 2. 鐢ㄦ埛閫夋嫨
+
+棰嗙敤浜哄拰褰掕繕浜洪渶瑕佷粠绯荤粺鐢ㄦ埛琛ㄩ�夋嫨锛岃皟鐢ㄧ郴缁熺敤鎴锋帴鍙o細
+```
+POST /system/user/list
+```
+
+### 3. 浜у搧瑙勬牸閫夋嫨
+
+閫夋嫨浜у搧瑙勬牸鏃讹紝鍙互璋冪敤浜у搧鎺ュ彛锛�
+```
+POST /basic/product/pageModel
+```
+
+### 4. 搴撳瓨鑱斿姩
+
+- **棰嗙敤**锛氭柊澧炴椂鑷姩鍑哄簱锛堣皟鐢ㄥ簱瀛樻墸鍑忔帴鍙o級
+- **褰掕繕**锛氭柊澧炴椂鑷姩鍏ュ簱锛堣皟鐢ㄥ簱瀛樺鍔犳帴鍙o級
+
+---
+
+## 涓冦�侀敊璇爜璇存槑
+
+| 閿欒淇℃伅 | 鍘熷洜 | 瑙e喅鏂规 |
+|----------|------|----------|
+| 棰嗙敤璁板綍涓嶅瓨鍦� | ID鏃犳晥 | 妫�鏌ヨ褰旾D |
+| 浜у搧搴撳瓨涓嶅瓨鍦� | 搴撳瓨涓虹┖ | 妫�鏌ュ簱瀛樻暟鎹� |
+| 搴撳瓨涓嶈冻鏃犳硶鍑哄簱 | 搴撳瓨涓嶈冻 | 妫�鏌ュ簱瀛樻暟閲� |
+| 璇ラ鐢ㄨ褰曞凡鍏ㄩ儴褰掕繕 | 褰掕繕瀹屾垚 | 鏃犻渶鍐嶆褰掕繕 |
+| 褰掕繕鏁伴噺涓嶈兘澶т簬鍓╀綑鍙綊杩樻暟閲� | 鏁伴噺瓒呴檺 | 妫�鏌ュ墿浣欏彲褰掕繕鏁伴噺 |
+
+---
+
+## 鍏�佹祴璇曠敤渚�
+
+### 1. 鏂板棰嗙敤锛堣嚜鍔ㄥ嚭搴擄級
+
+```json
+POST /productBorrow/add
+{
+ "productModelId": 1,
+ "borrowQuantity": 10,
+ "borrowerId": 1,
+ "borrowerName": "绠$悊鍛�"
+}
+```
+
+### 2. 鏌ヨ搴撳瓨鍜岄鐢ㄩ噺
+
+```json
+POST /stockInventory/pageStockAndBorrow
+{
+ "current": 1,
+ "size": 10,
+ "topParentProductId": 277
+}
+```
+
+### 3. 褰掕繕浜у搧锛堣嚜鍔ㄥ叆搴擄級
+
+```json
+POST /productBorrowReturn/add
+{
+ "borrowId": 1,
+ "returnQuantity": 5,
+ "returnerId": 1,
+ "returnerName": "绠$悊鍛�"
+}
+```
+
+### 4. 鏌ヨ棰嗙敤璇︽儏
+
+```json
+POST /productBorrow/getDetail
+{
+ "id": 1
+}
+```
+
+### 5. 鏌ヨ褰掕繕璁板綍
+
+```json
+POST /productBorrowReturn/listByBorrowId
+{
+ "borrowId": 1,
+ "current": 1,
+ "size": 10
+}
+```
diff --git a/src/main/java/com/ruoyi/basic/service/impl/ProductServiceImpl.java b/src/main/java/com/ruoyi/basic/service/impl/ProductServiceImpl.java
index 2b9eb40..222a9a4 100644
--- a/src/main/java/com/ruoyi/basic/service/impl/ProductServiceImpl.java
+++ b/src/main/java/com/ruoyi/basic/service/impl/ProductServiceImpl.java
@@ -89,9 +89,6 @@
@Override
public int addOrEditProduct(ProductDto productDto) {
- if (ObjectUtils.isEmpty(productDto.getParentId())) {
- throw new IllegalArgumentException("璇烽�夋嫨鐖惰妭鐐�");
- }
String productName = StringUtils.trim(productDto.getProductName());
if (StringUtils.isEmpty(productName)) {
throw new IllegalArgumentException("浜у搧鍚嶇О涓嶈兘涓虹┖");
@@ -100,15 +97,17 @@
checkProductNameUnique(productDto.getParentId(), productName, productDto.getId());
if (productDto.getId() == null) {
// 鏂板浜у搧閫昏緫
- // 妫�鏌ョ埗鑺傜偣鏄惁瀛樺湪锛堝彲閫夛紝鏍规嵁涓氬姟闇�姹傦級
- Product parent = productMapper.selectById(productDto.getParentId());
- if (parent == null) {
- throw new IllegalArgumentException("鐖惰妭鐐逛笉瀛樺湪锛屾棤娉曟坊鍔犲瓙浜у搧");
+ // 濡傛灉鏈夌埗鑺傜偣锛屾鏌ョ埗鑺傜偣鏄惁瀛樺湪
+ if (productDto.getParentId() != null) {
+ Product parent = productMapper.selectById(productDto.getParentId());
+ if (parent == null) {
+ throw new IllegalArgumentException("鐖惰妭鐐逛笉瀛樺湪锛屾棤娉曟坊鍔犲瓙浜у搧");
+ }
}
return productMapper.insert(productDto);
} else {
// 缂栬緫浜у搧閫昏緫
- // 妫�鏌ヤ骇鍝佹槸鍚﹀瓨鍦紙鍙�夛紝鏍规嵁涓氬姟闇�姹傦級
+ // 妫�鏌ヤ骇鍝佹槸鍚﹀瓨鍦�
Product existingProduct = productMapper.selectById(productDto.getId());
if (existingProduct == null) {
throw new IllegalArgumentException("瑕佺紪杈戠殑浜у搧涓嶅瓨鍦�");
@@ -119,10 +118,15 @@
private void checkProductNameUnique(Long parentId, String productName, Long currentId) {
LambdaQueryWrapper<Product> queryWrapper = new LambdaQueryWrapper<>();
- queryWrapper.eq(Product::getParentId, parentId)
- .eq(Product::getProductName, productName)
- .ne(currentId != null, Product::getId, currentId)
- .last("limit 1");
+ queryWrapper.eq(Product::getProductName, productName)
+ .ne(currentId != null, Product::getId, currentId);
+ // 澶勭悊 parentId 涓� null 鐨勬儏鍐�
+ if (parentId == null) {
+ queryWrapper.isNull(Product::getParentId);
+ } else {
+ queryWrapper.eq(Product::getParentId, parentId);
+ }
+ queryWrapper.last("limit 1");
Product duplicateProduct = productMapper.selectOne(queryWrapper);
if (duplicateProduct != null) {
throw new IllegalArgumentException("瀵瑰簲鐨�" + productName + "宸茬粡瀛樺湪");
diff --git a/src/main/java/com/ruoyi/common/enums/StockInQualifiedRecordTypeEnum.java b/src/main/java/com/ruoyi/common/enums/StockInQualifiedRecordTypeEnum.java
index 7c149bc..0a1200a 100644
--- a/src/main/java/com/ruoyi/common/enums/StockInQualifiedRecordTypeEnum.java
+++ b/src/main/java/com/ruoyi/common/enums/StockInQualifiedRecordTypeEnum.java
@@ -19,7 +19,8 @@
RETURN_HE_IN("14", "閿�鍞��璐�-鍚堟牸鍏ュ簱"),
RETURN_UNSTOCK_IN("15", "閿�鍞��璐�-涓嶅悎鏍煎叆搴�"),
PICK_RETURN_IN("20", "棰嗘枡閫�鏂�-鍚堟牸鍏ュ簱"),
- FEED_RETURN_IN("22", "鐢熶骇閫�鏂�-鍚堟牸鍏ュ簱");
+ FEED_RETURN_IN("22", "鐢熶骇閫�鏂�-鍚堟牸鍏ュ簱"),
+ PRODUCT_BORROW_RETURN("23", "浜у搧褰掕繕鍏ュ簱");
diff --git a/src/main/java/com/ruoyi/common/enums/StockOutQualifiedRecordTypeEnum.java b/src/main/java/com/ruoyi/common/enums/StockOutQualifiedRecordTypeEnum.java
index ccc6d78..a810b31 100644
--- a/src/main/java/com/ruoyi/common/enums/StockOutQualifiedRecordTypeEnum.java
+++ b/src/main/java/com/ruoyi/common/enums/StockOutQualifiedRecordTypeEnum.java
@@ -12,7 +12,9 @@
CUSTOMIZATION_UNSTOCK_OUT("10", "涓嶅悎鏍艰嚜瀹氫箟鍑哄簱"),
SALE_SHIP_STOCK_OUT("13", "閿�鍞�-鍙戣揣鍑哄簱"),
PICK_STOCK_OUT("14", "鐢熶骇棰嗘枡鍑哄簱"),
- FEED_STOCK_OUT("15", "鐢熶骇琛ユ枡鍑哄簱");
+ FEED_STOCK_OUT("15", "鐢熶骇琛ユ枡鍑哄簱"),
+ PRODUCT_BORROW("16", "浜у搧棰嗙敤鍑哄簱"),
+ PRODUCT_RETURN("17", "浜у搧褰掕繕鍏ュ簱");
private final String code;
private final String value;
diff --git a/src/main/java/com/ruoyi/stock/controller/ProductBorrowController.java b/src/main/java/com/ruoyi/stock/controller/ProductBorrowController.java
new file mode 100644
index 0000000..03954ff
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/controller/ProductBorrowController.java
@@ -0,0 +1,96 @@
+package com.ruoyi.stock.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+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.stock.dto.ProductBorrowDto;
+import com.ruoyi.stock.service.ProductBorrowService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import org.springframework.util.CollectionUtils;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 浜у搧棰嗙敤 Controller
+ *
+ * @author ruoyi
+ */
+@Tag(name = "浜у搧棰嗙敤")
+@RestController
+@RequestMapping("/productBorrow")
+@RequiredArgsConstructor
+public class ProductBorrowController {
+
+ private final ProductBorrowService productBorrowService;
+
+ @PostMapping("/listPage")
+ @Log(title = "浜у搧棰嗙敤-鍒楄〃", businessType = BusinessType.OTHER)
+ @Operation(summary = "鍒嗛〉鏌ヨ棰嗙敤璁板綍")
+ public AjaxResult listPage(@RequestBody Page<ProductBorrowDto> page, @RequestBody ProductBorrowDto dto) {
+ IPage<ProductBorrowDto> result = productBorrowService.listPage(page, dto);
+ return AjaxResult.success(result);
+ }
+
+ @PostMapping("/getDetail")
+ @Log(title = "浜у搧棰嗙敤-璇︽儏", businessType = BusinessType.OTHER)
+ @Operation(summary = "鏌ヨ棰嗙敤璁板綍璇︽儏")
+ public AjaxResult getDetail(@RequestBody ProductBorrowDto dto) {
+ ProductBorrowDto result = productBorrowService.getDetailById(dto.getId());
+ return AjaxResult.success(result);
+ }
+
+ @PostMapping("/add")
+ @Log(title = "浜у搧棰嗙敤-鏂板", businessType = BusinessType.INSERT)
+ @Operation(summary = "鏂板棰嗙敤璁板綍")
+ public AjaxResult add(@RequestBody ProductBorrowDto dto) {
+ productBorrowService.add(dto);
+ return AjaxResult.success();
+ }
+
+ @PostMapping("/update")
+ @Log(title = "浜у搧棰嗙敤-淇敼", businessType = BusinessType.UPDATE)
+ @Operation(summary = "淇敼棰嗙敤璁板綍")
+ public AjaxResult update(@RequestBody ProductBorrowDto dto) {
+ productBorrowService.update(dto);
+ return AjaxResult.success();
+ }
+
+ @PostMapping("/delete")
+ @Log(title = "浜у搧棰嗙敤-鍒犻櫎", businessType = BusinessType.DELETE)
+ @Operation(summary = "鍒犻櫎棰嗙敤璁板綍")
+ public AjaxResult delete(@RequestBody List<Long> ids) {
+ if (CollectionUtils.isEmpty(ids)) {
+ return AjaxResult.error("璇烽�夋嫨鑷冲皯涓�鏉℃暟鎹�");
+ }
+ productBorrowService.delete(ids);
+ return AjaxResult.success();
+ }
+
+ @PostMapping("/approve")
+ @Log(title = "浜у搧棰嗙敤-瀹℃壒", businessType = BusinessType.UPDATE)
+ @Operation(summary = "鎵归噺瀹℃壒棰嗙敤璁板綍")
+ public AjaxResult approve(@RequestBody ProductBorrowDto dto) {
+ if (CollectionUtils.isEmpty(dto.getIds())) {
+ return AjaxResult.error("璇烽�夋嫨鑷冲皯涓�鏉℃暟鎹�");
+ }
+ productBorrowService.batchApprove(dto.getIds(), dto.getApprovalStatusParam());
+ return AjaxResult.success();
+ }
+
+ @PostMapping("/reAudit")
+ @Log(title = "浜у搧棰嗙敤-鍙嶅", businessType = BusinessType.UPDATE)
+ @Operation(summary = "鎵归噺鍙嶅棰嗙敤璁板綍")
+ public AjaxResult reAudit(@RequestBody ProductBorrowDto dto) {
+ if (CollectionUtils.isEmpty(dto.getIds())) {
+ return AjaxResult.error("璇烽�夋嫨鑷冲皯涓�鏉℃暟鎹�");
+ }
+ // 鍙嶅锛氬皢宸查�氳繃鐨勮褰曠姸鎬佹敼鍥炲緟瀹℃壒锛岄渶瑕佽�冭檻鏄惁宸茬粡褰掕繕
+ // 姝ゅ鏆備笉瀹炵幇鍙嶅閫昏緫锛屽闇�瑕佸彲浠ユ墿灞�
+ return AjaxResult.error("鍙嶅鍔熻兘鏆傛湭瀹炵幇");
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/ruoyi/stock/controller/ProductBorrowReturnController.java b/src/main/java/com/ruoyi/stock/controller/ProductBorrowReturnController.java
new file mode 100644
index 0000000..7e7b88f
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/controller/ProductBorrowReturnController.java
@@ -0,0 +1,51 @@
+package com.ruoyi.stock.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+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.stock.dto.ProductBorrowReturnDto;
+import com.ruoyi.stock.service.ProductBorrowReturnService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 浜у搧褰掕繕 Controller
+ *
+ * @author ruoyi
+ */
+@Tag(name = "浜у搧褰掕繕")
+@RestController
+@RequestMapping("/productBorrowReturn")
+@RequiredArgsConstructor
+public class ProductBorrowReturnController {
+
+ private final ProductBorrowReturnService productBorrowReturnService;
+
+ @PostMapping("/listPage")
+ @Log(title = "浜у搧褰掕繕-鍒楄〃", businessType = BusinessType.OTHER)
+ @Operation(summary = "鍒嗛〉鏌ヨ褰掕繕璁板綍")
+ public AjaxResult listPage(@RequestBody Page<ProductBorrowReturnDto> page, ProductBorrowReturnDto dto) {
+ IPage<ProductBorrowReturnDto> result = productBorrowReturnService.listPage(page, dto);
+ return AjaxResult.success(result);
+ }
+
+ @PostMapping("/listByBorrowId")
+ @Log(title = "浜у搧褰掕繕-鏍规嵁棰嗙敤ID鏌ヨ", businessType = BusinessType.OTHER)
+ @Operation(summary = "鏌ヨ鏌愪釜棰嗙敤璁板綍鐨勫綊杩樿褰�")
+ public AjaxResult listByBorrowId(@RequestBody Page<ProductBorrowReturnDto> page, @RequestBody ProductBorrowReturnDto dto) {
+ IPage<ProductBorrowReturnDto> result = productBorrowReturnService.listByBorrowId(page, dto.getBorrowId());
+ return AjaxResult.success(result);
+ }
+
+ @PostMapping("/add")
+ @Log(title = "浜у搧褰掕繕-鏂板", businessType = BusinessType.INSERT)
+ @Operation(summary = "鏂板褰掕繕璁板綍锛堢洿鎺ュ鍔犲簱瀛橈紝鏃犻渶瀹℃壒锛�")
+ public AjaxResult add(@RequestBody ProductBorrowReturnDto dto) {
+ productBorrowReturnService.add(dto);
+ return AjaxResult.success();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/ruoyi/stock/controller/StockInventoryController.java b/src/main/java/com/ruoyi/stock/controller/StockInventoryController.java
index a0a9fa5..d6d72c0 100644
--- a/src/main/java/com/ruoyi/stock/controller/StockInventoryController.java
+++ b/src/main/java/com/ruoyi/stock/controller/StockInventoryController.java
@@ -147,4 +147,10 @@
public R getByModelId(Long productModelId) {
return R.ok(stockInventoryService.getByModelId(productModelId));
}
+
+ @PostMapping("/pageStockAndBorrow")
+ @Operation(summary = "鍒嗛〉鏌ヨ浜у搧搴撳瓨鍜岄鐢ㄩ噺")
+ public R pageStockAndBorrow(@RequestBody Page page, @RequestBody StockInventoryDto stockInventoryDto) {
+ return R.ok(stockInventoryService.pageStockAndBorrow(page, stockInventoryDto));
+ }
}
diff --git a/src/main/java/com/ruoyi/stock/dto/ProductBorrowDto.java b/src/main/java/com/ruoyi/stock/dto/ProductBorrowDto.java
new file mode 100644
index 0000000..7f34e4c
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/dto/ProductBorrowDto.java
@@ -0,0 +1,49 @@
+package com.ruoyi.stock.dto;
+
+import com.ruoyi.stock.pojo.ProductBorrow;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.List;
+
+/**
+ * 浜у搧棰嗙敤DTO
+ *
+ * @author ruoyi
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Schema(description = "浜у搧棰嗙敤DTO")
+public class ProductBorrowDto extends ProductBorrow {
+
+ @Schema(description = "浜у搧鍚嶇О")
+ private String productName;
+
+ @Schema(description = "瑙勬牸鍨嬪彿")
+ private String model;
+
+ @Schema(description = "浜у搧缂栫爜")
+ private String productCode;
+
+ @Schema(description = "鍗曚綅")
+ private String unit;
+
+ @Schema(description = "鍓╀綑鍙綊杩樻暟閲�")
+ private java.math.BigDecimal remainingQuantity;
+
+ @Schema(description = "瀹℃壒鐘舵�佸悕绉�")
+ private String approvalStatusName;
+
+ @Schema(description = "褰掕繕鐘舵�佸悕绉�")
+ private String statusName;
+
+ @Schema(description = "鎵归噺瀹℃壒ID鍒楄〃")
+ private List<Long> ids;
+
+ @Schema(description = "瀹℃壒鐘舵�侊紙鐢ㄤ簬瀹℃壒鎿嶄綔锛�")
+ private Integer approvalStatusParam;
+
+ @Schema(description = "椤堕儴鐖朵骇鍝両D锛堜骇鍝佸垎绫籌D锛�")
+ private Long topParentProductId;
+}
diff --git a/src/main/java/com/ruoyi/stock/dto/ProductBorrowReturnDto.java b/src/main/java/com/ruoyi/stock/dto/ProductBorrowReturnDto.java
new file mode 100644
index 0000000..367875f
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/dto/ProductBorrowReturnDto.java
@@ -0,0 +1,38 @@
+package com.ruoyi.stock.dto;
+
+import com.ruoyi.stock.pojo.ProductBorrowReturn;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 浜у搧褰掕繕DTO
+ *
+ * @author ruoyi
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Schema(description = "浜у搧褰掕繕DTO")
+public class ProductBorrowReturnDto extends ProductBorrowReturn {
+
+ @Schema(description = "棰嗙敤鍗曞彿")
+ private String borrowNo;
+
+ @Schema(description = "浜у搧鍚嶇О")
+ private String productName;
+
+ @Schema(description = "瑙勬牸鍨嬪彿")
+ private String model;
+
+ @Schema(description = "浜у搧缂栫爜")
+ private String productCode;
+
+ @Schema(description = "鍗曚綅")
+ private String unit;
+
+ @Schema(description = "棰嗙敤鏁伴噺")
+ private java.math.BigDecimal borrowQuantity;
+
+ @Schema(description = "棰嗙敤浜哄鍚�")
+ private String borrowerName;
+}
\ No newline at end of file
diff --git a/src/main/java/com/ruoyi/stock/dto/StockInventoryDto.java b/src/main/java/com/ruoyi/stock/dto/StockInventoryDto.java
index 33b7599..5fa747a 100644
--- a/src/main/java/com/ruoyi/stock/dto/StockInventoryDto.java
+++ b/src/main/java/com/ruoyi/stock/dto/StockInventoryDto.java
@@ -80,4 +80,16 @@
@Schema(description = "浜у搧id")
private Long productId;
+
+ @Schema(description = "琚鐢ㄦ暟閲忥紙鏈綊杩橈級")
+ private BigDecimal borrowedQuantity;
+
+ @Schema(description = "鍙鐢ㄦ暟閲忥紙搴撳瓨 - 琚鐢級")
+ private BigDecimal availableQuantity;
+
+ @Schema(description = "浜у搧缂栫爜")
+ private String productCode;
+
+ @Schema(description = "瀹℃壒鐘舵�侊紙鐢ㄤ簬鍏ュ簱/鍑哄簱璁板綍锛�")
+ private Integer approvalStatus;
}
diff --git a/src/main/java/com/ruoyi/stock/mapper/ProductBorrowMapper.java b/src/main/java/com/ruoyi/stock/mapper/ProductBorrowMapper.java
new file mode 100644
index 0000000..458a97d
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/mapper/ProductBorrowMapper.java
@@ -0,0 +1,28 @@
+package com.ruoyi.stock.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.stock.dto.ProductBorrowDto;
+import com.ruoyi.stock.pojo.ProductBorrow;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * 浜у搧棰嗙敤 Mapper 鎺ュ彛
+ *
+ * @author ruoyi
+ */
+@Mapper
+public interface ProductBorrowMapper extends BaseMapper<ProductBorrow> {
+
+ /**
+ * 鍒嗛〉鏌ヨ棰嗙敤璁板綍
+ */
+ IPage<ProductBorrowDto> listPage(Page<ProductBorrowDto> page, @Param("ew") ProductBorrowDto dto);
+
+ /**
+ * 鏍规嵁ID鏌ヨ璇︽儏
+ */
+ ProductBorrowDto selectDetailById(@Param("id") Long id);
+}
diff --git a/src/main/java/com/ruoyi/stock/mapper/ProductBorrowReturnMapper.java b/src/main/java/com/ruoyi/stock/mapper/ProductBorrowReturnMapper.java
new file mode 100644
index 0000000..3039239
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/mapper/ProductBorrowReturnMapper.java
@@ -0,0 +1,28 @@
+package com.ruoyi.stock.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.stock.dto.ProductBorrowReturnDto;
+import com.ruoyi.stock.pojo.ProductBorrowReturn;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * 浜у搧褰掕繕璁板綍 Mapper 鎺ュ彛
+ *
+ * @author ruoyi
+ */
+@Mapper
+public interface ProductBorrowReturnMapper extends BaseMapper<ProductBorrowReturn> {
+
+ /**
+ * 鍒嗛〉鏌ヨ褰掕繕璁板綍
+ */
+ IPage<ProductBorrowReturnDto> listPage(Page<ProductBorrowReturnDto> page, @Param("ew") ProductBorrowReturnDto dto);
+
+ /**
+ * 鏍规嵁棰嗙敤ID鏌ヨ褰掕繕璁板綍
+ */
+ IPage<ProductBorrowReturnDto> listByBorrowId(Page<ProductBorrowReturnDto> page, @Param("borrowId") Long borrowId);
+}
diff --git a/src/main/java/com/ruoyi/stock/mapper/StockInventoryMapper.java b/src/main/java/com/ruoyi/stock/mapper/StockInventoryMapper.java
index dbc271b..7fc1c41 100644
--- a/src/main/java/com/ruoyi/stock/mapper/StockInventoryMapper.java
+++ b/src/main/java/com/ruoyi/stock/mapper/StockInventoryMapper.java
@@ -58,4 +58,9 @@
List<StockInventory> getByModelId(@Param("productModelId") Long productModelId);
IPage<StockInventoryDto> getBatchNoQty(Page page, @Param("ew") StockInventoryDto stockInventoryDto);
+
+ /**
+ * 鍒嗛〉鏌ヨ浜у搧搴撳瓨鍜岄鐢ㄩ噺
+ */
+ IPage<StockInventoryDto> pageStockAndBorrow(Page page, @Param("ew") StockInventoryDto stockInventoryDto);
}
diff --git a/src/main/java/com/ruoyi/stock/pojo/ProductBorrow.java b/src/main/java/com/ruoyi/stock/pojo/ProductBorrow.java
new file mode 100644
index 0000000..87b525d
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/pojo/ProductBorrow.java
@@ -0,0 +1,96 @@
+package com.ruoyi.stock.pojo;
+
+import com.baomidou.mybatisplus.annotation.*;
+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.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 浜у搧棰嗙敤琛�
+ *
+ * @author ruoyi
+ */
+@Data
+@TableName("product_borrow")
+@Schema(name = "ProductBorrow瀵硅薄", description = "浜у搧棰嗙敤琛�")
+public class ProductBorrow implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "涓婚敭")
+ @TableId(type = IdType.AUTO)
+ private Long id;
+
+ @Schema(description = "棰嗙敤鍗曞彿")
+ private String borrowNo;
+
+ @Schema(description = "浜у搧瑙勬牸ID")
+ private Long productModelId;
+
+ @Schema(description = "鎵瑰彿")
+ private String batchNo;
+
+ @Schema(description = "棰嗙敤鏁伴噺")
+ private BigDecimal borrowQuantity;
+
+ @Schema(description = "宸插綊杩樻暟閲�")
+ private BigDecimal returnedQuantity;
+
+ @Schema(description = "棰嗙敤浜篒D")
+ private Long borrowerId;
+
+ @Schema(description = "棰嗙敤浜哄鍚�")
+ private String borrowerName;
+
+ @Schema(description = "棰嗙敤鏃堕棿")
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private LocalDateTime borrowTime;
+
+ @Schema(description = "棰勮褰掕繕鏃堕棿")
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private LocalDateTime expectedReturnTime;
+
+ @Schema(description = "瀹℃壒鐘舵�侊紙0-寰呭鎵癸紝1-宸查�氳繃锛�2-宸查┏鍥烇級")
+ private Integer approvalStatus;
+
+ @Schema(description = "褰掕繕鐘舵�侊紙0-鏈綊杩橈紝1-閮ㄥ垎褰掕繕锛�2-宸插叏閮ㄥ綊杩橈級")
+ private Integer status;
+
+ @Schema(description = "澶囨敞")
+ private String remark;
+
+ @Schema(description = "绉熸埛ID")
+ @TableField(fill = FieldFill.INSERT)
+ private Long tenantId;
+
+ @Schema(description = "閮ㄩ棬ID")
+ @TableField(fill = FieldFill.INSERT)
+ private Long deptId;
+
+ @Schema(description = "鍒涘缓浜�")
+ @TableField(fill = FieldFill.INSERT)
+ private Integer createUser;
+
+ @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)
+ private Integer updateUser;
+
+ @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;
+}
diff --git a/src/main/java/com/ruoyi/stock/pojo/ProductBorrowReturn.java b/src/main/java/com/ruoyi/stock/pojo/ProductBorrowReturn.java
new file mode 100644
index 0000000..3f7585d
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/pojo/ProductBorrowReturn.java
@@ -0,0 +1,72 @@
+package com.ruoyi.stock.pojo;
+
+import com.baomidou.mybatisplus.annotation.*;
+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.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 浜у搧褰掕繕璁板綍琛�
+ *
+ * @author ruoyi
+ */
+@Data
+@TableName("product_borrow_return")
+@Schema(name = "ProductBorrowReturn瀵硅薄", description = "浜у搧褰掕繕璁板綍琛�")
+public class ProductBorrowReturn implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "涓婚敭")
+ @TableId(type = IdType.AUTO)
+ private Long id;
+
+ @Schema(description = "棰嗙敤璁板綍ID")
+ private Long borrowId;
+
+ @Schema(description = "浜у搧瑙勬牸ID")
+ private Long productModelId;
+
+ @Schema(description = "鎵瑰彿")
+ private String batchNo;
+
+ @Schema(description = "褰掕繕鏁伴噺")
+ private BigDecimal returnQuantity;
+
+ @Schema(description = "褰掕繕浜篒D")
+ private Long returnerId;
+
+ @Schema(description = "褰掕繕浜哄鍚�")
+ private String returnerName;
+
+ @Schema(description = "褰掕繕鏃堕棿")
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private LocalDateTime returnTime;
+
+ @Schema(description = "澶囨敞")
+ private String remark;
+
+ @Schema(description = "绉熸埛ID")
+ @TableField(fill = FieldFill.INSERT)
+ private Long tenantId;
+
+ @Schema(description = "閮ㄩ棬ID")
+ @TableField(fill = FieldFill.INSERT)
+ private Long deptId;
+
+ @Schema(description = "鍒涘缓浜�")
+ @TableField(fill = FieldFill.INSERT)
+ private Integer createUser;
+
+ @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;
+}
diff --git a/src/main/java/com/ruoyi/stock/service/ProductBorrowReturnService.java b/src/main/java/com/ruoyi/stock/service/ProductBorrowReturnService.java
new file mode 100644
index 0000000..139ba6d
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/service/ProductBorrowReturnService.java
@@ -0,0 +1,30 @@
+package com.ruoyi.stock.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.stock.dto.ProductBorrowReturnDto;
+import com.ruoyi.stock.pojo.ProductBorrowReturn;
+
+/**
+ * 浜у搧褰掕繕璁板綍 Service 鎺ュ彛
+ *
+ * @author ruoyi
+ */
+public interface ProductBorrowReturnService extends IService<ProductBorrowReturn> {
+
+ /**
+ * 鍒嗛〉鏌ヨ褰掕繕璁板綍
+ */
+ IPage<ProductBorrowReturnDto> listPage(Page<ProductBorrowReturnDto> page, ProductBorrowReturnDto dto);
+
+ /**
+ * 鏂板褰掕繕璁板綍锛堢洿鎺ュ鍔犲簱瀛橈紝鏃犻渶瀹℃壒锛�
+ */
+ Boolean add(ProductBorrowReturnDto dto);
+
+ /**
+ * 鏌ヨ鏌愪釜棰嗙敤璁板綍鐨勫綊杩樿褰�
+ */
+ IPage<ProductBorrowReturnDto> listByBorrowId(Page<ProductBorrowReturnDto> page, Long borrowId);
+}
diff --git a/src/main/java/com/ruoyi/stock/service/ProductBorrowService.java b/src/main/java/com/ruoyi/stock/service/ProductBorrowService.java
new file mode 100644
index 0000000..7f87951
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/service/ProductBorrowService.java
@@ -0,0 +1,52 @@
+package com.ruoyi.stock.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.stock.dto.ProductBorrowDto;
+import com.ruoyi.stock.pojo.ProductBorrow;
+
+import java.util.List;
+
+/**
+ * 浜у搧棰嗙敤 Service 鎺ュ彛
+ *
+ * @author ruoyi
+ */
+public interface ProductBorrowService extends IService<ProductBorrow> {
+
+ /**
+ * 鍒嗛〉鏌ヨ棰嗙敤璁板綍
+ */
+ IPage<ProductBorrowDto> listPage(Page<ProductBorrowDto> page, ProductBorrowDto dto);
+
+ /**
+ * 鏂板棰嗙敤璁板綍锛堝緟瀹℃壒锛�
+ */
+ Boolean add(ProductBorrowDto dto);
+
+ /**
+ * 鏇存柊棰嗙敤璁板綍
+ */
+ Boolean update(ProductBorrowDto dto);
+
+ /**
+ * 鍒犻櫎棰嗙敤璁板綍锛堜粎寰呭鎵圭姸鎬佸彲鍒犻櫎锛�
+ */
+ Boolean delete(List<Long> ids);
+
+ /**
+ * 鎵归噺瀹℃壒
+ */
+ Boolean batchApprove(List<Long> ids, Integer approvalStatus);
+
+ /**
+ * 瀹℃壒閫氳繃鍚庣殑鍥炶皟锛堟墸鍑忓簱瀛橈級
+ */
+ void onApprovePass(ProductBorrow borrow);
+
+ /**
+ * 鏍规嵁ID鏌ヨ璇︽儏
+ */
+ ProductBorrowDto getDetailById(Long id);
+}
diff --git a/src/main/java/com/ruoyi/stock/service/StockInventoryService.java b/src/main/java/com/ruoyi/stock/service/StockInventoryService.java
index 5d534d5..dd74407 100644
--- a/src/main/java/com/ruoyi/stock/service/StockInventoryService.java
+++ b/src/main/java/com/ruoyi/stock/service/StockInventoryService.java
@@ -49,4 +49,9 @@
List<StockInventory> getByModelId(Long modelId);
IPage<StockInventoryDto> getBatchNoQty(Page page, StockInventoryDto stockInventoryDto);
+
+ /**
+ * 鍒嗛〉鏌ヨ浜у搧搴撳瓨鍜岄鐢ㄩ噺
+ */
+ IPage<StockInventoryDto> pageStockAndBorrow(Page page, StockInventoryDto stockInventoryDto);
}
diff --git a/src/main/java/com/ruoyi/stock/service/impl/ProductBorrowReturnServiceImpl.java b/src/main/java/com/ruoyi/stock/service/impl/ProductBorrowReturnServiceImpl.java
new file mode 100644
index 0000000..06d8534
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/service/impl/ProductBorrowReturnServiceImpl.java
@@ -0,0 +1,109 @@
+package com.ruoyi.stock.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.common.enums.StockInQualifiedRecordTypeEnum;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.stock.dto.ProductBorrowReturnDto;
+import com.ruoyi.stock.dto.StockInventoryDto;
+import com.ruoyi.stock.mapper.ProductBorrowReturnMapper;
+import com.ruoyi.stock.pojo.ProductBorrow;
+import com.ruoyi.stock.pojo.ProductBorrowReturn;
+import com.ruoyi.stock.service.ProductBorrowReturnService;
+import com.ruoyi.stock.service.ProductBorrowService;
+import com.ruoyi.stock.service.StockInventoryService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 浜у搧褰掕繕璁板綍 Service 瀹炵幇绫�
+ *
+ * @author ruoyi
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class ProductBorrowReturnServiceImpl extends ServiceImpl<ProductBorrowReturnMapper, ProductBorrowReturn> implements ProductBorrowReturnService {
+
+ private final ProductBorrowReturnMapper productBorrowReturnMapper;
+ private final ProductBorrowService productBorrowService;
+ private final StockInventoryService stockInventoryService;
+
+ @Override
+ public IPage<ProductBorrowReturnDto> listPage(Page<ProductBorrowReturnDto> page, ProductBorrowReturnDto dto) {
+ return productBorrowReturnMapper.listPage(page, dto);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Boolean add(ProductBorrowReturnDto dto) {
+ // 1. 鏌ヨ棰嗙敤璁板綍
+ ProductBorrow borrow = productBorrowService.getById(dto.getBorrowId());
+ if (borrow == null) {
+ throw new ServiceException("棰嗙敤璁板綍涓嶅瓨鍦�");
+ }
+
+ // 2. 妫�鏌ラ鐢ㄨ褰曠姸鎬侊紙宸查�氳繃鐨勮褰曟墠鑳藉綊杩橈級
+ if (borrow.getApprovalStatus() != 1) {
+ throw new ServiceException("棰嗙敤璁板綍鏈畬鎴愬嚭搴擄紝鏃犳硶褰掕繕");
+ }
+ if (borrow.getStatus() == 2) {
+ throw new ServiceException("璇ラ鐢ㄨ褰曞凡鍏ㄩ儴褰掕繕");
+ }
+
+ // 3. 璁$畻鍓╀綑鍙綊杩樻暟閲�
+ BigDecimal returnedQty = borrow.getReturnedQuantity() != null ? borrow.getReturnedQuantity() : BigDecimal.ZERO;
+ BigDecimal remainingQty = borrow.getBorrowQuantity().subtract(returnedQty);
+
+ // 4. 楠岃瘉褰掕繕鏁伴噺
+ if (dto.getReturnQuantity().compareTo(remainingQty) > 0) {
+ throw new ServiceException("褰掕繕鏁伴噺涓嶈兘澶т簬鍓╀綑鍙綊杩樻暟閲忥細" + remainingQty);
+ }
+
+ // 5. 璁剧疆蹇呰瀛楁
+ dto.setProductModelId(borrow.getProductModelId());
+ dto.setBatchNo(borrow.getBatchNo());
+ dto.setReturnTime(LocalDateTime.now());
+
+ // 6. 淇濆瓨褰掕繕璁板綍
+ save(dto);
+
+ // 7. 鏇存柊棰嗙敤璁板綍鐨勫凡褰掕繕鏁伴噺鍜岀姸鎬�
+ BigDecimal newReturnedQty = returnedQty.add(dto.getReturnQuantity());
+ borrow.setReturnedQuantity(newReturnedQty);
+
+ // 鍒ゆ柇鏄惁鍏ㄩ儴褰掕繕
+ if (newReturnedQty.compareTo(borrow.getBorrowQuantity()) >= 0) {
+ borrow.setStatus(2); // 宸插叏閮ㄥ綊杩�
+ } else if (newReturnedQty.compareTo(BigDecimal.ZERO) > 0) {
+ borrow.setStatus(1); // 閮ㄥ垎褰掕繕
+ }
+ productBorrowService.updateById(borrow);
+
+ // 8. 澧炲姞搴撳瓨锛堝叆搴擄級锛岃缃鎵圭姸鎬佷负宸查�氳繃锛岃嚜鍔ㄥ鏍�
+ StockInventoryDto stockInDto = new StockInventoryDto();
+ stockInDto.setProductModelId(borrow.getProductModelId());
+ stockInDto.setBatchNo(borrow.getBatchNo());
+ stockInDto.setQualitity(dto.getReturnQuantity());
+ stockInDto.setRecordType(String.valueOf(StockInQualifiedRecordTypeEnum.PRODUCT_BORROW_RETURN.getCode()));
+ stockInDto.setRecordId(dto.getId());
+ stockInDto.setRemark("浜у搧褰掕繕鍏ュ簱锛岄鐢ㄥ崟鍙凤細" + borrow.getBorrowNo());
+ // 璁剧疆瀹℃壒鐘舵�佷负宸查�氳繃锛岃嚜鍔ㄥ鏍�
+ stockInDto.setApprovalStatus(1);
+ stockInventoryService.addstockInventory(stockInDto);
+
+ log.info("浜у搧褰掕繕鎴愬姛锛岄鐢ㄥ崟鍙凤細{}锛屽綊杩樻暟閲忥細{}", borrow.getBorrowNo(), dto.getReturnQuantity());
+ return true;
+ }
+
+ @Override
+ public IPage<ProductBorrowReturnDto> listByBorrowId(Page<ProductBorrowReturnDto> page, Long borrowId) {
+ return productBorrowReturnMapper.listByBorrowId(page, borrowId);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/ruoyi/stock/service/impl/ProductBorrowServiceImpl.java b/src/main/java/com/ruoyi/stock/service/impl/ProductBorrowServiceImpl.java
new file mode 100644
index 0000000..2fd71d3
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/service/impl/ProductBorrowServiceImpl.java
@@ -0,0 +1,214 @@
+package com.ruoyi.stock.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.enums.StockOutQualifiedRecordTypeEnum;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.stock.dto.ProductBorrowDto;
+import com.ruoyi.stock.dto.StockInventoryDto;
+import com.ruoyi.stock.mapper.ProductBorrowMapper;
+import com.ruoyi.stock.pojo.ProductBorrow;
+import com.ruoyi.stock.service.ProductBorrowService;
+import com.ruoyi.stock.service.StockInventoryService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+
+/**
+ * 浜у搧棰嗙敤 Service 瀹炵幇绫�
+ *
+ * @author ruoyi
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class ProductBorrowServiceImpl extends ServiceImpl<ProductBorrowMapper, ProductBorrow> implements ProductBorrowService {
+
+ private final ProductBorrowMapper productBorrowMapper;
+ private final StockInventoryService stockInventoryService;
+
+ @Override
+ public IPage<ProductBorrowDto> listPage(Page<ProductBorrowDto> page, ProductBorrowDto dto) {
+ IPage<ProductBorrowDto> result = productBorrowMapper.listPage(page, dto);
+ // 濉厖鐘舵�佸悕绉�
+ result.getRecords().forEach(this::fillStatusName);
+ return result;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Boolean add(ProductBorrowDto dto) {
+ // 鐢熸垚棰嗙敤鍗曞彿
+ String borrowNo = generateBorrowNo();
+ dto.setBorrowNo(borrowNo);
+
+ // 璁剧疆鍒濆鐘舵�� - 鐩存帴璁句负宸查�氳繃锛堝彇娑堝鎵规祦绋嬶級
+ dto.setApprovalStatus(1); // 宸查�氳繃
+ dto.setStatus(0); // 鏈綊杩�
+ dto.setReturnedQuantity(BigDecimal.ZERO);
+ dto.setBorrowTime(LocalDateTime.now());
+
+ // 淇濆瓨棰嗙敤璁板綍
+ save(dto);
+
+ // 棰嗙敤鏃剁洿鎺ュ仛鍑哄簱鎿嶄綔锛岃嚜鍔ㄥ鏍搁�氳繃
+ doBorrowStockOut(dto);
+
+ return true;
+ }
+
+ /**
+ * 鎵ц棰嗙敤鍑哄簱鎿嶄綔锛堣嚜鍔ㄥ鏍搁�氳繃锛�
+ */
+ private void doBorrowStockOut(ProductBorrow borrow) {
+ // 鏋勫缓搴撳瓨鎵e噺鍙傛暟
+ StockInventoryDto stockInventoryDto = new StockInventoryDto();
+ stockInventoryDto.setProductModelId(borrow.getProductModelId());
+ stockInventoryDto.setBatchNo(borrow.getBatchNo());
+ stockInventoryDto.setQualitity(borrow.getBorrowQuantity());
+ stockInventoryDto.setRecordType(String.valueOf(StockOutQualifiedRecordTypeEnum.PRODUCT_BORROW.getCode()));
+ stockInventoryDto.setRecordId(borrow.getId());
+ stockInventoryDto.setRemark("浜у搧棰嗙敤鍑哄簱锛岄鐢ㄥ崟鍙凤細" + borrow.getBorrowNo());
+ // 璁剧疆瀹℃壒鐘舵�佷负宸查�氳繃锛岃嚜鍔ㄥ鏍�
+ stockInventoryDto.setApprovalStatus(1);
+
+ // 璋冪敤搴撳瓨鏈嶅姟鎵e噺搴撳瓨
+ stockInventoryService.subtractStockInventory(stockInventoryDto);
+
+ log.info("浜у搧棰嗙敤鍑哄簱鎴愬姛锛岄鐢ㄥ崟鍙凤細{}锛屾暟閲忥細{}", borrow.getBorrowNo(), borrow.getBorrowQuantity());
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Boolean update(ProductBorrowDto dto) {
+ ProductBorrow existing = getById(dto.getId());
+ if (existing == null) {
+ throw new ServiceException("棰嗙敤璁板綍涓嶅瓨鍦�");
+ }
+ // 鍙湁鏈�氳繃瀹℃壒鐘舵�佹墠鑳戒慨鏀癸紙浣嗙幇鍦ㄩ粯璁ゅ凡閫氳繃锛屾墍浠ヤ笉鍏佽淇敼锛�
+ if (existing.getApprovalStatus() != 0) {
+ throw new ServiceException("棰嗙敤璁板綍宸插畬鎴愬嚭搴擄紝涓嶈兘淇敼");
+ }
+ return updateById(dto);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Boolean delete(List<Long> ids) {
+ for (Long id : ids) {
+ ProductBorrow borrow = getById(id);
+ if (borrow != null && borrow.getApprovalStatus() != 0) {
+ throw new ServiceException("棰嗙敤璁板綍宸插畬鎴愬嚭搴擄紝涓嶈兘鍒犻櫎");
+ }
+ }
+ return removeByIds(ids);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Boolean batchApprove(List<Long> ids, Integer approvalStatus) {
+ for (Long id : ids) {
+ ProductBorrow borrow = getById(id);
+ if (borrow == null) {
+ throw new ServiceException("棰嗙敤璁板綍涓嶅瓨鍦�: " + id);
+ }
+ if (borrow.getApprovalStatus() != 0) {
+ throw new ServiceException("鍙湁寰呭鎵圭姸鎬佺殑璁板綍鎵嶈兘瀹℃壒");
+ }
+
+ borrow.setApprovalStatus(approvalStatus);
+ updateById(borrow);
+
+ // 瀹℃壒閫氳繃鍚庢墸鍑忓簱瀛�
+ if (approvalStatus == 1) {
+ onApprovePass(borrow);
+ }
+ }
+ return true;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void onApprovePass(ProductBorrow borrow) {
+ // 鏋勫缓搴撳瓨鎵e噺鍙傛暟锛岃缃鎵圭姸鎬佷负宸查�氳繃
+ StockInventoryDto stockInventoryDto = new StockInventoryDto();
+ stockInventoryDto.setProductModelId(borrow.getProductModelId());
+ stockInventoryDto.setBatchNo(borrow.getBatchNo());
+ stockInventoryDto.setQualitity(borrow.getBorrowQuantity());
+ stockInventoryDto.setRecordType(String.valueOf(StockOutQualifiedRecordTypeEnum.PRODUCT_BORROW.getCode()));
+ stockInventoryDto.setRecordId(borrow.getId());
+ stockInventoryDto.setRemark("浜у搧棰嗙敤鍑哄簱锛岄鐢ㄥ崟鍙凤細" + borrow.getBorrowNo());
+ stockInventoryDto.setApprovalStatus(1);
+
+ stockInventoryService.subtractStockInventory(stockInventoryDto);
+ log.info("浜у搧棰嗙敤瀹℃壒閫氳繃锛屾墸鍑忓簱瀛樻垚鍔燂紝棰嗙敤鍗曞彿锛歿}锛屾暟閲忥細{}", borrow.getBorrowNo(), borrow.getBorrowQuantity());
+ }
+
+ @Override
+ public ProductBorrowDto getDetailById(Long id) {
+ ProductBorrowDto dto = productBorrowMapper.selectDetailById(id);
+ if (dto != null) {
+ fillStatusName(dto);
+ // 璁$畻鍓╀綑鍙綊杩樻暟閲�
+ if (dto.getBorrowQuantity() != null && dto.getReturnedQuantity() != null) {
+ dto.setRemainingQuantity(dto.getBorrowQuantity().subtract(dto.getReturnedQuantity()));
+ }
+ }
+ return dto;
+ }
+
+ /**
+ * 鐢熸垚棰嗙敤鍗曞彿
+ * 鏍煎紡锛歀Y + 骞存湀鏃� + 4浣嶅簭鍙�
+ */
+ private String generateBorrowNo() {
+ String dateStr = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
+ LambdaQueryWrapper<ProductBorrow> wrapper = new LambdaQueryWrapper<>();
+ wrapper.likeRight(ProductBorrow::getBorrowNo, "LY" + dateStr)
+ .orderByDesc(ProductBorrow::getBorrowNo)
+ .last("LIMIT 1");
+ ProductBorrow lastBorrow = getOne(wrapper);
+
+ int sequence = 1;
+ if (lastBorrow != null && lastBorrow.getBorrowNo() != null) {
+ String lastNo = lastBorrow.getBorrowNo();
+ if (lastNo.length() >= 14) {
+ try {
+ sequence = Integer.parseInt(lastNo.substring(10)) + 1;
+ } catch (NumberFormatException ignored) {
+ }
+ }
+ }
+ return "LY" + dateStr + String.format("%04d", sequence);
+ }
+
+ /**
+ * 濉厖鐘舵�佸悕绉�
+ */
+ private void fillStatusName(ProductBorrowDto dto) {
+ // 瀹℃壒鐘舵�佸悕绉�
+ if (dto.getApprovalStatus() != null) {
+ switch (dto.getApprovalStatus()) {
+ case 0: dto.setApprovalStatusName("寰呭鎵�"); break;
+ case 1: dto.setApprovalStatusName("宸查�氳繃"); break;
+ case 2: dto.setApprovalStatusName("宸查┏鍥�"); break;
+ }
+ }
+ // 褰掕繕鐘舵�佸悕绉�
+ if (dto.getStatus() != null) {
+ switch (dto.getStatus()) {
+ case 0: dto.setStatusName("鏈綊杩�"); break;
+ case 1: dto.setStatusName("閮ㄥ垎褰掕繕"); break;
+ case 2: dto.setStatusName("宸插叏閮ㄥ綊杩�"); break;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java b/src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java
index c82ac34..857fdc6 100644
--- a/src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java
+++ b/src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java
@@ -92,6 +92,11 @@
stockInRecordDto.setBatchNo(stockInventoryDto.getBatchNo());
stockInRecordDto.setProductModelId(stockInventoryDto.getProductModelId());
stockInRecordDto.setType("0");
+ stockInRecordDto.setRemark(stockInventoryDto.getRemark());
+ // 濡傛灉DTO涓寚瀹氫簡瀹℃壒鐘舵�侊紝鍒欎娇鐢紱鍚﹀垯榛樿寰呭鎵�
+ if (stockInventoryDto.getApprovalStatus() != null) {
+ stockInRecordDto.setApprovalStatus(stockInventoryDto.getApprovalStatus());
+ }
stockInRecordService.add(stockInRecordDto);
//鍐嶈繘琛屾柊澧炲簱瀛樻暟閲忓簱瀛�
//鍏堟煡璇㈠簱瀛樿〃涓殑浜у搧鏄惁瀛樺湪锛屼笉瀛樺湪鏂板锛屽瓨鍦ㄦ洿鏂�
@@ -132,6 +137,11 @@
stockOutRecordDto.setBatchNo(stockInventoryDto.getBatchNo());
stockOutRecordDto.setProductModelId(stockInventoryDto.getProductModelId());
stockOutRecordDto.setType("0");
+ stockOutRecordDto.setRemark(stockInventoryDto.getRemark());
+ // 濡傛灉DTO涓寚瀹氫簡瀹℃壒鐘舵�侊紝鍒欎娇鐢紱鍚﹀垯榛樿寰呭鎵�
+ if (stockInventoryDto.getApprovalStatus() != null) {
+ stockOutRecordDto.setApprovalStatus(stockInventoryDto.getApprovalStatus());
+ }
stockOutRecordService.add(stockOutRecordDto);
@@ -440,4 +450,9 @@
public IPage<StockInventoryDto> getBatchNoQty(Page page, StockInventoryDto stockInventoryDto) {
return stockInventoryMapper.getBatchNoQty(page, stockInventoryDto);
}
+
+ @Override
+ public IPage<StockInventoryDto> pageStockAndBorrow(Page page, StockInventoryDto stockInventoryDto) {
+ return stockInventoryMapper.pageStockAndBorrow(page, stockInventoryDto);
+ }
}
diff --git a/src/main/resources/mapper/stock/ProductBorrowMapper.xml b/src/main/resources/mapper/stock/ProductBorrowMapper.xml
new file mode 100644
index 0000000..0aebc8a
--- /dev/null
+++ b/src/main/resources/mapper/stock/ProductBorrowMapper.xml
@@ -0,0 +1,95 @@
+<?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.stock.mapper.ProductBorrowMapper">
+
+ <resultMap id="ProductBorrowDtoMap" type="com.ruoyi.stock.dto.ProductBorrowDto">
+ <id column="id" property="id"/>
+ <result column="borrow_no" property="borrowNo"/>
+ <result column="product_model_id" property="productModelId"/>
+ <result column="batch_no" property="batchNo"/>
+ <result column="borrow_quantity" property="borrowQuantity"/>
+ <result column="returned_quantity" property="returnedQuantity"/>
+ <result column="borrower_id" property="borrowerId"/>
+ <result column="borrower_name" property="borrowerName"/>
+ <result column="borrow_time" property="borrowTime"/>
+ <result column="expected_return_time" property="expectedReturnTime"/>
+ <result column="approval_status" property="approvalStatus"/>
+ <result column="status" property="status"/>
+ <result column="remark" property="remark"/>
+ <result column="tenant_id" property="tenantId"/>
+ <result column="dept_id" property="deptId"/>
+ <result column="create_user" property="createUser"/>
+ <result column="create_time" property="createTime"/>
+ <result column="update_user" property="updateUser"/>
+ <result column="update_time" property="updateTime"/>
+ <result column="product_name" property="productName"/>
+ <result column="model" property="model"/>
+ <result column="product_code" property="productCode"/>
+ <result column="unit" property="unit"/>
+ </resultMap>
+
+ <select id="listPage" resultMap="ProductBorrowDtoMap">
+ WITH RECURSIVE product_tree AS (
+ SELECT id
+ FROM product
+ WHERE id = #{ew.topParentProductId}
+ UNION ALL
+ SELECT p.id
+ FROM product p
+ INNER JOIN product_tree pt ON p.parent_id = pt.id
+ )
+ SELECT
+ pb.*,
+ p.product_name,
+ pm.model,
+ pm.product_code,
+ pm.unit
+ FROM product_borrow pb
+ LEFT JOIN product_model pm ON pb.product_model_id = pm.id
+ LEFT JOIN product p ON pm.product_id = p.id
+ <where>
+ <if test="ew.borrowNo != null and ew.borrowNo != ''">
+ AND pb.borrow_no LIKE CONCAT('%', #{ew.borrowNo}, '%')
+ </if>
+ <if test="ew.productModelId != null">
+ AND pb.product_model_id = #{ew.productModelId}
+ </if>
+ <if test="ew.borrowerId != null">
+ AND pb.borrower_id = #{ew.borrowerId}
+ </if>
+ <if test="ew.borrowerName != null and ew.borrowerName != ''">
+ AND pb.borrower_name LIKE CONCAT('%', #{ew.borrowerName}, '%')
+ </if>
+ <if test="ew.approvalStatus != null">
+ AND pb.approval_status = #{ew.approvalStatus}
+ </if>
+ <if test="ew.status != null">
+ AND pb.status = #{ew.status}
+ </if>
+ <if test="ew.deptId != null">
+ AND pb.dept_id = #{ew.deptId}
+ </if>
+ <if test="ew.topParentProductId != null and ew.topParentProductId > 0">
+ AND p.id IN (SELECT id FROM product_tree)
+ </if>
+ <if test="ew.model != null and ew.model != ''">
+ AND pm.model LIKE CONCAT('%', #{ew.model}, '%')
+ </if>
+ </where>
+ ORDER BY pb.create_time DESC
+ </select>
+
+ <select id="selectDetailById" resultMap="ProductBorrowDtoMap">
+ SELECT
+ pb.*,
+ p.product_name,
+ pm.model,
+ pm.product_code,
+ pm.unit
+ FROM product_borrow pb
+ LEFT JOIN product_model pm ON pb.product_model_id = pm.id
+ LEFT JOIN product p ON pm.product_id = p.id
+ WHERE pb.id = #{id}
+ </select>
+
+</mapper>
diff --git a/src/main/resources/mapper/stock/ProductBorrowReturnMapper.xml b/src/main/resources/mapper/stock/ProductBorrowReturnMapper.xml
new file mode 100644
index 0000000..b0508a3
--- /dev/null
+++ b/src/main/resources/mapper/stock/ProductBorrowReturnMapper.xml
@@ -0,0 +1,80 @@
+<?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.stock.mapper.ProductBorrowReturnMapper">
+
+ <resultMap id="ProductBorrowReturnDtoMap" type="com.ruoyi.stock.dto.ProductBorrowReturnDto">
+ <id column="id" property="id"/>
+ <result column="borrow_id" property="borrowId"/>
+ <result column="product_model_id" property="productModelId"/>
+ <result column="batch_no" property="batchNo"/>
+ <result column="return_quantity" property="returnQuantity"/>
+ <result column="returner_id" property="returnerId"/>
+ <result column="returner_name" property="returnerName"/>
+ <result column="return_time" property="returnTime"/>
+ <result column="remark" property="remark"/>
+ <result column="tenant_id" property="tenantId"/>
+ <result column="dept_id" property="deptId"/>
+ <result column="create_user" property="createUser"/>
+ <result column="create_time" property="createTime"/>
+ <result column="borrow_no" property="borrowNo"/>
+ <result column="product_name" property="productName"/>
+ <result column="model" property="model"/>
+ <result column="product_code" property="productCode"/>
+ <result column="unit" property="unit"/>
+ <result column="borrow_quantity" property="borrowQuantity"/>
+ <result column="borrower_name" property="borrowerName"/>
+ </resultMap>
+
+ <select id="listPage" resultMap="ProductBorrowReturnDtoMap">
+ SELECT
+ pbr.*,
+ pb.borrow_no,
+ pb.borrow_quantity,
+ pb.borrower_name,
+ p.product_name,
+ pm.model,
+ pm.product_code,
+ pm.unit
+ FROM product_borrow_return pbr
+ LEFT JOIN product_borrow pb ON pbr.borrow_id = pb.id
+ LEFT JOIN product_model pm ON pbr.product_model_id = pm.id
+ LEFT JOIN product p ON pm.product_id = p.id
+ <where>
+ <if test="ew.borrowId != null">
+ AND pbr.borrow_id = #{ew.borrowId}
+ </if>
+ <if test="ew.productModelId != null">
+ AND pbr.product_model_id = #{ew.productModelId}
+ </if>
+ <if test="ew.returnerId != null">
+ AND pbr.returner_id = #{ew.returnerId}
+ </if>
+ <if test="ew.returnerName != null and ew.returnerName != ''">
+ AND pbr.returner_name LIKE CONCAT('%', #{ew.returnerName}, '%')
+ </if>
+ <if test="ew.deptId != null">
+ AND pbr.dept_id = #{ew.deptId}
+ </if>
+ </where>
+ ORDER BY pbr.create_time DESC
+ </select>
+
+ <select id="listByBorrowId" resultMap="ProductBorrowReturnDtoMap">
+ SELECT
+ pbr.*,
+ pb.borrow_no,
+ pb.borrow_quantity,
+ pb.borrower_name,
+ p.product_name,
+ pm.model,
+ pm.product_code,
+ pm.unit
+ FROM product_borrow_return pbr
+ LEFT JOIN product_borrow pb ON pbr.borrow_id = pb.id
+ LEFT JOIN product_model pm ON pbr.product_model_id = pm.id
+ LEFT JOIN product p ON pm.product_id = p.id
+ WHERE pbr.borrow_id = #{borrowId}
+ ORDER BY pbr.create_time DESC
+ </select>
+
+</mapper>
diff --git a/src/main/resources/mapper/stock/StockInventoryMapper.xml b/src/main/resources/mapper/stock/StockInventoryMapper.xml
index 6ca5bf7..99365a0 100644
--- a/src/main/resources/mapper/stock/StockInventoryMapper.xml
+++ b/src/main/resources/mapper/stock/StockInventoryMapper.xml
@@ -671,4 +671,58 @@
batch_no
</select>
+ <!-- 鍒嗛〉鏌ヨ浜у搧搴撳瓨鍜岄鐢ㄩ噺锛堟寜鎵瑰彿鍖哄垎锛� -->
+ <select id="pageStockAndBorrow" resultType="com.ruoyi.stock.dto.StockInventoryDto">
+ WITH RECURSIVE product_tree AS (
+ SELECT id
+ FROM product
+ WHERE id = #{ew.topParentProductId}
+ UNION ALL
+ SELECT p.id
+ FROM product p
+ INNER JOIN product_tree pt ON p.parent_id = pt.id
+ )
+ SELECT
+ pm.id as product_model_id,
+ pm.model,
+ pm.product_code,
+ pm.unit,
+ p.product_name,
+ p.id as product_id,
+ si.batch_no,
+ IFNULL(si.qualitity, 0) as qualitity,
+ IFNULL(si.locked_quantity, 0) as locked_quantity,
+ IFNULL(borrowed.borrowed_quantity, 0) as borrowed_quantity,
+ IFNULL(si.qualitity, 0) - IFNULL(borrowed.borrowed_quantity, 0) as available_quantity
+ FROM product_model pm
+ LEFT JOIN product p ON pm.product_id = p.id
+ LEFT JOIN stock_inventory si ON pm.id = si.product_model_id
+ LEFT JOIN (
+ SELECT
+ product_model_id,
+ batch_no,
+ SUM(borrow_quantity - IFNULL(returned_quantity, 0)) as borrowed_quantity
+ FROM product_borrow
+ WHERE approval_status = 1
+ AND status != 2
+ GROUP BY product_model_id, batch_no
+ ) borrowed ON pm.id = borrowed.product_model_id
+ AND (si.batch_no = borrowed.batch_no OR (si.batch_no IS NULL AND borrowed.batch_no IS NULL))
+ <where>
+ <if test="ew.topParentProductId != null and ew.topParentProductId > 0">
+ AND p.id IN (SELECT id FROM product_tree)
+ </if>
+ <if test="ew.productName != null and ew.productName != ''">
+ AND p.product_name LIKE CONCAT('%', #{ew.productName}, '%')
+ </if>
+ <if test="ew.model != null and ew.model != ''">
+ AND pm.model LIKE CONCAT('%', #{ew.model}, '%')
+ </if>
+ <if test="ew.batchNo != null and ew.batchNo != ''">
+ AND si.batch_no LIKE CONCAT('%', #{ew.batchNo}, '%')
+ </if>
+ </where>
+ ORDER BY pm.id DESC, si.batch_no
+ </select>
+
</mapper>
--
Gitblit v1.9.3