From ee359b8abc3708e9ed4796cede561685a54aafba Mon Sep 17 00:00:00 2001
From: liyong <18434998025@163.com>
Date: 星期二, 27 一月 2026 15:50:56 +0800
Subject: [PATCH] refactor(stock): 库存代码迁移

---
 src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java           |   80 -
 src/main/java/com/ruoyi/stock/pojo/StockInventory.java                                |   66 +
 src/main/java/com/ruoyi/stock/controller/StockUninventoryController.java              |   73 +
 src/main/java/com/ruoyi/stock/dto/StockOutRecordDto.java                              |   28 
 src/main/java/com/ruoyi/stock/service/StockInventoryService.java                      |   41 
 src/main/java/com/ruoyi/stock/mapper/StockOutRecordMapper.java                        |   28 
 src/main/java/com/ruoyi/stock/execl/StockInRecordExportData.java                      |   32 
 src/main/java/com/ruoyi/sales/pojo/SalesLedgerProduct.java                            |    4 
 src/main/java/com/ruoyi/stock/pojo/StockOutRecord.java                                |   74 +
 src/main/java/com/ruoyi/common/enums/StockUnQualifiedRecordTypeEnum.java              |   25 
 src/main/java/com/ruoyi/procurementrecord/utils/StockUtils.java                       |   84 +
 src/main/java/com/ruoyi/stock/service/StockInRecordService.java                       |   22 
 src/main/java/com/ruoyi/stock/service/impl/StockInRecordServiceImpl.java              |  113 ++
 src/main/java/com/ruoyi/stock/controller/StockInRecordController.java                 |   51 +
 src/main/resources/mapper/sales/SalesLedgerProductMapper.xml                          |   17 
 src/main/java/com/ruoyi/stock/controller/StockOutRecordController.java                |   69 +
 src/main/java/com/ruoyi/stock/mapper/StockInRecordMapper.java                         |   17 
 src/main/resources/mapper/stock/StockUninventoryMapper.xml                            |   81 +
 src/main/java/com/ruoyi/basic/controller/EnumController.java                          |   36 
 src/main/java/com/ruoyi/sales/controller/SalesLedgerProductController.java            |   16 
 src/main/java/com/ruoyi/stock/service/StockUninventoryService.java                    |   33 
 src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java             |  197 ++++
 src/main/java/com/ruoyi/stock/dto/StockInventoryDto.java                              |   42 
 src/main/java/com/ruoyi/stock/pojo/StockInRecord.java                                 |   65 +
 src/main/resources/mapper/stock/StockOutRecordMapper.xml                              |   75 +
 src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java |   27 
 src/main/resources/mapper/stock/StockInventoryMapper.xml                              |  179 ++++
 src/main/java/com/ruoyi/stock/dto/StockUninventoryDto.java                            |   22 
 src/main/java/com/ruoyi/stock/service/impl/StockOutRecordServiceImpl.java             |  116 ++
 src/main/java/com/ruoyi/stock/mapper/StockUninventoryMapper.java                      |   32 
 src/main/resources/mapper/stock/StockInRecordMapper.xml                               |   59 +
 src/main/java/com/ruoyi/basic/dto/SelectOptionDTO.java                                |   14 
 src/main/java/com/ruoyi/stock/controller/StockInventoryController.java                |  107 ++
 src/main/java/com/ruoyi/common/config/IgnoreTableConfig.java                          |    4 
 src/main/java/com/ruoyi/sales/controller/ShipmentApprovalController.java              |   16 
 src/main/java/com/ruoyi/stock/dto/StockInRecordDto.java                               |   28 
 src/main/java/com/ruoyi/stock/execl/StockOutRecordExportData.java                     |   31 
 src/main/java/com/ruoyi/stock/service/StockOutRecordService.java                      |   30 
 src/main/java/com/ruoyi/stock/execl/StockInventoryExportData.java                     |   33 
 src/main/java/com/ruoyi/basic/utils/EnumUtils.java                                    |   45 +
 src/main/java/com/ruoyi/stock/mapper/StockInventoryMapper.java                        |   38 
 src/main/java/com/ruoyi/stock/service/impl/StockUninventoryServiceImpl.java           |  126 ++
 src/main/java/com/ruoyi/common/utils/EnumUtil.java                                    |  106 ++
 src/main/java/com/ruoyi/common/enums/StockQualifiedRecordTypeEnum.java                |   26 
 src/main/java/com/ruoyi/common/enums/BaseEnum.java                                    |   58 +
 src/main/java/com/ruoyi/sales/mapper/SalesLedgerProductMapper.java                    |    3 
 src/main/java/com/ruoyi/stock/pojo/StockUninventory.java                              |   62 +
 47 files changed, 2,414 insertions(+), 117 deletions(-)

diff --git a/src/main/java/com/ruoyi/basic/controller/EnumController.java b/src/main/java/com/ruoyi/basic/controller/EnumController.java
new file mode 100644
index 0000000..b02326a
--- /dev/null
+++ b/src/main/java/com/ruoyi/basic/controller/EnumController.java
@@ -0,0 +1,36 @@
+package com.ruoyi.basic.controller;
+
+import com.ruoyi.basic.utils.EnumUtils;
+import com.ruoyi.common.enums.StockQualifiedRecordTypeEnum;
+import com.ruoyi.common.utils.EnumUtil;
+import com.ruoyi.framework.aspectj.lang.annotation.Anonymous;
+import com.ruoyi.framework.web.domain.R;
+import io.swagger.annotations.Api;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+import java.util.Map;
+
+@RestController
+@Api(tags = "鏋氫妇鎺ュ彛")
+@RequestMapping("/basic/enum")
+public class EnumController {
+
+
+    //鑾峰彇搴撳瓨鏋氫妇
+    @GetMapping("/stockRecordType")
+    @Anonymous
+    public R  getStockRecordTypeEnum(){
+        List<Map<String, Object>> list = EnumUtil.toList(StockQualifiedRecordTypeEnum.class);
+        return R.ok(list);
+    }
+
+    @GetMapping("/{className}")
+    @Anonymous
+    public R test(@PathVariable String className){
+        return R.ok(EnumUtils.getOptions("com.ruoyi.common.enums." + className));
+    }
+}
diff --git a/src/main/java/com/ruoyi/basic/dto/SelectOptionDTO.java b/src/main/java/com/ruoyi/basic/dto/SelectOptionDTO.java
new file mode 100644
index 0000000..4323126
--- /dev/null
+++ b/src/main/java/com/ruoyi/basic/dto/SelectOptionDTO.java
@@ -0,0 +1,14 @@
+package com.ruoyi.basic.dto;
+
+import lombok.Data;
+
+@Data
+public class SelectOptionDTO<T> {
+    private T value;
+    private String label;
+
+    public SelectOptionDTO(T code, String value) {
+        this.value = code;
+        this.label = value;
+    }
+}
diff --git a/src/main/java/com/ruoyi/basic/utils/EnumUtils.java b/src/main/java/com/ruoyi/basic/utils/EnumUtils.java
new file mode 100644
index 0000000..5103ac5
--- /dev/null
+++ b/src/main/java/com/ruoyi/basic/utils/EnumUtils.java
@@ -0,0 +1,45 @@
+package com.ruoyi.basic.utils;
+
+import com.ruoyi.basic.dto.SelectOptionDTO;
+import com.ruoyi.common.enums.BaseEnum;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class EnumUtils {
+    public static List<SelectOptionDTO<?>> getOptions(String className) {
+        try {
+            Class<?> clazz = Class.forName(className);
+            return getOptionsByClass(clazz);
+        } catch (ClassNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private static List<SelectOptionDTO<?>> getOptionsByClass(Class<?> clazz) {
+        if (!clazz.isEnum()) {
+            throw new RuntimeException("涓嶆槸鏋氫妇锛�" + clazz.getName());
+        }
+        if (!BaseEnum.class.isAssignableFrom(clazz)) {
+            throw new RuntimeException("鏈疄鐜� BaseEnum锛�" + clazz.getName());
+        }
+        return (List<SelectOptionDTO<?>>) getOptionsInternal((Class) clazz);
+    }
+
+    private static <T> List<SelectOptionDTO<T>> getOptionsInternal(
+            Class<? extends BaseEnum<T>> clazz) {
+
+        Enum<?>[] enums = (Enum<?>[]) clazz.getEnumConstants();
+        List<SelectOptionDTO<T>> list = new ArrayList<>();
+
+        for (Enum<?> e : enums) {
+            BaseEnum<T> option = (BaseEnum<T>) e;
+            list.add(new SelectOptionDTO<>(
+                    option.getCode(),
+                    option.getValue()
+            ));
+        }
+        return list;
+    }
+}
diff --git a/src/main/java/com/ruoyi/common/config/IgnoreTableConfig.java b/src/main/java/com/ruoyi/common/config/IgnoreTableConfig.java
index 66f674a..dc7289f 100644
--- a/src/main/java/com/ruoyi/common/config/IgnoreTableConfig.java
+++ b/src/main/java/com/ruoyi/common/config/IgnoreTableConfig.java
@@ -35,5 +35,9 @@
         IGNORE_TABLES.add("sys_job_log");
         IGNORE_TABLES.add("gen_table");
         IGNORE_TABLES.add("gen_table_column");
+        IGNORE_TABLES.add("stock_in_record");
+        IGNORE_TABLES.add("stock_inventory");
+        IGNORE_TABLES.add("stock_out_record");
+        IGNORE_TABLES.add("stock_uninventory");
     }
 }
diff --git a/src/main/java/com/ruoyi/common/enums/BaseEnum.java b/src/main/java/com/ruoyi/common/enums/BaseEnum.java
new file mode 100644
index 0000000..cf84dde
--- /dev/null
+++ b/src/main/java/com/ruoyi/common/enums/BaseEnum.java
@@ -0,0 +1,58 @@
+package com.ruoyi.common.enums;
+
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+public interface BaseEnum<T> {
+    @JsonValue
+    T getCode();
+    
+    String getValue();
+
+    /**
+     * 閫氱敤闈欐�佸伐鍏锋柟娉曪紙鍙鎵�鏈夋灇涓捐皟鐢級
+     */
+    /**
+     * 閫氱敤闈欐�佸伐鍏锋柟娉曪細鏀寔浠� Integer 鎴� String 绫诲瀷鐨� Code 杩涜鍙嶅簭鍒楀寲
+     */
+    @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
+    static <E extends Enum<E> & BaseEnum<?>> E fromCode(Class<E> enumClass, Object code) {
+        if (code == null) {
+            return null;
+        }
+
+        // 鐩爣 Code 鐨勬暣鏁板��
+        Integer targetCode = null;
+
+        if (code instanceof Integer) {
+            // 1. 濡傛灉浼犲叆鐨勬槸鏁板瓧 (Integer)
+            targetCode = (Integer) code;
+        } else if (code instanceof String) {
+            // 2. 濡傛灉浼犲叆鐨勬槸瀛楃涓� ("1")
+            try {
+                // 灏濊瘯灏嗗瓧绗︿覆杞崲涓烘暣鏁�
+                targetCode = Integer.valueOf((String) code);
+            } catch (NumberFormatException e) {
+                // 濡傛灉瀛楃涓蹭笉鏄湁鏁堢殑鏁板瓧锛堜緥濡� "Unknown"锛夛紝鍒� targetCode 淇濇寔涓� null
+                // 鎮ㄤ篃鍙互鍦ㄨ繖閲岃褰曟棩蹇楁垨鎵ц鍏朵粬閿欒澶勭悊
+                // System.err.println("鏃犳硶灏嗗瓧绗︿覆 Code 杞崲涓烘暟瀛�: " + code);
+                return null; // 鎴栬�呭湪鎵句笉鍒板尮閰嶇殑鎯呭喌涓嬭繑鍥� null
+            }
+        }
+        // else if (code instanceof Long) { ... 鎮ㄤ篃鍙互娣诲姞瀵� Long 绫诲瀷鐨勬敮鎸� }
+
+        if (targetCode == null) {
+            return null;
+        }
+
+        // 浣跨敤鑾峰彇鍒扮殑鏁存暟鍊艰繘琛屾煡鎵�
+        for (E e : enumClass.getEnumConstants()) {
+            if (e.getCode().equals(targetCode)) {
+                return e;
+            }
+        }
+        return null;
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/ruoyi/common/enums/StockQualifiedRecordTypeEnum.java b/src/main/java/com/ruoyi/common/enums/StockQualifiedRecordTypeEnum.java
new file mode 100644
index 0000000..50ef0c7
--- /dev/null
+++ b/src/main/java/com/ruoyi/common/enums/StockQualifiedRecordTypeEnum.java
@@ -0,0 +1,26 @@
+package com.ruoyi.common.enums;
+
+import lombok.Getter;
+
+
+@Getter
+public enum StockQualifiedRecordTypeEnum implements BaseEnum<String> {
+    CUSTOMIZATION_STOCK_IN("0", "鍚堟牸鑷畾涔夊叆搴�"),
+    CUSTOMIZATION_STOCK_OUT("1", "鍚堟牸鑷畾涔夊嚭搴�"),
+    PRODUCTION_REPORT_STOCK_IN("2", "鐢熶骇鎶ュ伐-鍏ュ簱"),
+    PRODUCTION_REPORT_STOCK_OUT("3", "鐢熶骇鎶ュ伐-鍑哄簱"),
+    DEFECTIVE_PASS("6", "涓嶅悎鏍煎鐞�-璁╂鏀捐"),
+    PURCHASE_STOCK_IN("7", "閲囪喘-鍏ュ簱"),
+    SALE_STOCK_OUT("8", "閿�鍞�-鍑哄簱"),
+    QUALITYINSPECT_STOCK_IN("11", "璐ㄦ-鍚堟牸鍏ュ簱"),
+    SALE_SHIP_STOCK_OUT("13", "閿�鍞�-鍙戣揣鍑哄簱");
+
+    private final String code;
+    private final String value;
+
+    StockQualifiedRecordTypeEnum(String code, String value) {
+        this.code = code;
+        this.value = value;
+    }
+
+    }
diff --git a/src/main/java/com/ruoyi/common/enums/StockUnQualifiedRecordTypeEnum.java b/src/main/java/com/ruoyi/common/enums/StockUnQualifiedRecordTypeEnum.java
new file mode 100644
index 0000000..8a39c56
--- /dev/null
+++ b/src/main/java/com/ruoyi/common/enums/StockUnQualifiedRecordTypeEnum.java
@@ -0,0 +1,25 @@
+package com.ruoyi.common.enums;
+
+import lombok.Getter;
+
+
+@Getter
+public enum StockUnQualifiedRecordTypeEnum implements BaseEnum<String> {
+
+
+    PRODUCTION_SCRAP("4", "鐢熶骇鎶ュ伐-鎶ュ簾"),
+    DEFECTIVE_SCRAP("5", "涓嶅悎鏍煎鐞�-鎶ュ簾"),
+    CUSTOMIZATION_UNSTOCK_IN("9", "涓嶅悎鏍艰嚜瀹氫箟鍏ュ簱"),
+    CUSTOMIZATION_UNSTOCK_OUT("10", "涓嶅悎鏍艰嚜瀹氫箟鍑哄簱"),
+    QUALITYINSPECT_UNSTOCK_IN("12", "璐ㄦ-涓嶅悎鏍煎叆搴�");
+
+
+    private final String code;
+    private final String value;
+
+    StockUnQualifiedRecordTypeEnum(String code, String value) {
+        this.code = code;
+        this.value = value;
+    }
+
+}
diff --git a/src/main/java/com/ruoyi/common/utils/EnumUtil.java b/src/main/java/com/ruoyi/common/utils/EnumUtil.java
new file mode 100644
index 0000000..931fd1c
--- /dev/null
+++ b/src/main/java/com/ruoyi/common/utils/EnumUtil.java
@@ -0,0 +1,106 @@
+package com.ruoyi.common.utils;
+
+
+import com.ruoyi.common.enums.BaseEnum;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 閫氱敤鏋氫妇宸ュ叿绫�
+ * 閫傜敤浜庢墍鏈夊疄鐜� BaseEnum 鎺ュ彛鐨勬灇涓�
+ *
+ * @author Bu
+ */
+public class EnumUtil {
+
+    /**
+     * 鏍规嵁 code 鑾峰彇鏋氫妇瀹炰緥
+     */
+    public static <E extends Enum<E> & BaseEnum> E fromCode(Class<E> enumClass, int code) {
+        for (E e : enumClass.getEnumConstants()) {
+            if (Integer.parseInt(String.valueOf(e.getCode())) == code) {
+                return e;
+            }
+        }
+        throw new IllegalArgumentException("鏈煡鐨� code: " + code + " 瀵逛簬鏋氫妇 " + enumClass.getSimpleName());
+    }
+
+    public static <E extends Enum<E> & BaseEnum> E fromCodeHasNull(Class<E> enumClass, int code) {
+        for (E e : enumClass.getEnumConstants()) {
+            if (Integer.parseInt(String.valueOf(e.getCode())) == code) {
+                return e;
+            }
+        }
+        return null;
+    }
+
+    public static <E extends Enum<E> & BaseEnum> E fromCodeHasDefault(Class<E> enumClass, int code,E defaultE) {
+        for (E e : enumClass.getEnumConstants()) {
+            if (Integer.parseInt(String.valueOf(e.getCode())) == code) {
+                return e;
+            }
+        }
+        return defaultE;
+    }
+
+
+
+    /**
+     * 鏍规嵁 value 鑾峰彇鏋氫妇瀹炰緥
+     */
+    public static <E extends Enum<E> & BaseEnum> E fromValue(Class<E> enumClass, String value) {
+        for (E e : enumClass.getEnumConstants()) {
+            if (Objects.equals(e.getValue(), value)) {
+                return e;
+            }
+        }
+        throw new IllegalArgumentException("鏈煡鐨� value: " + value + " 瀵逛簬鏋氫妇 " + enumClass.getSimpleName());
+    }
+
+    /**
+     * 鑾峰彇鎵�鏈夋灇涓鹃」鍒楄〃
+     */
+    public static <E extends Enum<E> & BaseEnum> List<E> listAll(Class<E> enumClass) {
+        return Arrays.asList(enumClass.getEnumConstants());
+    }
+
+    /**
+     * 杞负 List<Map>锛堥�傚悎杩斿洖缁欏墠绔級
+     * 鏍煎紡: [{ "code": 1, "value": "缁炵嚎" }, ...]
+     */
+    public static <E extends Enum<E> & BaseEnum> List<Map<String, Object>> toList(Class<E> enumClass) {
+        return Arrays.stream(enumClass.getEnumConstants())
+                .map(e -> {
+                    Map<String, Object> map = new HashMap<>();
+                    map.put("key", e.getCode().toString());
+                    map.put("value", e.getValue());
+                    return map;
+                })
+                .collect(Collectors.toList());
+    }
+
+//    /**
+//     * 杞负 Map<Integer, String>
+//     * 鏍煎紡: { 1: "缁炵嚎", 0: "鎷変笣" }
+//     */
+//    public static <E extends Enum<E> & BaseEnum> Map<Integer, String> toMap(Class<E> enumClass) {
+//        return Arrays.stream(enumClass.getEnumConstants())
+//                .collect(Collectors.toMap((BaseEnum::getCode, BaseEnum::getValue));
+//    }
+//
+//
+//    public static void main(String[] args) {
+//        // 鉁� 1. 鏍规嵁 code 鑾峰彇鏋氫妇
+//        StockRecordTypeEnum status = EnumUtil.fromCode(StockRecordTypeEnum.class, 1);
+//        System.out.println(status.getValue()); // 缁炵嚎
+//
+//        // 鉁� 2. 杞垚 list (閫傚悎鍓嶇)
+//        System.out.println(EnumUtil.toList(StockRecordTypeEnum.class));
+//        // 杈撳嚭: [{code=1, value=缁炵嚎}, {code=0, value=鎷変笣}]
+//
+//        // 鉁� 3. 杞垚 map
+//        System.out.println(EnumUtil.toMap(StockRecordTypeEnum.class));
+//        // 杈撳嚭: {1=缁炵嚎, 0=鎷変笣}
+//    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/ruoyi/procurementrecord/utils/StockUtils.java b/src/main/java/com/ruoyi/procurementrecord/utils/StockUtils.java
index 0d61f87..3ced2d4 100644
--- a/src/main/java/com/ruoyi/procurementrecord/utils/StockUtils.java
+++ b/src/main/java/com/ruoyi/procurementrecord/utils/StockUtils.java
@@ -1,11 +1,19 @@
 package com.ruoyi.procurementrecord.utils;
 
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.ruoyi.procurementrecord.mapper.ProcurementRecordMapper;
 import com.ruoyi.procurementrecord.mapper.ProcurementRecordOutMapper;
+import com.ruoyi.stock.dto.StockInventoryDto;
+import com.ruoyi.stock.dto.StockUninventoryDto;
+import com.ruoyi.stock.pojo.StockInRecord;
+import com.ruoyi.stock.service.StockInRecordService;
+import com.ruoyi.stock.service.StockInventoryService;
+import com.ruoyi.stock.service.StockUninventoryService;
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Component;
 
 import java.math.BigDecimal;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -14,6 +22,9 @@
 public class StockUtils {
     private final ProcurementRecordOutMapper procurementRecordOutMapper;
     private final ProcurementRecordMapper procurementRecordMapper;
+    private final StockUninventoryService stockUninventoryService;
+    private final StockInventoryService stockInventoryService;
+    private final StockInRecordService stockInRecordService;
 
     // 鑾峰彇鍟嗗搧鍏ュ簱鏁伴噺,鍑哄簱鏁伴噺,鍓╀綑搴撳瓨
     public Map<String, BigDecimal> getStockQuantity(Long productModelId) {
@@ -29,4 +40,77 @@
         stockMap.put("stockQuantity", stockQuantity);
         return stockMap;
     }
+
+    /**
+     * 涓嶅悎鏍煎叆搴�
+     * @param productModelId
+     * @param quantity
+     * @param recordType
+     * @param recordId
+     */
+    public void addUnStock(Long productModelId, BigDecimal quantity, String recordType,Long recordId) {
+        StockUninventoryDto stockUninventoryDto = new StockUninventoryDto();
+        stockUninventoryDto.setRecordId(recordId);
+        stockUninventoryDto.setRecordType(String.valueOf(recordType));
+        stockUninventoryDto.setQualitity(quantity);
+        stockUninventoryDto.setProductModelId(productModelId);
+        stockUninventoryService.addStockUninventory(stockUninventoryDto);
+    }
+
+    /**
+     * 涓嶅悎鏍煎嚭搴�
+     * @param productModelId
+     * @param quantity
+     * @param recordType
+     * @param recordId
+     */
+    public void subtractUnStock(Long productModelId, BigDecimal quantity, Integer recordType,Long recordId) {
+        StockUninventoryDto stockUninventoryDto = new StockUninventoryDto();
+        stockUninventoryDto.setRecordId(recordId);
+        stockUninventoryDto.setRecordType(String.valueOf(recordType));
+        stockUninventoryDto.setQualitity(quantity);
+        stockUninventoryDto.setProductModelId(productModelId);
+        stockUninventoryService.subtractStockUninventory(stockUninventoryDto);
+    }
+
+    /**
+     * 鍚堟牸鍏ュ簱
+     * @param productModelId
+     * @param quantity
+     * @param recordType
+     * @param recordId
+     */
+    public void addStock(Long productModelId, BigDecimal quantity, String recordType,Long recordId) {
+        StockInventoryDto stockInventoryDto = new StockInventoryDto();
+        stockInventoryDto.setRecordId(recordId);
+        stockInventoryDto.setRecordType(String.valueOf(recordType));
+        stockInventoryDto.setQualitity(quantity);
+        stockInventoryDto.setProductModelId(productModelId);
+        stockInventoryService.addstockInventory(stockInventoryDto);
+    }
+
+    /**
+     * 鍚堟牸鍑哄簱
+     * @param productModelId
+     * @param quantity
+     * @param recordType
+     * @param recordId
+     */
+    public void substractStock(Long productModelId, BigDecimal quantity, String recordType,Long recordId) {
+        StockInventoryDto stockInventoryDto = new StockInventoryDto();
+        stockInventoryDto.setRecordId(recordId);
+        stockInventoryDto.setRecordType(String.valueOf(recordType));
+        stockInventoryDto.setQualitity(quantity);
+        stockInventoryDto.setProductModelId(productModelId);
+        stockInventoryService.subtractStockInventory(stockInventoryDto);
+    }
+
+    //涓嶅悎鏍煎簱瀛樺垹闄�
+    public void deleteStockRecord(Long recordId, String recordType) {
+        StockInRecord one = stockInRecordService.getOne(new QueryWrapper<StockInRecord>()
+                .lambda().eq(StockInRecord::getRecordId, recordId)
+                .eq(StockInRecord::getRecordType, recordType));
+
+        stockInRecordService.batchDelete(Collections.singletonList(one.getId()));
+    }
 }
diff --git a/src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java b/src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java
index 5ca8a86..d99bcca 100644
--- a/src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java
+++ b/src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java
@@ -13,6 +13,7 @@
 import com.ruoyi.basic.mapper.ProductModelMapper;
 import com.ruoyi.basic.pojo.Product;
 import com.ruoyi.basic.pojo.ProductModel;
+import com.ruoyi.common.enums.StockQualifiedRecordTypeEnum;
 import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.common.utils.bean.BeanUtils;
 import com.ruoyi.procurementrecord.mapper.ProcurementRecordOutMapper;
@@ -158,33 +159,14 @@
         for (ProductStructureDto productStructureDto : productStructureDtos) {
             ProductModel productModel1 = productModelMapper.selectById(productStructureDto.getProductModelId());
             Product product = productMapper.selectById(productModel1.getProductId());
-            BigDecimal stockQuantity = stockUtils.getStockQuantity(productModel1.getId()).get("stockQuantity");
-            if (!(stockQuantity.compareTo(BigDecimal.ZERO) > 0)) {
-                throw new RuntimeException(product.getProductName()+"浜у搧鐨�"+productModel1.getModel() + "鐨勮鏍煎簱瀛樹负0");
-            }
-            if (stockQuantity.compareTo(productStructureDto.getUnitQuantity().multiply(dto.getQuantity())) < 0) {
-                throw new RuntimeException(product.getProductName()+"浜у搧鐨�"+productModel1.getModel() + "鐨勮鏍煎簱瀛樹笉瓒�");
-            }
+
             ProductionProductInput productionProductInput = new ProductionProductInput();
             productionProductInput.setProductModelId(productStructureDto.getProductModelId());
             productionProductInput.setQuantity(productStructureDto.getUnitQuantity().multiply(dto.getQuantity()));
             productionProductInput.setProductMainId(productionProductMain.getId());
             productionProductInputMapper.insert(productionProductInput);
             //瀵瑰簲鐨勫簱瀛樺嚭搴�
-            DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyyMMdd");
-            LocalDate now = LocalDate.now();
-            ProcurementRecordOut procurementRecordOut1 = procurementRecordOutMapper.selectCode(dateFormat.format(now));
-            Long aLong = procurementRecordOut1 == null ? 1L : Long.valueOf(procurementRecordOut1.getCode().split("LS" + dateFormat.format(now))[1]);
-            ProcurementRecordOut.ProcurementRecordOutBuilder procurementRecordOut = ProcurementRecordOut.builder()
-                    .procurementRecordStorageId(0)
-                    .code("LS" + dateFormat.format(now) + String.format("%03d", aLong + 1))
-                    .salesLedgerProductId(productionProductMain.getId())//鍏宠仈鎶ュ伐浜у嚭
-                    .inboundBatches(aLong.equals(0L) ? "绗�1鎵规" : "绗�" + (aLong + 1) + "鎵规")
-                    .inboundNum(productionProductInput.getQuantity())
-                    .type(4)
-                    .createBy(user.getNickName())
-                    .productModelId(productModel1.getId());
-            procurementRecordOutMapper.insert(procurementRecordOut.build());
+            stockUtils.substractStock(productStructureDto.getProductModelId(), productStructureDto.getUnitQuantity().multiply(dto.getQuantity()), StockQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_OUT.getCode(), productionProductMain.getId());
         }
         /*鏂板鎶ュ伐浜у嚭琛�*/
         ProductionProductOutput productionProductOutput = new ProductionProductOutput();
@@ -293,6 +275,7 @@
             productOrderMapper.updateById(productOrder);
         }
         /*鍒犻櫎浜у嚭*/
+
         //鍒犻櫎璐ㄦ
         qualityInspectMapper.selectList(
                 new LambdaQueryWrapper<QualityInspect>()
@@ -309,6 +292,8 @@
         /*鍒犻櫎鎶曞叆*/
         procurementRecordOutMapper.delete(new LambdaQueryWrapper<ProcurementRecordOut>()
                 .eq(ProcurementRecordOut::getSalesLedgerProductId, productionProductMain.getId()));
+        //鍒犻櫎鍑哄簱璁板綍
+        stockUtils.deleteStockRecord(productionProductMain.getId(), StockQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_OUT.getCode());
         productionProductInputMapper.delete(new LambdaQueryWrapper<ProductionProductInput>()
                 .eq(ProductionProductInput::getProductMainId, productionProductMain.getId()));
         // 鍒犻櫎涓昏〃
diff --git a/src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java b/src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java
index 5be05ad..76280a7 100644
--- a/src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java
+++ b/src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java
@@ -10,6 +10,7 @@
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.deepoove.poi.XWPFTemplate;
 import com.deepoove.poi.config.Configure;
+import com.ruoyi.common.enums.StockQualifiedRecordTypeEnum;
 import com.ruoyi.common.exception.base.BaseException;
 import com.ruoyi.common.utils.HackLoopTableRenderPolicy;
 import com.ruoyi.common.utils.SecurityUtils;
@@ -19,6 +20,7 @@
 import com.ruoyi.procurementrecord.dto.ProcurementAddDto;
 import com.ruoyi.procurementrecord.pojo.ProcurementRecordStorage;
 import com.ruoyi.procurementrecord.service.ProcurementRecordService;
+import com.ruoyi.procurementrecord.utils.StockUtils;
 import com.ruoyi.quality.dto.QualityInspectDto;
 import com.ruoyi.quality.mapper.QualityInspectMapper;
 import com.ruoyi.quality.mapper.QualityInspectParamMapper;
@@ -58,6 +60,7 @@
 @Transactional(rollbackFor = Exception.class)
 public class QualityInspectServiceImpl extends ServiceImpl<QualityInspectMapper, QualityInspect> implements IQualityInspectService {
 
+    private final StockUtils stockUtils;
     private QualityInspectMapper qualityInspectMapper;
 
     private IQualityInspectParamService qualityInspectParamService;
@@ -109,83 +112,8 @@
             qualityUnqualified.setDefectivePhenomena(text + "杩欎簺鎸囨爣涓瓨鍦ㄤ笉鍚堟牸");//涓嶅悎鏍肩幇璞�
             qualityUnqualifiedMapper.insert(qualityUnqualified);
         } else {
-
-            if (qualityInspect.getInspectType() == 0) {
-                if ("鍚堟牸".equals(qualityInspect.getCheckResult())) {
-                    ProcurementAddDto procurementRecordOutAdd = new ProcurementAddDto();
-                    procurementRecordOutAdd.setType(1);
-                    procurementRecordOutAdd.setTypeName("閲囪喘鍘熸潗鏂欐楠屽悎鏍煎叆搴�");
-                    procurementRecordOutAdd.setNickName(loginUser.getNickName());
-                    procurementRecordOutAdd.setPurchaseLedgerId(Math.toIntExact(qualityInspect.getPurchaseLedgerId()));
-                    if (qualityInspect.getPurchaseLedgerId() == null) {
-                        throw new BaseException("璇烽�夋嫨閲囪喘鍗�");
-                    }
-                    SalesLedgerProduct salesLedgerProduct = salesLedgerProductMapper.selectById(qualityInspect.getPurchaseLedgerId());
-
-                    ArrayList<Details> detailss = new ArrayList<>();
-                    Details details = new Details();
-                    details.setId(Math.toIntExact(salesLedgerProduct.getId()));
-                    details.setInboundQuantity(qualityInspect.getQuantity());
-                    details.setWarnNum(salesLedgerProduct.getWarnNum());
-                    details.setUnitPrice(salesLedgerProduct.getTaxInclusiveUnitPrice());
-                    details.setTotalPrice(salesLedgerProduct.getTaxInclusiveTotalPrice());
-                    details.setProductModelId(qualityInspect.getProductModelId());
-                    detailss.add(details);
-                    procurementRecordOutAdd.setDetails(detailss);
-                    procurementRecordOutAdd.setQualityInspectId(qualityInspect.getId());
-                    procurementRecordService.add(procurementRecordOutAdd);
-                }
-            } else if (qualityInspect.getInspectType() == 1) {
-                //鏌ヨUnitPrice/TotalPrice
-                ProcurementAddDto procurementRecordOutAdd = new ProcurementAddDto();
-                procurementRecordOutAdd.setType(2);
-                procurementRecordOutAdd.setTypeName("鐢熶骇杩囩▼妫�楠屽悎鏍煎叆搴�");
-                procurementRecordOutAdd.setNickName(loginUser.getNickName());
-                List<Details> details = new ArrayList<>();
-                Details details1 = new Details();
-                details1.setInboundQuantity(qualityInspect.getQuantity());
-                details1.setProductModelId(qualityInspect.getProductModelId());
-                procurementRecordOutAdd.setDetails(details);
-
-                ProcurementRecordStorage.ProcurementRecordStorageBuilder procurementRecordBuilder = ProcurementRecordStorage.builder()
-                        .salesLedgerProductId(0)
-                        .inboundBatches("鐢熶骇鍗婃垚鍝佸叆搴�")
-                        .inboundNum(details1.getInboundQuantity())
-                        .type(2)
-                        .warnNum(new BigDecimal(0))
-                        .unitPrice(new BigDecimal(0))
-                        .totalPrice(new BigDecimal(0))
-                        .createTime(LocalDateTime.now())
-                        .createUser(loginUser.getUserId())
-                        .updateTime(LocalDateTime.now())
-                        .updateUser(loginUser.getUserId())
-                        .createBy(procurementRecordOutAdd.getNickName())
-                        .productModelId(details1.getProductModelId())
-                        .qualityInspectId(qualityInspect.getId());
-                procurementRecordService.save(procurementRecordBuilder.build());
-
-
-            } else if (qualityInspect.getInspectType() == 2) {
-                //鏌ヨUnitPrice/TotalPrice
-                SalesLedgerProduct salesLedgerProduct = salesLedgerProductMapper.selectSalesLedgerProductByMainId(qualityInspect.getProductMainId());
-                ProcurementAddDto procurementRecordOutAdd = new ProcurementAddDto();
-                procurementRecordOutAdd.setType(2);
-                procurementRecordOutAdd.setTypeName("鐢熶骇鍑哄巶妫�楠屽悎鏍煎叆搴�");
-                procurementRecordOutAdd.setNickName(loginUser.getNickName());
-                List<Details> details = new ArrayList<>();
-                Details details1 = new Details();
-                details1.setInboundQuantity(qualityInspect.getQuantity());
-                details1.setId(Math.toIntExact(salesLedgerProduct.getId()));
-                details1.setUnitPrice(salesLedgerProduct.getTaxInclusiveUnitPrice());
-                details1.setTotalPrice(salesLedgerProduct.getTaxInclusiveTotalPrice());
-                details1.setProductModelId(salesLedgerProduct.getProductModelId());
-                details.add(details1);
-                procurementRecordOutAdd.setDetails(details);
-                procurementRecordOutAdd.setQualityInspectId(qualityInspect.getId());
-                procurementRecordService.add(procurementRecordOutAdd);
-            }
+           stockUtils.addStock(inspect.getProductModelId(), inspect.getQuantity(), StockQualifiedRecordTypeEnum.QUALITYINSPECT_STOCK_IN.getCode(), inspect.getId());
         }
-
         qualityInspect.setInspectState(1);//宸叉彁浜�
         return qualityInspectMapper.updateById(qualityInspect);
     }
diff --git a/src/main/java/com/ruoyi/sales/controller/SalesLedgerProductController.java b/src/main/java/com/ruoyi/sales/controller/SalesLedgerProductController.java
index 549896a..d11ca15 100644
--- a/src/main/java/com/ruoyi/sales/controller/SalesLedgerProductController.java
+++ b/src/main/java/com/ruoyi/sales/controller/SalesLedgerProductController.java
@@ -52,9 +52,9 @@
     {
         List<SalesLedgerProduct> list = salesLedgerProductService.selectSalesLedgerProductList(salesLedgerProduct);
         list.forEach(item -> {
-                if (item.getFutureTickets().compareTo(BigDecimal.ZERO) == 0) {
-                    item.setFutureTickets(item.getQuantity());
-                }
+            if (item.getFutureTickets().compareTo(BigDecimal.ZERO) == 0) {
+                item.setFutureTickets(item.getQuantity());
+            }
             if (item.getFutureTicketsAmount().compareTo(BigDecimal.ZERO) == 0) {
                 item.setFutureTicketsAmount(item.getTaxInclusiveTotalPrice());
             }
@@ -62,12 +62,14 @@
 //            procurementDto.setSalesLedgerProductId(item.getId());
 //            procurementDto.setProductCategory(item.getProductCategory());
 //            IPage<ProcurementPageDtoCopy> result = procurementRecordService.listPageCopyByProduction(new Page<>(1,-1), procurementDto);
-            BigDecimal stockQuantity = stockUtils.getStockQuantity(item.getProductModelId()).get("stockQuantity");
-            if(stockQuantity != null) {
+//            BigDecimal stockQuantity = stockUtils.getStockQuantity(item.getProductModelId()).get("stockQuantity");
+
 //                ProcurementPageDtoCopy procurementDtoCopy = result.getRecords().get(0);
-                if (item.getQuantity().compareTo(stockQuantity) <= 0 && item.getApproveStatus() == 0) {
+            if (item.getApproveStatus() != 2) {
+                if (item.getHasSufficientStock() == 0) {
+                    item.setApproveStatus(0);
+                }else {
                     item.setApproveStatus(1);
-                    salesLedgerProductService.updateById(item);
                 }
             }
         });
diff --git a/src/main/java/com/ruoyi/sales/controller/ShipmentApprovalController.java b/src/main/java/com/ruoyi/sales/controller/ShipmentApprovalController.java
index 63f417b..53f8444 100644
--- a/src/main/java/com/ruoyi/sales/controller/ShipmentApprovalController.java
+++ b/src/main/java/com/ruoyi/sales/controller/ShipmentApprovalController.java
@@ -4,6 +4,7 @@
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.google.common.math.LongMath;
+import com.ruoyi.common.enums.StockQualifiedRecordTypeEnum;
 import com.ruoyi.common.exception.ServiceException;
 import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.framework.web.controller.BaseController;
@@ -14,6 +15,7 @@
 import com.ruoyi.procurementrecord.pojo.ProcurementRecordStorage;
 import com.ruoyi.procurementrecord.service.ProcurementRecordOutService;
 import com.ruoyi.procurementrecord.service.ProcurementRecordService;
+import com.ruoyi.procurementrecord.utils.StockUtils;
 import com.ruoyi.sales.mapper.ShipmentApprovalMapper;
 import com.ruoyi.sales.mapper.ShippingInfoMapper;
 import com.ruoyi.sales.pojo.SalesLedgerProduct;
@@ -50,6 +52,8 @@
     private ProcurementRecordOutService procurementRecordOutService;
     @Autowired
     private ProcurementRecordService procurementRecordStorageService;
+    @Autowired
+    private StockUtils stockUtils;
 
     @GetMapping("/listPage")
     @ApiOperation("鍙戣揣瀹℃壒鍒楄〃")
@@ -100,17 +104,7 @@
 //            }
 
             //  鐢熸垚鍑哄簱璁板綍
-            ProcurementRecordOutAdd procurementRecordOutAdd = new ProcurementRecordOutAdd();
-//            procurementRecordOutAdd.setId(procurementRecordStorage.getId());
-            procurementRecordOutAdd.setId(0);
-            procurementRecordOutAdd.setProductModelId(salesLedgerProduct.getProductModelId());
-            procurementRecordOutAdd.setSalesLedgerProductId((long) Math.toIntExact(salesLedgerProduct.getId()));
-            procurementRecordOutAdd.setType(2);
-            procurementRecordOutAdd.setUserId(Math.toIntExact(getUserId()));
-            procurementRecordOutAdd.setQuantity(salesLedgerProduct.getQuantity().toPlainString());
-            procurementRecordOutAdd.setTime(LocalDate.now().toString());
-
-            procurementRecordOutService.stockout(procurementRecordOutAdd);
+            stockUtils.substractStock(salesLedgerProduct.getProductModelId(), salesLedgerProduct.getQuantity(), StockQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode(), shipmentApproval.getId());
         }
 
         return AjaxResult.success();
diff --git a/src/main/java/com/ruoyi/sales/mapper/SalesLedgerProductMapper.java b/src/main/java/com/ruoyi/sales/mapper/SalesLedgerProductMapper.java
index 58e8d9c..b4ec1fd 100644
--- a/src/main/java/com/ruoyi/sales/mapper/SalesLedgerProductMapper.java
+++ b/src/main/java/com/ruoyi/sales/mapper/SalesLedgerProductMapper.java
@@ -15,6 +15,9 @@
 public interface SalesLedgerProductMapper extends MyBaseMapper<SalesLedgerProduct> {
     List<SalesLedgerProduct> selectSalesLedgerProductList(@Param("salesLedgerProduct") SalesLedgerProduct salesLedgerProduct);
 
+    List<SalesLedgerProduct> selectProduct() ;
+
+
     SalesLedgerProduct selectSalesLedgerProductByMainId(@Param("productMainId") Long productMainId);
 
 }
diff --git a/src/main/java/com/ruoyi/sales/pojo/SalesLedgerProduct.java b/src/main/java/com/ruoyi/sales/pojo/SalesLedgerProduct.java
index 03a034d..b17f173 100644
--- a/src/main/java/com/ruoyi/sales/pojo/SalesLedgerProduct.java
+++ b/src/main/java/com/ruoyi/sales/pojo/SalesLedgerProduct.java
@@ -212,4 +212,8 @@
     
     @ApiModelProperty(value = "鏄惁璐ㄦ")
     private Boolean isChecked;
+
+
+    @TableField(exist = false)
+    private Integer hasSufficientStock;
 }
diff --git a/src/main/java/com/ruoyi/stock/controller/StockInRecordController.java b/src/main/java/com/ruoyi/stock/controller/StockInRecordController.java
new file mode 100644
index 0000000..b12fc67
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/controller/StockInRecordController.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.StockInRecordDto;
+import com.ruoyi.stock.service.StockInRecordService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.CollectionUtils;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+@RestController
+@Api(tags = "鍏ュ簱")
+@RequestMapping("/stockInRecord")
+public class StockInRecordController {
+    @Autowired
+    private StockInRecordService stockInRecordService;
+
+    @GetMapping("/listPage")
+    @Log(title = "鐢熶骇鍏ュ簱-鍏ュ簱绠$悊-鍒楄〃", businessType = BusinessType.OTHER)
+    @ApiOperation(value = "鍏ュ簱绠$悊鍒楄〃")
+    public AjaxResult listPage(Page page, StockInRecordDto stockInRecordDto) {
+        IPage<StockInRecordDto> result = stockInRecordService.listPage(page, stockInRecordDto);
+        return AjaxResult.success(result);
+    }
+
+
+
+    @DeleteMapping("")
+    @Log(title = "鍏ュ簱绠$悊-鍒犻櫎鍏ュ簱", businessType = BusinessType.DELETE)
+    public AjaxResult delete(@RequestBody List<Long> ids) {
+        if(CollectionUtils.isEmpty(ids)){
+            return AjaxResult.error("璇烽�夋嫨鑷冲皯涓�鏉℃暟鎹�");
+        }
+        return AjaxResult.success(stockInRecordService.batchDelete(ids));
+    }
+
+    @PostMapping("/exportStockInRecord")
+    @ApiOperation("瀵煎嚭鍏ュ簱璁板綍")
+    public void exportStockInRecord(HttpServletResponse response, StockInRecordDto stockInRecordDto) {
+        stockInRecordService.exportStockInRecord(response,stockInRecordDto);
+    }
+
+}
diff --git a/src/main/java/com/ruoyi/stock/controller/StockInventoryController.java b/src/main/java/com/ruoyi/stock/controller/StockInventoryController.java
new file mode 100644
index 0000000..f4bd009
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/controller/StockInventoryController.java
@@ -0,0 +1,107 @@
+package com.ruoyi.stock.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.common.enums.StockQualifiedRecordTypeEnum;
+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.R;
+import com.ruoyi.stock.dto.StockInventoryDto;
+import com.ruoyi.stock.execl.StockInventoryExportData;
+import com.ruoyi.stock.service.StockInventoryService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * <p>
+ * 搴撳瓨琛� 鍓嶇鎺у埗鍣�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-01-21 04:16:36
+ */
+@RestController
+@RequestMapping("/stockInventory")
+@Api(tags = "搴撳瓨琛�")
+public class StockInventoryController {
+
+    @Autowired
+    private StockInventoryService stockInventoryService;
+
+    @GetMapping("/pagestockInventory")
+    @ApiOperation("鍒嗛〉鏌ヨ搴撳瓨")
+    public R pagestockInventory(Page page, StockInventoryDto stockInventoryDto) {
+        IPage<StockInventoryDto> stockInventoryDtoIPage = stockInventoryService.pagestockInventory(page, stockInventoryDto);
+        return R.ok(stockInventoryDtoIPage);
+    }
+
+    @PostMapping("/addstockInventory")
+    @ApiOperation("鏂板搴撳瓨")
+    public R addstockInventory(@RequestBody StockInventoryDto stockInventoryDto) {
+        stockInventoryDto.setRecordType(String.valueOf(StockQualifiedRecordTypeEnum.CUSTOMIZATION_STOCK_IN.getCode()));
+        stockInventoryDto.setRecordId(0L);
+        return R.ok(stockInventoryService.addstockInventory(stockInventoryDto));
+    }
+
+
+    @PostMapping("/subtractStockInventory")
+    @ApiOperation("鎵e噺搴撳瓨")
+    public R subtractStockInventory(@RequestBody StockInventoryDto stockInventoryDto) {
+        stockInventoryDto.setRecordType(String.valueOf(StockQualifiedRecordTypeEnum.CUSTOMIZATION_STOCK_OUT.getCode()));
+        stockInventoryDto.setRecordId(0L);
+        return R.ok(stockInventoryService.subtractStockInventory(stockInventoryDto));
+    }
+
+
+    @PostMapping("importStockInventory")
+    @ApiOperation("瀵煎叆搴撳瓨")
+    public R importStockInventory(MultipartFile file) {
+        return stockInventoryService.importStockInventory(file);
+    }
+
+    @Log(title = "涓嬭浇搴撳瓨瀵煎叆妯℃澘", businessType = BusinessType.EXPORT)
+    @PostMapping("/downloadStockInventory")
+    public void downloadStockInventory(HttpServletResponse response) {
+        List<StockInventoryExportData> list = new ArrayList<>();
+        ExcelUtil<StockInventoryExportData> util = new ExcelUtil<>(StockInventoryExportData.class);
+        util.exportExcel(response, list, "搴撳瓨妯℃澘");
+    }
+
+    @PostMapping("/exportStockInventory")
+    @ApiOperation("瀵煎嚭搴撳瓨")
+    public void exportStockInventory(HttpServletResponse response, StockInventoryDto stockInventoryDto) {
+        stockInventoryService.exportStockInventory(response, stockInventoryDto);
+    }
+
+    @GetMapping("stockInventoryPage")
+    @ApiOperation("搴撳瓨鎶ヨ〃鏌ヨ")
+    public R stockInventoryPage(Page page, StockInventoryDto stockInventoryDto) {
+        return R.ok(stockInventoryService.stockInventoryPage(stockInventoryDto,page));
+    }
+
+    @GetMapping("stockInAndOutRecord")
+    @ApiOperation("缁熻鍚勪釜浜у搧鐨勫叆搴撳拰鍑哄簱璁板綍")
+    public R stockInAndOutRecord(StockInventoryDto stockInventoryDto,Page page) {
+        return R.ok(stockInventoryService.stockInAndOutRecord(stockInventoryDto,page));
+    }
+
+    @PostMapping("/frozenStock")
+    @ApiOperation("鍐荤粨搴撳瓨")
+    public R frozenStock(@RequestBody StockInventoryDto stockInventoryDto) {
+        return R.ok(stockInventoryService.frozenStock(stockInventoryDto));
+    }
+
+    @PostMapping("/thawStock")
+    @ApiOperation("瑙e喕搴撳瓨")
+    public R thawStock(@RequestBody StockInventoryDto stockInventoryDto) {
+        return R.ok(stockInventoryService.thawStock(stockInventoryDto));
+    }
+}
diff --git a/src/main/java/com/ruoyi/stock/controller/StockOutRecordController.java b/src/main/java/com/ruoyi/stock/controller/StockOutRecordController.java
new file mode 100644
index 0000000..bd98859
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/controller/StockOutRecordController.java
@@ -0,0 +1,69 @@
+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.StockOutRecordDto;
+import com.ruoyi.stock.service.StockOutRecordService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.CollectionUtils;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+/**
+ * <p>
+ * 鍑哄簱璁板綍琛� 鍓嶇鎺у埗鍣�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-01-21 05:27:04
+ */
+@Api(tags = "鍑哄簱")
+@RestController
+@RequestMapping("/stockOutRecord")
+public class StockOutRecordController {
+    @Autowired
+    private StockOutRecordService stockOutRecordService;
+
+    @GetMapping("/listPage")
+    @Log(title = "鐢熶骇鍑哄簱-鍑哄簱绠$悊-鍒楄〃", businessType = BusinessType.OTHER)
+    @ApiOperation(value = "鍑哄簱绠$悊鍒楄〃")
+    public AjaxResult listPage(Page page, StockOutRecordDto stockOutRecordDto) {
+        IPage<StockOutRecordDto> result = stockOutRecordService.listPage(page, stockOutRecordDto);
+        return AjaxResult.success(result);
+    }
+
+    @PostMapping("")
+    @Log(title = "鍑哄簱绠$悊-鏂板鍑哄簱", businessType = BusinessType.INSERT)
+    public AjaxResult add(@RequestBody StockOutRecordDto stockOutRecordDto) {
+        return AjaxResult.success(stockOutRecordService.add(stockOutRecordDto));
+    }
+
+    @PutMapping("/{id}")
+    @Log(title = "鍑哄簱绠$悊-鏇存柊鍑哄簱", businessType = BusinessType.UPDATE)
+    public AjaxResult update(@PathVariable("id") Long id, @RequestBody StockOutRecordDto stockOutRecordDto) {
+        return AjaxResult.success(stockOutRecordService.update(id, stockOutRecordDto));
+    }
+
+    @DeleteMapping("")
+    @Log(title = "鍑哄簱绠$悊-鍒犻櫎鍑哄簱", businessType = BusinessType.DELETE)
+    public AjaxResult delete(@RequestBody List<Long> ids) {
+        if(CollectionUtils.isEmpty(ids)){
+            return AjaxResult.error("璇烽�夋嫨鑷冲皯涓�鏉℃暟鎹�");
+        }
+        return AjaxResult.success(stockOutRecordService.batchDelete(ids));
+    }
+
+    @PostMapping("/exportStockOutRecord")
+    @ApiOperation("瀵煎嚭鍑哄簱璁板綍")
+    public void exportStockOutRecord(HttpServletResponse response, StockOutRecordDto stockOutRecordDto) {
+        stockOutRecordService.exportStockOutRecord(response,stockOutRecordDto);
+    }
+
+}
diff --git a/src/main/java/com/ruoyi/stock/controller/StockUninventoryController.java b/src/main/java/com/ruoyi/stock/controller/StockUninventoryController.java
new file mode 100644
index 0000000..43714ca
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/controller/StockUninventoryController.java
@@ -0,0 +1,73 @@
+package com.ruoyi.stock.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.common.enums.StockUnQualifiedRecordTypeEnum;
+import com.ruoyi.framework.web.domain.R;
+import com.ruoyi.stock.dto.StockInventoryDto;
+import com.ruoyi.stock.dto.StockUninventoryDto;
+import com.ruoyi.stock.service.StockUninventoryService;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * <p>
+ * 涓嶅悎鏍煎簱瀛樿〃 鍓嶇鎺у埗鍣�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-01-22 10:17:45
+ */
+@RestController
+@RequestMapping("/stockUninventory")
+public class StockUninventoryController {
+    @Autowired
+    private StockUninventoryService stockUninventoryService;
+
+    @GetMapping("/pagestockUninventory")
+    @ApiOperation("鍒嗛〉鏌ヨ搴撳瓨")
+    public R pagestockUninventory(Page page, StockUninventoryDto stockUninventoryDto) {
+        IPage<StockUninventoryDto> stockUninventoryDtoIPage = stockUninventoryService.pageStockUninventory(page, stockUninventoryDto);
+        return R.ok(stockUninventoryDtoIPage);
+    }
+
+    @PostMapping("/addstockUninventory")
+    @ApiOperation("鏂板搴撳瓨")
+    public R addstockUninventory(@RequestBody StockUninventoryDto stockUninventoryDto) {
+        stockUninventoryDto.setRecordType(String.valueOf(StockUnQualifiedRecordTypeEnum.CUSTOMIZATION_UNSTOCK_IN.getCode()));
+        stockUninventoryDto.setRecordId(0L);
+        return R.ok(stockUninventoryService.addStockUninventory(stockUninventoryDto));
+    }
+
+
+    @PostMapping("/subtractstockUninventory")
+    @ApiOperation("鎵e噺搴撳瓨")
+    public R subtractstockUninventory(@RequestBody StockUninventoryDto stockUninventoryDto) {
+        stockUninventoryDto.setRecordType(String.valueOf(StockUnQualifiedRecordTypeEnum.CUSTOMIZATION_UNSTOCK_OUT.getCode()));
+        stockUninventoryDto.setRecordId(0L);
+        return R.ok(stockUninventoryService.subtractStockUninventory(stockUninventoryDto));
+    }
+
+    @PostMapping("/exportStockUninventory")
+    @ApiOperation("瀵煎嚭搴撳瓨")
+    public void exportStockUninventory(HttpServletResponse response, StockUninventoryDto stockUninventoryDto) {
+        stockUninventoryService.exportStockUninventory(response,stockUninventoryDto);
+    }
+
+
+    @PostMapping("/frozenStock")
+    @ApiOperation("鍐荤粨搴撳瓨")
+    public R frozenStock(@RequestBody StockInventoryDto stockInventoryDto) {
+        return R.ok(stockUninventoryService.frozenStock(stockInventoryDto));
+    }
+
+    @PostMapping("/thawStock")
+    @ApiOperation("瑙e喕搴撳瓨")
+    public R thawStock(@RequestBody StockInventoryDto stockInventoryDto) {
+        return R.ok(stockUninventoryService.thawStock(stockInventoryDto));
+    }
+
+}
diff --git a/src/main/java/com/ruoyi/stock/dto/StockInRecordDto.java b/src/main/java/com/ruoyi/stock/dto/StockInRecordDto.java
new file mode 100644
index 0000000..1aed75c
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/dto/StockInRecordDto.java
@@ -0,0 +1,28 @@
+package com.ruoyi.stock.dto;
+
+import com.ruoyi.stock.pojo.StockInRecord;
+import lombok.Data;
+
+
+@Data
+public class StockInRecordDto extends StockInRecord {
+    /**
+     * 浜у搧鍚嶇О
+     */
+    private String productName;
+    /**
+     * 浜у搧瑙勬牸
+     */
+    private String model;
+    /**
+     * 浜у搧鍗曚綅
+     */
+    private String unit;
+
+    private String timeStr;
+
+    private String createBy;
+
+    //鐜板瓨閲�
+    private String currentStock;
+}
diff --git a/src/main/java/com/ruoyi/stock/dto/StockInventoryDto.java b/src/main/java/com/ruoyi/stock/dto/StockInventoryDto.java
new file mode 100644
index 0000000..3d5856f
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/dto/StockInventoryDto.java
@@ -0,0 +1,42 @@
+package com.ruoyi.stock.dto;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.stock.pojo.StockInventory;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+@Data
+public class StockInventoryDto extends StockInventory {
+
+    private String productName;
+    private String model;
+    private String unit;
+
+
+    //鍏ュ簱绫诲瀷
+    private String recordType;
+
+    //鍏ュ簱绫诲瀷瀵瑰簲鐨刬d
+    private Long recordId;
+
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private LocalDate reportDate;
+
+    //搴撳瓨鏈堟姤鏌ヨ瀛楁
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private LocalDate startMonth;
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private LocalDate endMonth;
+
+    private BigDecimal totalStockIn;
+    private BigDecimal totalStockOut;
+    private BigDecimal currentStock;
+
+    private String  unLockedQuantity;
+}
diff --git a/src/main/java/com/ruoyi/stock/dto/StockOutRecordDto.java b/src/main/java/com/ruoyi/stock/dto/StockOutRecordDto.java
new file mode 100644
index 0000000..3e3abf9
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/dto/StockOutRecordDto.java
@@ -0,0 +1,28 @@
+package com.ruoyi.stock.dto;
+
+import com.ruoyi.stock.pojo.StockOutRecord;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class StockOutRecordDto extends StockOutRecord {
+    /**
+     * 浜у搧鍚嶇О
+     */
+    private String productName;
+    /**
+     * 浜у搧瑙勬牸
+     */
+    private String model;
+    /**
+     * 浜у搧鍗曚綅
+     */
+    private String unit;
+
+    private String timeStr;
+
+    private String createBy;
+}
diff --git a/src/main/java/com/ruoyi/stock/dto/StockUninventoryDto.java b/src/main/java/com/ruoyi/stock/dto/StockUninventoryDto.java
new file mode 100644
index 0000000..404c58a
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/dto/StockUninventoryDto.java
@@ -0,0 +1,22 @@
+package com.ruoyi.stock.dto;
+
+import com.ruoyi.stock.pojo.StockUninventory;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+public class StockUninventoryDto  extends StockUninventory {
+    private String productName;
+    private String model;
+    private String unit;
+
+
+    //鍏ュ簱绫诲瀷
+    private String recordType;
+
+    //鍏ュ簱绫诲瀷瀵瑰簲鐨刬d
+    private Long recordId;
+
+    private BigDecimal unLockedQuantity;
+}
diff --git a/src/main/java/com/ruoyi/stock/execl/StockInRecordExportData.java b/src/main/java/com/ruoyi/stock/execl/StockInRecordExportData.java
new file mode 100644
index 0000000..d705110
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/execl/StockInRecordExportData.java
@@ -0,0 +1,32 @@
+package com.ruoyi.stock.execl;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.framework.aspectj.lang.annotation.Excel;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+@Data
+public class StockInRecordExportData {
+
+    @Excel(name = "鍏ュ簱鎵规")
+    private String inboundBatches;
+    @Excel(name = "浜у搧鍚嶇О")
+    private String productName;
+    @Excel(name = "瑙勬牸鍨嬪彿")
+    private String model;
+    @Excel(name = "鍗曚綅")
+    private String unit;
+    @Excel(name = "鍏ュ簱鏉ユ簮")
+    private String recordType;
+    @Excel(name = "鍏ュ簱鏁伴噺")
+    private String stockInNum;
+    @Excel(name = "鍏ュ簱鏃堕棿")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    @Excel(isExport = false)
+    private String type;
+}
diff --git a/src/main/java/com/ruoyi/stock/execl/StockInventoryExportData.java b/src/main/java/com/ruoyi/stock/execl/StockInventoryExportData.java
new file mode 100644
index 0000000..86feed9
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/execl/StockInventoryExportData.java
@@ -0,0 +1,33 @@
+package com.ruoyi.stock.execl;
+
+import com.ruoyi.framework.aspectj.lang.annotation.Excel;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+public class StockInventoryExportData {
+
+
+
+    @Excel(name = "浜у搧鍚嶇О")
+    private String productName;
+
+    @Excel(name = "瑙勬牸")
+    private String model;
+
+    @Excel(name = "鍗曚綅")
+    private String unit;
+
+    @Excel(name = "搴撳瓨鏁伴噺")
+    private BigDecimal qualitity;
+
+    @Excel(name = "澶囨敞")
+    private String remark;
+//
+//    @Excel(name = "鏈�鏂版洿鏂版椂闂�")
+//    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+//    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+//    private LocalDateTime updateTime;
+
+}
diff --git a/src/main/java/com/ruoyi/stock/execl/StockOutRecordExportData.java b/src/main/java/com/ruoyi/stock/execl/StockOutRecordExportData.java
new file mode 100644
index 0000000..f120817
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/execl/StockOutRecordExportData.java
@@ -0,0 +1,31 @@
+package com.ruoyi.stock.execl;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.framework.aspectj.lang.annotation.Excel;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+@Data
+public class StockOutRecordExportData {
+    @Excel(name = "鍑哄簱鎵规")
+    private String outboundBatches;
+    @Excel(name = "浜у搧鍚嶇О")
+    private String productName;
+    @Excel(name = "瑙勬牸鍨嬪彿")
+    private String model;
+    @Excel(name = "鍗曚綅")
+    private String unit;
+    @Excel(name = "鍑哄簱鏉ユ簮")
+    private String recordType;
+    @Excel(name = "鍑哄簱鏁伴噺")
+    private String stockInNum;
+    @Excel(name = "鍑哄簱鏃堕棿")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    @Excel(isExport = false)
+    private String type;
+}
diff --git a/src/main/java/com/ruoyi/stock/mapper/StockInRecordMapper.java b/src/main/java/com/ruoyi/stock/mapper/StockInRecordMapper.java
new file mode 100644
index 0000000..afc30ae
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/mapper/StockInRecordMapper.java
@@ -0,0 +1,17 @@
+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.StockInRecordDto;
+import com.ruoyi.stock.execl.StockInRecordExportData;
+import com.ruoyi.stock.pojo.StockInRecord;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+public interface StockInRecordMapper extends BaseMapper<StockInRecord> {
+    IPage<StockInRecordDto> listPage(Page page, @Param("params") StockInRecordDto stockInRecordDto);
+
+    List<StockInRecordExportData> listStockInRecordExportData(@Param("params") StockInRecordDto stockInRecordDto);
+}
diff --git a/src/main/java/com/ruoyi/stock/mapper/StockInventoryMapper.java b/src/main/java/com/ruoyi/stock/mapper/StockInventoryMapper.java
new file mode 100644
index 0000000..d37e367
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/mapper/StockInventoryMapper.java
@@ -0,0 +1,38 @@
+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.StockInRecordDto;
+import com.ruoyi.stock.dto.StockInventoryDto;
+import com.ruoyi.stock.execl.StockInventoryExportData;
+import com.ruoyi.stock.pojo.StockInventory;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 搴撳瓨琛� Mapper 鎺ュ彛
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-01-21 04:16:36
+ */
+@Mapper
+public interface StockInventoryMapper extends BaseMapper<StockInventory> {
+
+    IPage<StockInventoryDto> pagestockInventory(Page page, @Param("ew") StockInventoryDto stockInventoryDto);
+
+
+    int updateAddStockInventory(@Param("ew") StockInventoryDto stockInventoryDto);
+
+    int updateSubtractStockInventory(@Param("ew") StockInventoryDto stockInventoryDto);
+
+    List<StockInventoryExportData> listStockInventoryExportData(@Param("ew") StockInventoryDto stockInventoryDto);
+
+    IPage<StockInRecordDto> stockInventoryPage(@Param("ew") StockInventoryDto stockInventoryDto, Page page);
+
+    IPage<StockInventoryDto> stockInAndOutRecord(@Param("ew") StockInventoryDto stockInventoryDto, Page page);
+}
diff --git a/src/main/java/com/ruoyi/stock/mapper/StockOutRecordMapper.java b/src/main/java/com/ruoyi/stock/mapper/StockOutRecordMapper.java
new file mode 100644
index 0000000..d180ef9
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/mapper/StockOutRecordMapper.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.StockOutRecordDto;
+import com.ruoyi.stock.execl.StockOutRecordExportData;
+import com.ruoyi.stock.pojo.StockOutRecord;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 鍑哄簱璁板綍琛� Mapper 鎺ュ彛
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-01-21 05:27:04
+ */
+@Mapper
+public interface StockOutRecordMapper extends BaseMapper<StockOutRecord> {
+    IPage<StockOutRecordDto> listPage(Page page, @Param("params") StockOutRecordDto stockOutRecordDto);
+
+    List<StockOutRecordExportData> listStockOutRecordExportData(@Param("params") StockOutRecordDto stockOutRecordDto);
+
+}
diff --git a/src/main/java/com/ruoyi/stock/mapper/StockUninventoryMapper.java b/src/main/java/com/ruoyi/stock/mapper/StockUninventoryMapper.java
new file mode 100644
index 0000000..a5bdc44
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/mapper/StockUninventoryMapper.java
@@ -0,0 +1,32 @@
+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.StockUninventoryDto;
+import com.ruoyi.stock.execl.StockInventoryExportData;
+import com.ruoyi.stock.pojo.StockUninventory;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 涓嶅悎鏍煎簱瀛樿〃 Mapper 鎺ュ彛
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-01-22 10:17:45
+ */
+@Mapper
+public interface StockUninventoryMapper extends BaseMapper<StockUninventory> {
+
+    IPage<StockUninventoryDto> pageStockUninventory(Page page, @Param("ew") StockUninventoryDto stockUninventoryDto);
+
+    int updateSubtractStockUnInventory(@Param("ew") StockUninventoryDto stockUninventoryDto);
+
+    int updateAddStockUnInventory(@Param("ew") StockUninventoryDto stockUninventoryDto);
+
+    List<StockInventoryExportData> listStockInventoryExportData(@Param("ew") StockUninventoryDto stockUninventoryDto);
+}
diff --git a/src/main/java/com/ruoyi/stock/pojo/StockInRecord.java b/src/main/java/com/ruoyi/stock/pojo/StockInRecord.java
new file mode 100644
index 0000000..395d18f
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/pojo/StockInRecord.java
@@ -0,0 +1,65 @@
+package com.ruoyi.stock.pojo;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Data
+@TableName("stock_in_record")
+@ApiModel("鍏ュ簱绠$悊")
+public class StockInRecord {
+
+    private static final long serialVersionUID = 1L;
+    /**
+     * 搴忓彿
+     */
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty(value = "鍏ュ簱鎵规")
+    private String inboundBatches;
+
+    @ApiModelProperty(value = "鍏ュ簱鏁伴噺")
+    private BigDecimal stockInNum;
+
+    @ApiModelProperty(value = "璁板綍绫诲瀷  鏋氫妇")
+    private String recordType;
+
+    @ApiModelProperty(value = "璁板綍ID  ")
+    private Long recordId;
+
+    @ApiModelProperty(value = "浜у搧瑙勬牸ID")
+    private Long productModelId;
+
+    @ApiModelProperty(value = "澶囨敞")
+    private String remark;
+
+    @ApiModelProperty(value = "绫诲瀷  0鍚堟牸鍏ュ簱 1涓嶅悎鏍煎叆搴�")
+    private String type;
+
+    @ApiModelProperty(value = "鍒涘缓鏃堕棿")
+    @TableField(fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    @ApiModelProperty(value = "鍒涘缓鐢ㄦ埛")
+    @TableField(fill = FieldFill.INSERT)
+    private Integer createUser;
+
+    @ApiModelProperty(value = "淇敼鏃堕棿")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime updateTime;
+
+    @ApiModelProperty(value = "淇敼鐢ㄦ埛")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Integer updateUser;
+}
diff --git a/src/main/java/com/ruoyi/stock/pojo/StockInventory.java b/src/main/java/com/ruoyi/stock/pojo/StockInventory.java
new file mode 100644
index 0000000..3e0ad52
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/pojo/StockInventory.java
@@ -0,0 +1,66 @@
+package com.ruoyi.stock.pojo;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import javax.validation.constraints.NotBlank;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * <p>
+ * 搴撳瓨琛�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-01-21 04:16:36
+ */
+@Getter
+@Setter
+@TableName("stock_inventory")
+@ApiModel(value = "StockInventory瀵硅薄", description = "搴撳瓨琛�")
+public class StockInventory implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty("涓婚敭id")
+    private Long id;
+
+    @ApiModelProperty("瑙勬牸id")
+    @NotBlank(message = "涓嶈兘涓虹┖")
+    private Long productModelId;
+
+    @ApiModelProperty("鏁伴噺")
+    private BigDecimal qualitity;
+
+    @TableField(fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    @ApiModelProperty("鏇存柊鏃堕棿")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime updateTime;
+
+    @ApiModelProperty("鐗堟湰鍙�")
+    private Integer version;
+
+    @ApiModelProperty("琚鍗曢攣瀹氭暟閲�")
+    private BigDecimal lockedQuantity;
+
+    @ApiModelProperty("棰勮鏁伴噺")
+    private Integer warnNum;
+
+    @ApiModelProperty("澶囨敞")
+    private String remark;
+}
diff --git a/src/main/java/com/ruoyi/stock/pojo/StockOutRecord.java b/src/main/java/com/ruoyi/stock/pojo/StockOutRecord.java
new file mode 100644
index 0000000..7fba29a
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/pojo/StockOutRecord.java
@@ -0,0 +1,74 @@
+package com.ruoyi.stock.pojo;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * <p>
+ * 鍑哄簱璁板綍琛�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-01-21 05:27:04
+ */
+@Getter
+@Setter
+@TableName("stock_out_record")
+@ApiModel(value = "StockOutRecord瀵硅薄", description = "鍑哄簱璁板綍琛�")
+public class StockOutRecord implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty("鍏ュ簱鎵规")
+    private String outboundBatches;
+
+    @ApiModelProperty("鍏ュ簱鏁伴噺")
+    private BigDecimal stockOutNum;
+
+    @ApiModelProperty("鍏ュ簱鏉ユ簮id")
+    private Long recordId;
+
+    @ApiModelProperty("鍏ュ簱绫诲瀷")
+    private String recordType;
+
+    @ApiModelProperty("浜у搧瑙勬牸id")
+    private Long productModelId;
+
+    @ApiModelProperty("澶囨敞")
+    private String remark;
+
+    @ApiModelProperty("鍒涘缓鏃堕棿")
+    @TableField(fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    @ApiModelProperty("鏇存柊鏃堕棿")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime updateTime;
+
+    @ApiModelProperty("鍒涘缓浜�")
+    @TableField(fill = FieldFill.INSERT)
+    private Integer createUser;
+
+    @ApiModelProperty("鏇存柊浜�")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Integer updateUser;
+
+    @ApiModelProperty(value = "绫诲瀷  0鍚堟牸鍏ュ簱 1涓嶅悎鏍煎叆搴�")
+    private String type;
+}
diff --git a/src/main/java/com/ruoyi/stock/pojo/StockUninventory.java b/src/main/java/com/ruoyi/stock/pojo/StockUninventory.java
new file mode 100644
index 0000000..6ed47de
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/pojo/StockUninventory.java
@@ -0,0 +1,62 @@
+package com.ruoyi.stock.pojo;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * <p>
+ * 涓嶅悎鏍煎簱瀛樿〃
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-01-22 10:17:45
+ */
+@Getter
+@Setter
+@TableName("stock_uninventory")
+@ApiModel(value = "StockUninventory瀵硅薄", description = "涓嶅悎鏍煎簱瀛樿〃")
+public class StockUninventory implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty("涓婚敭id")
+    private Long id;
+
+    @ApiModelProperty("瑙勬牸id")
+    private Long productModelId;
+
+    @ApiModelProperty("鏁伴噺")
+    private BigDecimal qualitity;
+
+    @TableField(fill = FieldFill.INSERT)
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime createTime;
+
+    @ApiModelProperty("鏇存柊鏃堕棿")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime updateTime;
+
+    @ApiModelProperty("鐗堟湰鍙�")
+    private Integer version;
+
+    @ApiModelProperty("澶囨敞")
+    private String remark;
+
+    @ApiModelProperty("琚鍗曢攣瀹氭暟閲�")
+    private BigDecimal lockedQuantity;
+
+}
diff --git a/src/main/java/com/ruoyi/stock/service/StockInRecordService.java b/src/main/java/com/ruoyi/stock/service/StockInRecordService.java
new file mode 100644
index 0000000..29ba7e5
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/service/StockInRecordService.java
@@ -0,0 +1,22 @@
+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.StockInRecordDto;
+import com.ruoyi.stock.pojo.StockInRecord;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+public interface StockInRecordService extends IService<StockInRecord> {
+    IPage<StockInRecordDto> listPage(Page page, StockInRecordDto stockInRecordDto);
+
+    int add(StockInRecordDto stockInRecordDto);
+
+    int update(Long id, StockInRecordDto stockInRecordDto);
+
+    int batchDelete(List<Long> ids);
+
+    void exportStockInRecord(HttpServletResponse response, StockInRecordDto stockInRecordDto);
+}
diff --git a/src/main/java/com/ruoyi/stock/service/StockInventoryService.java b/src/main/java/com/ruoyi/stock/service/StockInventoryService.java
new file mode 100644
index 0000000..65dcca3
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/service/StockInventoryService.java
@@ -0,0 +1,41 @@
+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.framework.web.domain.R;
+import com.ruoyi.stock.dto.StockInRecordDto;
+import com.ruoyi.stock.dto.StockInventoryDto;
+import com.ruoyi.stock.pojo.StockInventory;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * <p>
+ * 搴撳瓨琛� 鏈嶅姟绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-01-21 04:16:36
+ */
+public interface StockInventoryService extends IService<StockInventory> {
+
+    IPage<StockInventoryDto> pagestockInventory(Page page, StockInventoryDto stockInventoryDto);
+
+    Boolean addstockInventory(StockInventoryDto stockInventoryDto);
+
+    Boolean subtractStockInventory(StockInventoryDto stockInventoryDto);
+
+    R importStockInventory(MultipartFile file);
+
+    void exportStockInventory(HttpServletResponse response, StockInventoryDto stockInventoryDto);
+
+    IPage<StockInRecordDto> stockInventoryPage(StockInventoryDto stockInventoryDto,Page page);
+
+    IPage<StockInventoryDto> stockInAndOutRecord(StockInventoryDto stockInventoryDto,Page page);
+
+    Boolean frozenStock(StockInventoryDto stockInventoryDto);
+
+    Boolean thawStock(StockInventoryDto stockInventoryDto);
+}
diff --git a/src/main/java/com/ruoyi/stock/service/StockOutRecordService.java b/src/main/java/com/ruoyi/stock/service/StockOutRecordService.java
new file mode 100644
index 0000000..f18d50c
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/service/StockOutRecordService.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.StockOutRecordDto;
+import com.ruoyi.stock.pojo.StockOutRecord;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+/**
+ * <p>
+ * 鍑哄簱璁板綍琛� 鏈嶅姟绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-01-21 05:27:04
+ */
+public interface StockOutRecordService extends IService<StockOutRecord> {
+    IPage<StockOutRecordDto> listPage(Page page, StockOutRecordDto stockOutRecordDto);
+
+    int add(StockOutRecordDto stockOutRecordDto);
+
+    int update(Long id, StockOutRecordDto stockOutRecordDto);
+
+    int batchDelete(List<Long> ids);
+
+    void exportStockOutRecord(HttpServletResponse response, StockOutRecordDto stockOutRecordDto);
+}
diff --git a/src/main/java/com/ruoyi/stock/service/StockUninventoryService.java b/src/main/java/com/ruoyi/stock/service/StockUninventoryService.java
new file mode 100644
index 0000000..0d6ba6d
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/service/StockUninventoryService.java
@@ -0,0 +1,33 @@
+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.StockInventoryDto;
+import com.ruoyi.stock.dto.StockUninventoryDto;
+import com.ruoyi.stock.pojo.StockUninventory;
+
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * <p>
+ * 涓嶅悎鏍煎簱瀛樿〃 鏈嶅姟绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-01-22 10:17:45
+ */
+public interface StockUninventoryService extends IService<StockUninventory> {
+
+    IPage<StockUninventoryDto> pageStockUninventory(Page page, StockUninventoryDto stockUninventoryDto);
+
+    Integer addStockUninventory(StockUninventoryDto stockUninventoryDto);
+
+    Integer subtractStockUninventory(StockUninventoryDto stockUninventoryDto);
+
+    void exportStockUninventory(HttpServletResponse response, StockUninventoryDto stockUninventoryDto);
+
+    Boolean frozenStock(StockInventoryDto stockInventoryDto);
+
+    Boolean thawStock(StockInventoryDto stockInventoryDto);
+}
diff --git a/src/main/java/com/ruoyi/stock/service/impl/StockInRecordServiceImpl.java b/src/main/java/com/ruoyi/stock/service/impl/StockInRecordServiceImpl.java
new file mode 100644
index 0000000..bf64268
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/service/impl/StockInRecordServiceImpl.java
@@ -0,0 +1,113 @@
+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.StockQualifiedRecordTypeEnum;
+import com.ruoyi.common.enums.StockUnQualifiedRecordTypeEnum;
+import com.ruoyi.common.exception.base.BaseException;
+import com.ruoyi.common.utils.EnumUtil;
+import com.ruoyi.common.utils.OrderUtils;
+import com.ruoyi.common.utils.bean.BeanUtils;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.stock.dto.StockInRecordDto;
+import com.ruoyi.stock.dto.StockInventoryDto;
+import com.ruoyi.stock.dto.StockUninventoryDto;
+import com.ruoyi.stock.execl.StockInRecordExportData;
+import com.ruoyi.stock.mapper.StockInRecordMapper;
+import com.ruoyi.stock.mapper.StockInventoryMapper;
+import com.ruoyi.stock.mapper.StockUninventoryMapper;
+import com.ruoyi.stock.pojo.StockInRecord;
+import com.ruoyi.stock.pojo.StockInventory;
+import com.ruoyi.stock.pojo.StockUninventory;
+import com.ruoyi.stock.service.StockInRecordService;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+@Service
+@AllArgsConstructor
+public class StockInRecordServiceImpl extends ServiceImpl<StockInRecordMapper, StockInRecord> implements StockInRecordService {
+
+    private StockInRecordMapper stockInRecordMapper;
+    private StockInventoryMapper stockInventoryMapper;
+    private StockUninventoryMapper stockUninventoryMapper;
+
+    @Override
+    public IPage<StockInRecordDto> listPage(Page page, StockInRecordDto stockInRecordDto) {
+        return stockInRecordMapper.listPage(page, stockInRecordDto);
+    }
+
+    // 鏂板鍏ュ簱
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public int add(StockInRecordDto stockInRecordDto) {
+        String no = OrderUtils.countTodayByCreateTime(stockInRecordMapper, "RK");
+        stockInRecordDto.setInboundBatches(no);
+        StockInRecord stockInRecord = new StockInRecord();
+        BeanUtils.copyProperties(stockInRecordDto, stockInRecord);
+        return stockInRecordMapper.insert(stockInRecord);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public int update(Long id, StockInRecordDto stockInRecordDto) {
+        // 鍒ゆ柇瀵硅薄鏄惁瀛樺湪
+        StockInRecord stockInRecord = stockInRecordMapper.selectById(id);
+        if (stockInRecord == null){
+            throw new BaseException("璇ュ叆搴撹褰曚笉瀛樺湪,鏃犳硶鏇存柊!!!");
+        }
+
+        String[] ignoreProperties = {"id", "inbound_batches"};//鎺掗櫎id灞炴��
+        BeanUtils.copyProperties(stockInRecordDto, stockInRecord, ignoreProperties);
+        return stockInRecordMapper.updateById(stockInRecord);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public int batchDelete(List<Long> ids) {
+        for (Long id : ids) {
+            StockInRecord stockInRecord = stockInRecordMapper.selectById(id);
+            if (stockInRecord.getType().equals("0")) {
+                StockInventory stockInventory = stockInventoryMapper.selectOne(new LambdaQueryWrapper<StockInventory>().eq(StockInventory::getProductModelId, stockInRecord.getProductModelId()));
+                if (stockInventory == null) {
+                    throw new BaseException("搴撳瓨璁板綍涓病鏈夊搴旂殑浜у搧,鏃犳硶鍒犻櫎!!!");
+                }else {
+                    StockInventoryDto stockInRecordDto = new StockInventoryDto();
+                    stockInRecordDto.setProductModelId(stockInventory.getProductModelId());
+                    stockInRecordDto.setQualitity(stockInRecord.getStockInNum());
+                    stockInventoryMapper.updateAddStockInventory(stockInRecordDto);
+                }
+            }else if (stockInRecord.getType().equals("1")) {
+                StockUninventory stockUninventory = stockUninventoryMapper.selectOne(new LambdaQueryWrapper<StockUninventory>().eq(StockUninventory::getProductModelId, stockInRecord.getProductModelId()));
+                if (stockUninventory == null) {
+                    throw new BaseException("搴撳瓨璁板綍涓病鏈夊搴旂殑浜у搧,鏃犳硶鍒犻櫎!!!");
+                }else {
+                    StockUninventoryDto stockUninventoryDto = new StockUninventoryDto();
+                    stockUninventoryDto.setProductModelId(stockUninventory.getProductModelId());
+                    stockUninventoryDto.setQualitity(stockInRecord.getStockInNum());
+                    stockUninventoryMapper.updateAddStockUnInventory(stockUninventoryDto);
+                }
+            }
+        }
+        return stockInRecordMapper.deleteBatchIds(ids);
+    }
+
+    @Override
+    public void exportStockInRecord(HttpServletResponse response, StockInRecordDto stockInRecordDto) {
+        List<StockInRecordExportData> list = stockInRecordMapper.listStockInRecordExportData(stockInRecordDto);
+        for (StockInRecordExportData stockInRecordExportData : list) {
+            if (stockInRecordExportData.getType().equals("0")) {
+                stockInRecordExportData.setRecordType(EnumUtil.fromCode(StockQualifiedRecordTypeEnum.class, Integer.parseInt(stockInRecordExportData.getRecordType())).getValue());
+            }else {
+                stockInRecordExportData.setRecordType(EnumUtil.fromCode(StockUnQualifiedRecordTypeEnum.class, Integer.parseInt(stockInRecordExportData.getRecordType())).getValue());
+            }
+        }
+        ExcelUtil<StockInRecordExportData> util = new ExcelUtil<>(StockInRecordExportData.class);
+        util.exportExcel(response,list, "鍏ュ簱璁板綍淇℃伅");
+    }
+}
diff --git a/src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java b/src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java
new file mode 100644
index 0000000..4a83a22
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java
@@ -0,0 +1,197 @@
+package com.ruoyi.stock.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.common.enums.StockQualifiedRecordTypeEnum;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.framework.web.domain.R;
+import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
+import com.ruoyi.sales.pojo.SalesLedgerProduct;
+import com.ruoyi.stock.dto.StockInRecordDto;
+import com.ruoyi.stock.dto.StockInventoryDto;
+import com.ruoyi.stock.dto.StockOutRecordDto;
+import com.ruoyi.stock.execl.StockInventoryExportData;
+import com.ruoyi.stock.mapper.StockInventoryMapper;
+import com.ruoyi.stock.pojo.StockInventory;
+import com.ruoyi.stock.service.StockInRecordService;
+import com.ruoyi.stock.service.StockInventoryService;
+import com.ruoyi.stock.service.StockOutRecordService;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * <p>
+ * 搴撳瓨琛� 鏈嶅姟瀹炵幇绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-01-21 04:16:36
+ */
+@Service
+@AllArgsConstructor
+public class StockInventoryServiceImpl extends ServiceImpl<StockInventoryMapper, StockInventory> implements StockInventoryService {
+
+    private  StockInventoryMapper stockInventoryMapper;
+    private StockInRecordService stockInRecordService;
+    private StockOutRecordService stockOutRecordService;
+    private SalesLedgerProductMapper salesLedgerProductMapper;
+    @Override
+    public IPage<StockInventoryDto> pagestockInventory(Page page, StockInventoryDto stockInventoryDto) {
+        return stockInventoryMapper.pagestockInventory(page, stockInventoryDto);
+    }
+
+    //鍏ュ簱璋冪敤
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean addstockInventory(StockInventoryDto stockInventoryDto) {
+        //鏂板鍏ュ簱璁板綍鍐嶆坊鍔犲簱瀛�
+        StockInRecordDto stockInRecordDto = new StockInRecordDto();
+        stockInRecordDto.setRecordId(stockInventoryDto.getRecordId());
+        stockInRecordDto.setRecordType(stockInventoryDto.getRecordType());
+        stockInRecordDto.setStockInNum(stockInventoryDto.getQualitity());
+        stockInRecordDto.setProductModelId(stockInventoryDto.getProductModelId());
+        stockInRecordDto.setType("0");
+        stockInRecordService.add(stockInRecordDto);
+        //鍐嶈繘琛屾柊澧炲簱瀛樻暟閲忓簱瀛�
+        //鍏堟煡璇㈠簱瀛樿〃涓殑浜у搧鏄惁瀛樺湪锛屼笉瀛樺湪鏂板锛屽瓨鍦ㄦ洿鏂�
+        StockInventory oldStockInventory = stockInventoryMapper.selectOne(new QueryWrapper<StockInventory>().lambda().eq(StockInventory::getProductModelId, stockInventoryDto.getProductModelId()));
+        if (ObjectUtils.isEmpty(oldStockInventory)) {
+            StockInventory newStockInventory = new StockInventory();
+            newStockInventory.setProductModelId(stockInventoryDto.getProductModelId());
+            newStockInventory.setQualitity(stockInventoryDto.getQualitity());
+            newStockInventory.setVersion(1);
+            newStockInventory.setRemark(stockInventoryDto.getRemark());
+            stockInventoryMapper.insert(newStockInventory);
+        }else {
+             stockInventoryMapper.updateAddStockInventory(stockInventoryDto);
+        }
+        return true;
+    }
+
+    //鍑哄簱璋冪敤
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean subtractStockInventory(StockInventoryDto stockInventoryDto) {
+            //  鏂板鍑哄簱璁板綍
+        StockOutRecordDto stockOutRecordDto = new StockOutRecordDto();
+        stockOutRecordDto.setRecordId(stockInventoryDto.getRecordId());
+        stockOutRecordDto.setRecordType(stockInventoryDto.getRecordType());
+        stockOutRecordDto.setStockOutNum(stockInventoryDto.getQualitity());
+        stockOutRecordDto.setProductModelId(stockInventoryDto.getProductModelId());
+        stockOutRecordDto.setType("0");
+        stockOutRecordService.add(stockOutRecordDto);
+        StockInventory oldStockInventory = stockInventoryMapper.selectOne(new QueryWrapper<StockInventory>().lambda().eq(StockInventory::getProductModelId, stockInventoryDto.getProductModelId()));
+        if (stockInventoryDto.getQualitity().compareTo( oldStockInventory.getQualitity().subtract(oldStockInventory.getLockedQuantity()))>0) {
+            throw new RuntimeException("搴撳瓨涓嶈冻鏃犳硶鍑哄簱");
+        }
+        if (ObjectUtils.isEmpty(oldStockInventory)) {
+            throw new RuntimeException("浜у搧搴撳瓨涓嶅瓨鍦�");
+        }else {
+            stockInventoryMapper.updateSubtractStockInventory(stockInventoryDto);
+        }
+        return true;
+    }
+
+    @Override
+    public R importStockInventory(MultipartFile file) {
+        try {
+            // 鏌ヨ鎵�鏈夌殑浜у搧
+            List<SalesLedgerProduct> salesLedgerProducts = salesLedgerProductMapper.selectProduct();
+
+            ExcelUtil<StockInventoryExportData> util = new ExcelUtil<StockInventoryExportData>(StockInventoryExportData.class);
+            List<StockInventoryExportData> list = util.importExcel(file.getInputStream());
+
+            // 璁板綍鏈壘鍒板尮閰嶉」鐨勬暟鎹�
+            List<String> unmatchedRecords = new ArrayList<>();
+
+            list.forEach(dto -> {
+                boolean matched = false;
+                for (SalesLedgerProduct item : salesLedgerProducts) {
+                    if (item.getProductCategory().equals(dto.getProductName()) &&
+                            item.getSpecificationModel().equals(dto.getModel())) {
+                        StockInventoryDto stockInventoryDto = new StockInventoryDto();
+                        stockInventoryDto.setRecordId(0L);
+                        stockInventoryDto.setRecordType(StockQualifiedRecordTypeEnum.CUSTOMIZATION_STOCK_IN.getCode());
+                        stockInventoryDto.setQualitity(dto.getQualitity());
+                        stockInventoryDto.setRemark(dto.getRemark());
+                        stockInventoryDto.setProductModelId(item.getProductModelId());
+                        this.addstockInventory(stockInventoryDto);
+                        matched = true;
+                        break; // 鎵惧埌鍖归厤椤瑰悗璺冲嚭寰幆
+                    }
+                }
+                if (!matched) {
+                    // 璁板綍鏈尮閰嶇殑鏁版嵁
+                    String unmatchedInfo = String.format("浜у搧鍚嶇О锛�%s锛岃鏍煎瀷鍙凤細%s",
+                            dto.getProductName(), dto.getModel());
+                    unmatchedRecords.add(unmatchedInfo);
+                }
+            });
+            // 鏋勫缓杩斿洖淇℃伅
+            StringBuilder message = new StringBuilder();
+            if (!unmatchedRecords.isEmpty()) {
+                message.append("浠ヤ笅浜у搧鏈壘鍒板尮閰嶉」锛歕n");
+                for (String record : unmatchedRecords) {
+                    message.append(record).append("\n");
+                }
+                throw new RuntimeException(message.toString());
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            return R.fail("瀵煎叆澶辫触锛�" + e.getMessage());
+        }
+        return R.ok("瀵煎叆鎴愬姛");
+    }
+
+
+    @Override
+    public void exportStockInventory(HttpServletResponse response, StockInventoryDto stockInventoryDto) {
+
+        List<StockInventoryExportData> list = stockInventoryMapper.listStockInventoryExportData(stockInventoryDto);
+        ExcelUtil<StockInventoryExportData> util = new ExcelUtil<>(StockInventoryExportData.class);
+        util.exportExcel(response,list, "搴撳瓨淇℃伅");
+    }
+
+    @Override
+    public IPage<StockInRecordDto> stockInventoryPage(StockInventoryDto stockInventoryDto, Page page) {
+        return stockInventoryMapper.stockInventoryPage(stockInventoryDto,page);
+    }
+
+    @Override
+    public IPage<StockInventoryDto> stockInAndOutRecord(StockInventoryDto stockInventoryDto, Page page) {
+        return stockInventoryMapper.stockInAndOutRecord(stockInventoryDto,page);
+    }
+
+    @Override
+    public Boolean frozenStock(StockInventoryDto stockInventoryDto) {
+        StockInventory stockInventory = stockInventoryMapper.selectById(stockInventoryDto.getId());
+        if (stockInventory.getQualitity().compareTo(stockInventoryDto.getLockedQuantity())<0) {
+            throw new RuntimeException("鍐荤粨鏁伴噺涓嶈兘瓒呰繃搴撳瓨鏁伴噺");
+        }
+        if (ObjectUtils.isEmpty(stockInventory.getLockedQuantity())) {
+            stockInventory.setLockedQuantity(stockInventoryDto.getLockedQuantity());
+        }else {
+            stockInventory.setLockedQuantity(stockInventory.getLockedQuantity().add(stockInventoryDto.getLockedQuantity()));
+        }
+        return this.updateById(stockInventory);
+    }
+
+    @Override
+    public Boolean thawStock(StockInventoryDto stockInventoryDto) {
+        StockInventory stockInventory = stockInventoryMapper.selectById(stockInventoryDto.getId());
+        if (stockInventory.getLockedQuantity().compareTo(stockInventoryDto.getLockedQuantity())<0) {
+            throw new RuntimeException("瑙e喕鏁伴噺涓嶈兘瓒呰繃鍐荤粨鏁伴噺");
+        }
+        stockInventory.setLockedQuantity(stockInventory.getLockedQuantity().subtract(stockInventoryDto.getLockedQuantity()));
+        return this.updateById(stockInventory);
+    }
+}
diff --git a/src/main/java/com/ruoyi/stock/service/impl/StockOutRecordServiceImpl.java b/src/main/java/com/ruoyi/stock/service/impl/StockOutRecordServiceImpl.java
new file mode 100644
index 0000000..f46f4c0
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/service/impl/StockOutRecordServiceImpl.java
@@ -0,0 +1,116 @@
+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.StockQualifiedRecordTypeEnum;
+import com.ruoyi.common.enums.StockUnQualifiedRecordTypeEnum;
+import com.ruoyi.common.exception.base.BaseException;
+import com.ruoyi.common.utils.EnumUtil;
+import com.ruoyi.common.utils.OrderUtils;
+import com.ruoyi.common.utils.bean.BeanUtils;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.stock.dto.StockInventoryDto;
+import com.ruoyi.stock.dto.StockOutRecordDto;
+import com.ruoyi.stock.dto.StockUninventoryDto;
+import com.ruoyi.stock.execl.StockOutRecordExportData;
+import com.ruoyi.stock.mapper.StockInventoryMapper;
+import com.ruoyi.stock.mapper.StockOutRecordMapper;
+import com.ruoyi.stock.mapper.StockUninventoryMapper;
+import com.ruoyi.stock.pojo.StockInRecord;
+import com.ruoyi.stock.pojo.StockInventory;
+import com.ruoyi.stock.pojo.StockOutRecord;
+import com.ruoyi.stock.pojo.StockUninventory;
+import com.ruoyi.stock.service.StockOutRecordService;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+/**
+ * <p>
+ * 鍑哄簱璁板綍琛� 鏈嶅姟瀹炵幇绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-01-21 05:27:04
+ */
+@Service
+@AllArgsConstructor
+public class StockOutRecordServiceImpl extends ServiceImpl<StockOutRecordMapper, StockOutRecord> implements StockOutRecordService {
+    private StockOutRecordMapper stockOutRecordMapper;
+    private StockInventoryMapper stockInventoryMapper;
+    private StockUninventoryMapper stockUninventoryMapper;
+
+    @Override
+    public IPage<StockOutRecordDto> listPage(Page page, StockOutRecordDto stockOutRecordDto) {
+        return stockOutRecordMapper.listPage(page, stockOutRecordDto);
+    }
+
+    @Override
+    public int add(StockOutRecordDto stockOutRecordDto) {
+        String no = OrderUtils.countTodayByCreateTime(stockOutRecordMapper, "CK");
+        stockOutRecordDto.setOutboundBatches(no);
+        StockInRecord stockInRecord = new StockInRecord();
+        BeanUtils.copyProperties(stockOutRecordDto, stockInRecord);
+        return stockOutRecordMapper.insert(stockOutRecordDto);
+    }
+
+    @Override
+    public int update(Long id, StockOutRecordDto stockOutRecordDto) {
+        // 鍒ゆ柇瀵硅薄鏄惁瀛樺湪
+        StockOutRecord stockOutRecord = stockOutRecordMapper.selectById(id);
+        if (stockOutRecord == null){
+            throw new BaseException("璇ュ嚭搴撹褰曚笉瀛樺湪,鏃犳硶鏇存柊!!!");
+        }
+
+        String[] ignoreProperties = {"id", "outbound_batches"};//鎺掗櫎id灞炴��
+        BeanUtils.copyProperties(stockOutRecordDto, stockOutRecord, ignoreProperties);
+        return stockOutRecordMapper.updateById(stockOutRecord);
+    }
+
+    @Override
+    public int batchDelete(List<Long> ids) {
+        for (Long id : ids) {
+            StockOutRecord stockOutRecord = stockOutRecordMapper.selectById(id);
+            if (stockOutRecord.getType().equals("0")) {
+                StockInventory stockInventory = stockInventoryMapper.selectOne(new LambdaQueryWrapper<StockInventory>().eq(StockInventory::getProductModelId, stockOutRecord.getProductModelId()));
+                if (stockInventory == null) {
+                    throw new BaseException("搴撳瓨璁板綍涓病鏈夊搴旂殑浜у搧,鏃犳硶鍒犻櫎!!!");
+                }else {
+                    StockInventoryDto stockInRecordDto = new StockInventoryDto();
+                    stockInRecordDto.setProductModelId(stockInventory.getProductModelId());
+                    stockInRecordDto.setQualitity(stockOutRecord.getStockOutNum());
+                    stockInventoryMapper.updateAddStockInventory(stockInRecordDto);
+                }
+            }else if (stockOutRecord.getType().equals("1")) {
+                StockUninventory stockUninventory = stockUninventoryMapper.selectOne(new LambdaQueryWrapper<StockUninventory>().eq(StockUninventory::getProductModelId, stockOutRecord.getProductModelId()));
+                if (stockUninventory == null) {
+                    throw new BaseException("搴撳瓨璁板綍涓病鏈夊搴旂殑浜у搧,鏃犳硶鍒犻櫎!!!");
+                }else {
+                    StockUninventoryDto stockUninventoryDto = new StockUninventoryDto();
+                    stockUninventoryDto.setProductModelId(stockUninventory.getProductModelId());
+                    stockUninventoryDto.setQualitity(stockOutRecord.getStockOutNum());
+                    stockUninventoryMapper.updateAddStockUnInventory(stockUninventoryDto);
+                }
+            }
+        }
+        return stockOutRecordMapper.deleteBatchIds(ids);
+    }
+
+    @Override
+    public void exportStockOutRecord(HttpServletResponse response, StockOutRecordDto stockOutRecordDto) {
+        List<StockOutRecordExportData> list = stockOutRecordMapper.listStockOutRecordExportData(stockOutRecordDto);
+        for (StockOutRecordExportData stockInRecordExportData : list) {
+            if (stockInRecordExportData.getType().equals("0")) {
+                stockInRecordExportData.setRecordType(EnumUtil.fromCode(StockQualifiedRecordTypeEnum.class, Integer.parseInt(stockInRecordExportData.getRecordType())).getValue());
+            }else {
+                stockInRecordExportData.setRecordType(EnumUtil.fromCode(StockUnQualifiedRecordTypeEnum.class, Integer.parseInt(stockInRecordExportData.getRecordType())).getValue());
+            }
+        }
+        ExcelUtil<StockOutRecordExportData> util = new ExcelUtil<>(StockOutRecordExportData.class);
+        util.exportExcel(response,list, "鍑哄簱璁板綍淇℃伅");
+    }
+}
diff --git a/src/main/java/com/ruoyi/stock/service/impl/StockUninventoryServiceImpl.java b/src/main/java/com/ruoyi/stock/service/impl/StockUninventoryServiceImpl.java
new file mode 100644
index 0000000..42fa159
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/service/impl/StockUninventoryServiceImpl.java
@@ -0,0 +1,126 @@
+package com.ruoyi.stock.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.stock.dto.StockInRecordDto;
+import com.ruoyi.stock.dto.StockInventoryDto;
+import com.ruoyi.stock.dto.StockOutRecordDto;
+import com.ruoyi.stock.dto.StockUninventoryDto;
+import com.ruoyi.stock.execl.StockInventoryExportData;
+import com.ruoyi.stock.mapper.StockUninventoryMapper;
+import com.ruoyi.stock.pojo.StockInventory;
+import com.ruoyi.stock.pojo.StockUninventory;
+import com.ruoyi.stock.service.StockInRecordService;
+import com.ruoyi.stock.service.StockOutRecordService;
+import com.ruoyi.stock.service.StockUninventoryService;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+/**
+ * <p>
+ * 涓嶅悎鏍煎簱瀛樿〃 鏈嶅姟瀹炵幇绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-01-22 10:17:45
+ */
+@Service
+@AllArgsConstructor
+public class StockUninventoryServiceImpl extends ServiceImpl<StockUninventoryMapper, StockUninventory> implements StockUninventoryService {
+
+    private StockUninventoryMapper stockUninventoryMapper;
+    private StockOutRecordService stockOutRecordService;
+    private StockInRecordService stockInRecordService;
+
+    @Override
+    public IPage<StockUninventoryDto> pageStockUninventory(Page page, StockUninventoryDto stockUninventoryDto) {
+        return stockUninventoryMapper.pageStockUninventory(page, stockUninventoryDto);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Integer addStockUninventory(StockUninventoryDto stockUninventoryDto) {
+        //鏂板鍏ュ簱璁板綍鍐嶆坊鍔犲簱瀛�
+        StockInRecordDto stockInRecordDto = new StockInRecordDto();
+        stockInRecordDto.setRecordId(stockUninventoryDto.getRecordId());
+        stockInRecordDto.setRecordType(stockUninventoryDto.getRecordType());
+        stockInRecordDto.setStockInNum(stockUninventoryDto.getQualitity());
+        stockInRecordDto.setProductModelId(stockUninventoryDto.getProductModelId());
+        stockInRecordDto.setType("1");
+        stockInRecordService.add(stockInRecordDto);
+        //鍐嶈繘琛屾柊澧炲簱瀛樻暟閲忓簱瀛�
+        //鍏堟煡璇㈠簱瀛樿〃涓殑浜у搧鏄惁瀛樺湪锛屼笉瀛樺湪鏂板锛屽瓨鍦ㄦ洿鏂�
+        StockUninventory oldStockUnInventory = stockUninventoryMapper.selectOne(new QueryWrapper<StockUninventory>().lambda().eq(StockUninventory::getProductModelId, stockUninventoryDto.getProductModelId()));
+        if (ObjectUtils.isEmpty(oldStockUnInventory)) {
+            StockUninventory newStockUnInventory = new StockUninventory();
+            newStockUnInventory.setProductModelId(stockUninventoryDto.getProductModelId());
+            newStockUnInventory.setQualitity(stockUninventoryDto.getQualitity());
+            newStockUnInventory.setVersion(1);
+            newStockUnInventory.setRemark(stockUninventoryDto.getRemark());
+            stockUninventoryMapper.insert(newStockUnInventory);
+        }else {
+            stockUninventoryMapper.updateAddStockUnInventory(stockUninventoryDto);
+        }
+        return 1;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Integer subtractStockUninventory(StockUninventoryDto stockUninventoryDto) {
+
+        //  鏂板鍑哄簱璁板綍
+        StockOutRecordDto stockOutRecordDto = new StockOutRecordDto();
+        stockOutRecordDto.setRecordId(stockUninventoryDto.getRecordId());
+        stockOutRecordDto.setRecordType(stockUninventoryDto.getRecordType());
+        stockOutRecordDto.setStockOutNum(stockUninventoryDto.getQualitity());
+        stockOutRecordDto.setProductModelId(stockUninventoryDto.getProductModelId());
+        stockOutRecordDto.setType("1");
+        stockOutRecordService.add(stockOutRecordDto);
+        StockUninventory oldStockInventory = stockUninventoryMapper.selectOne(new QueryWrapper<StockUninventory>().lambda().eq(StockUninventory::getProductModelId, stockUninventoryDto.getProductModelId()));
+        if (ObjectUtils.isEmpty(oldStockInventory)) {
+            throw new RuntimeException("浜у搧搴撳瓨涓嶅瓨鍦�");
+        }else {
+            stockUninventoryMapper.updateSubtractStockUnInventory(stockUninventoryDto);
+        }
+        return 1;
+    }
+
+    @Override
+    public void exportStockUninventory(HttpServletResponse response, StockUninventoryDto stockUninventoryDto) {
+        List<StockInventoryExportData> list = stockUninventoryMapper.listStockInventoryExportData(stockUninventoryDto);
+        ExcelUtil<StockInventoryExportData> util = new ExcelUtil<>(StockInventoryExportData.class);
+        util.exportExcel(response,list, "涓嶅悎鏍煎簱瀛樹俊鎭�");
+    }
+
+    @Override
+    public Boolean frozenStock(StockInventoryDto stockInventoryDto) {
+        StockUninventory stockUninventory = stockUninventoryMapper.selectById(stockInventoryDto.getId());
+        if (stockUninventory.getQualitity().compareTo(stockInventoryDto.getLockedQuantity())<0) {
+            throw new RuntimeException("鍐荤粨鏁伴噺涓嶈兘瓒呰繃搴撳瓨鏁伴噺");
+        }
+        if (ObjectUtils.isEmpty(stockUninventory.getLockedQuantity())) {
+            stockUninventory.setLockedQuantity(stockInventoryDto.getLockedQuantity());
+        }else {
+            stockUninventory.setLockedQuantity(stockUninventory.getLockedQuantity().add(stockInventoryDto.getLockedQuantity()));
+        }
+        return this.updateById(stockUninventory);
+    }
+
+    @Override
+    public Boolean thawStock(StockInventoryDto stockInventoryDto) {
+        StockUninventory stockUninventory = stockUninventoryMapper.selectById(stockInventoryDto.getId());
+        if (stockUninventory.getLockedQuantity().compareTo(stockInventoryDto.getLockedQuantity())<0) {
+            throw new RuntimeException("瑙e喕鏁伴噺涓嶈兘瓒呰繃鍐荤粨鏁伴噺");
+        }
+        stockUninventory.setLockedQuantity(stockUninventory.getLockedQuantity().subtract(stockInventoryDto.getLockedQuantity()));
+        return this.updateById(stockUninventory);
+    }
+}
diff --git a/src/main/resources/mapper/sales/SalesLedgerProductMapper.xml b/src/main/resources/mapper/sales/SalesLedgerProductMapper.xml
index 395ce1b..292221c 100644
--- a/src/main/resources/mapper/sales/SalesLedgerProductMapper.xml
+++ b/src/main/resources/mapper/sales/SalesLedgerProductMapper.xml
@@ -8,17 +8,22 @@
         SELECT
         T1.*,
         t3.shipping_car_number,
+        CASE
+        WHEN (t2.qualitity - COALESCE(t2.locked_quantity, 0) )> T1.quantity THEN 1
+        ELSE 0
+        END as has_sufficient_stock,
         t3.shipping_date
         FROM
         sales_ledger_product T1
         left join shipping_info t3 on T1.id = t3.sales_ledger_product_id
+        LEFT JOIN stock_inventory t2 ON T1.product_model_id = t2.product_model_id
         <where>
             1=1
             <if test="salesLedgerProduct.salesLedgerId != null and salesLedgerProduct.salesLedgerId != '' ">
-                AND  T1.sales_ledger_id = #{salesLedgerProduct.salesLedgerId}
+                AND T1.sales_ledger_id = #{salesLedgerProduct.salesLedgerId}
             </if>
             <if test="salesLedgerProduct.type != null and salesLedgerProduct.type != '' ">
-                AND  T1.type = #{salesLedgerProduct.type}
+                AND T1.type = #{salesLedgerProduct.type}
             </if>
         </where>
     </select>
@@ -33,4 +38,12 @@
 
 
     </select>
+    <select id="selectProduct" resultType="com.ruoyi.sales.pojo.SalesLedgerProduct">
+        select
+            p.product_name as product_category,
+            pm.model as specification_model,
+            pm.id as product_model_id
+        from product_model pm
+                 left join product p on pm.product_id = p.id
+    </select>
 </mapper>
\ No newline at end of file
diff --git a/src/main/resources/mapper/stock/StockInRecordMapper.xml b/src/main/resources/mapper/stock/StockInRecordMapper.xml
new file mode 100644
index 0000000..d5c3f38
--- /dev/null
+++ b/src/main/resources/mapper/stock/StockInRecordMapper.xml
@@ -0,0 +1,59 @@
+<?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.StockInRecordMapper">
+
+    <select id="listPage" resultType="com.ruoyi.stock.dto.StockInRecordDto">
+        SELECT
+        sir.*,
+        p.product_name as product_name,
+        pm.model,
+        pm.unit,
+        u.nick_name as createBy
+        FROM stock_in_record as sir
+        LEFT JOIN product_model as pm on sir.product_model_id = pm.id
+        LEFT JOIN product as p on pm.product_id = p.id
+        LEFT JOIN sys_user as u on sir.create_user = u.user_id
+        <where>
+            <if test="params.timeStr != null and params.timeStr != ''">
+                and sir.create_time like concat('%',#{params.timeStr},'%')
+            </if>
+            <if test="params.productName != null and params.productName != ''">
+                and p.product_name like concat('%',#{params.productName},'%')
+            </if>
+            <if test="params.type != null and params.type != ''">
+                and sir.type = #{params.type}
+            </if>
+            <if test="params.recordType != null and params.recordType != ''">
+                and sir.record_type = #{params.recordType}
+            </if>
+        </where>
+        order by sir.id desc
+    </select>
+    <select id="listStockInRecordExportData" resultType="com.ruoyi.stock.execl.StockInRecordExportData">
+        SELECT
+        sir.*,
+        p.product_name as product_name,
+        pm.model,
+        pm.unit,
+        u.nick_name as createBy
+        FROM stock_in_record as sir
+        LEFT JOIN product_model as pm on sir.product_model_id = pm.id
+        LEFT JOIN product as p on pm.product_id = p.id
+        LEFT JOIN sys_user as u on sir.create_user = u.user_id
+        <where>
+            <if test="params.timeStr != null and params.timeStr != ''">
+                and sir.create_time like concat('%',#{params.timeStr},'%')
+            </if>
+            <if test="params.productName != null and params.productName != ''">
+                and p.product_name like concat('%',#{params.productName},'%')
+            </if>
+            <if test="params.type != null and params.type != ''">
+                and sir.type = #{params.type}
+            </if>
+            <if test="params.recordType != null and params.recordType != ''">
+                and sir.record_type = #{params.recordType}
+            </if>
+        </where>
+        order by sir.id desc
+    </select>
+</mapper>
\ No newline at end of file
diff --git a/src/main/resources/mapper/stock/StockInventoryMapper.xml b/src/main/resources/mapper/stock/StockInventoryMapper.xml
new file mode 100644
index 0000000..e14b9ce
--- /dev/null
+++ b/src/main/resources/mapper/stock/StockInventoryMapper.xml
@@ -0,0 +1,179 @@
+<?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.StockInventoryMapper">
+
+    <!-- 閫氱敤鏌ヨ鏄犲皠缁撴灉 -->
+    <resultMap id="BaseResultMap" type="com.ruoyi.stock.pojo.StockInventory">
+        <result column="id" property="id"/>
+        <result column="product_model_id" property="productModelId"/>
+        <result column="qualitity" property="qualitity"/>
+        <result column="create_time" property="createTime"/>
+        <result column="update_time" property="updateTime"/>
+        <result column="version" property="version"/>
+        <result column="locked_quantity" property="lockedQuantity"/>
+        <result column="warn_num" property="warnNum"/>
+    </resultMap>
+    <update id="updateAddStockInventory">
+        update stock_inventory
+        <set>
+            <if test="ew.qualitity != null">
+                qualitity = qualitity + #{ew.qualitity},
+            </if>
+            <if test="ew.version != null">
+                version = version + 1,
+            </if>
+            <if test="ew.remark != null and ew.remark !=''">
+                remark = #{ew.remark},
+            </if>
+            update_time = now()
+        </set>
+        where product_model_id = #{ew.productModelId}
+    </update>
+    <update id="updateSubtractStockInventory">
+        update stock_inventory
+        <set>
+            <if test="ew.qualitity != null">
+                qualitity = qualitity - #{ew.qualitity},
+            </if>
+            <if test="ew.version != null">
+                version = version + 1,
+            </if>
+            <if test="ew.remark != null and ew.remark !=''">
+                remark = #{ew.remark},
+            </if>
+            update_time = now()
+        </set>
+        where product_model_id = #{ew.productModelId} and qualitity >= #{ew.qualitity}
+    </update>
+    <select id="pagestockInventory" resultType="com.ruoyi.stock.dto.StockInventoryDto">
+        select si.id,
+        si.qualitity,
+        COALESCE(si.locked_quantity, 0) as locked_quantity,
+        si.product_model_id,
+        si.create_time,
+        si.update_time,
+        si.warn_num,
+        si.version,
+        (si.qualitity - COALESCE(si.locked_quantity, 0)) as un_locked_quantity,
+        pm.model,
+        pm.unit,
+        p.product_name
+        from stock_inventory si
+        left join product_model pm on si.product_model_id = pm.id
+        left join product p on pm.product_id = p.id
+        where 1 = 1
+        <if test="ew.productName != null and ew.productName !=''">
+            and p.product_name like concat('%',#{ew.productName},'%')
+        </if>
+    </select>
+    <select id="listStockInventoryExportData" resultType="com.ruoyi.stock.execl.StockInventoryExportData">
+        select si.qualitity,
+        pm.model,
+        pm.unit,
+        p.product_name,
+        si.remark,
+        si.update_time
+        from stock_inventory si
+        left join product_model pm on si.product_model_id = pm.id
+        left join product p on pm.product_id = p.id
+        where 1 = 1
+        <if test="ew.productName != null and ew.productName !=''">
+            and p.product_name like concat('%',#{ew.productName},'%')
+        </if>
+    </select>
+    <select id="stockInventoryPage" resultType="com.ruoyi.stock.dto.StockInRecordDto">
+        select sir.*,si.qualitity,
+        pm.model,
+        pm.unit,
+        p.product_name
+        from
+        stock_in_record sir
+        left join stock_inventory si on sir.product_model_id = si.product_model_id
+        left join product_model pm on sir.product_model_id = pm.id
+        left join product p on pm.product_id = p.id
+        <where>
+            <if test="ew.reportDate != null">
+                and sir.create_time >= #{ew.reportDate}
+                and sir.create_time &lt; DATE_ADD(#{ew.reportDate}, INTERVAL 1 DAY)
+            </if>
+            <if test="ew.startMonth != null">
+                and sir.create_time &gt;= #{ew.startMonth}
+            </if>
+            <if test="ew.endMonth != null">
+                and sir.create_time &lt;= #{ew.endMonth}
+            </if>
+        </where>
+    </select>
+
+    <select id="stockInAndOutRecord" resultType="com.ruoyi.stock.dto.StockInventoryDto">
+        SELECT
+        pm.model,
+        pm.unit,
+        p.product_name,
+        MAX(current_inventory) as current_stock,
+        SUM(CASE WHEN record_type = 'in' THEN amount ELSE 0 END) as total_stock_in,
+        SUM(CASE WHEN record_type = 'out' THEN amount ELSE 0 END) as total_stock_out
+        FROM (
+        SELECT
+        product_model_id,
+        SUM(qualitity) as current_inventory,
+        0 as amount,
+        '' as record_type
+        FROM stock_inventory
+        GROUP BY product_model_id
+
+        UNION ALL
+
+        SELECT
+        product_model_id,
+        0 as current_inventory,
+        SUM(stock_in_num) as amount,
+        'in' as record_type
+        FROM stock_in_record
+        <where>
+            type = 0
+            <if test="ew.startMonth != null">
+                and stock_in_record.create_time &gt;= #{ew.startMonth}
+            </if>
+            <if test="ew.endMonth != null">
+                and stock_in_record.create_time &lt;= #{ew.endMonth}
+            </if>
+        </where>
+        GROUP BY product_model_id
+
+        UNION ALL
+
+        SELECT
+        product_model_id,
+        0 as current_inventory,
+        SUM(stock_out_num) as amount,
+        'out' as record_type
+        FROM stock_out_record
+        <where>
+            type = 0
+            <if test="ew.startMonth != null">
+                and stock_out_record.create_time &gt;= #{ew.startMonth}
+            </if>
+            <if test="ew.endMonth != null">
+                and stock_out_record.create_time &lt;= #{ew.endMonth}
+            </if>
+        </where>
+        GROUP BY product_model_id
+        ) combined_data
+        LEFT JOIN product_model pm ON pm.id = combined_data.product_model_id
+        LEFT JOIN product p ON p.id = pm.product_id
+        <where>
+            <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>
+        </where>
+        GROUP BY
+        pm.model,
+        pm.unit,
+        p.product_name
+    </select>
+
+</mapper>
diff --git a/src/main/resources/mapper/stock/StockOutRecordMapper.xml b/src/main/resources/mapper/stock/StockOutRecordMapper.xml
new file mode 100644
index 0000000..a441de9
--- /dev/null
+++ b/src/main/resources/mapper/stock/StockOutRecordMapper.xml
@@ -0,0 +1,75 @@
+<?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.StockOutRecordMapper">
+
+    <!-- 閫氱敤鏌ヨ鏄犲皠缁撴灉 -->
+    <resultMap id="BaseResultMap" type="com.ruoyi.stock.pojo.StockOutRecord">
+        <id column="id" property="id" />
+        <result column="outbound_batches" property="outboundBatches" />
+        <result column="stock_out_num" property="stockOutNum" />
+        <result column="record_id" property="recordId" />
+        <result column="record_type" property="recordType" />
+        <result column="product_model_id" property="productModelId" />
+        <result column="remark" property="remark" />
+        <result column="create_time" property="createTime" />
+        <result column="update_time" property="updateTime" />
+        <result column="create_user" property="createUser" />
+        <result column="update_user" property="updateUser" />
+    </resultMap>
+
+    <select id="listPage" resultType="com.ruoyi.stock.dto.StockOutRecordDto">
+        SELECT
+        sor.*,
+        p.product_name as productName,
+        pm.model,
+        pm.unit,
+        u.nick_name as createBy
+        FROM stock_out_record as sor
+        LEFT JOIN product_model as pm on sor.product_model_id = pm.id
+        LEFT JOIN product as p on pm.product_id = p.id
+        LEFT JOIN sys_user as u on sor.create_user = u.user_id
+        <where>
+            <if test="params.timeStr != null and params.timeStr != ''">
+                and sor.create_time like concat('%',#{params.timeStr},'%')
+            </if>
+            <if test="params.productName != null and params.productName != ''">
+                and p.product_name like concat('%',#{params.productName},'%')
+            </if>
+            <if test="params.type != null and params.type != ''">
+                and sor.type = #{params.type}
+            </if>
+            <if test="params.recordType != null and params.recordType != ''">
+                and sor.record_type = #{params.recordType}
+            </if>
+        </where>
+        order by sor.id desc
+    </select>
+    <select id="listStockOutRecordExportData" resultType="com.ruoyi.stock.execl.StockOutRecordExportData">
+        SELECT
+        sor.*,
+        p.product_name as productName,
+        pm.model,
+        pm.unit,
+        u.nick_name as createBy
+        FROM stock_out_record as sor
+        LEFT JOIN product_model as pm on sor.product_model_id = pm.id
+        LEFT JOIN product as p on pm.product_id = p.id
+        LEFT JOIN sys_user as u on sor.create_user = u.user_id
+        <where>
+            <if test="params.timeStr != null and params.timeStr != ''">
+                and sor.create_time like concat('%',#{params.timeStr},'%')
+            </if>
+            <if test="params.productName != null and params.productName != ''">
+                and p.product_name like concat('%',#{params.productName},'%')
+            </if>
+            <if test="params.type != null and params.type != ''">
+                and sor.type = #{params.type}
+            </if>
+            <if test="params.recordType != null and params.recordType != ''">
+                and sor.record_type = #{params.recordType}
+            </if>
+        </where>
+        order by sor.id desc
+    </select>
+
+</mapper>
diff --git a/src/main/resources/mapper/stock/StockUninventoryMapper.xml b/src/main/resources/mapper/stock/StockUninventoryMapper.xml
new file mode 100644
index 0000000..6c5d3be
--- /dev/null
+++ b/src/main/resources/mapper/stock/StockUninventoryMapper.xml
@@ -0,0 +1,81 @@
+<?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.StockUninventoryMapper">
+
+    <!-- 閫氱敤鏌ヨ鏄犲皠缁撴灉 -->
+    <resultMap id="BaseResultMap" type="com.ruoyi.stock.pojo.StockUninventory">
+        <result column="id" property="id" />
+        <result column="product_model_id" property="productModelId" />
+        <result column="qualitity" property="qualitity" />
+        <result column="create_time" property="createTime" />
+        <result column="update_time" property="updateTime" />
+        <result column="version" property="version" />
+    </resultMap>
+    <update id="updateSubtractStockUnInventory">
+        update stock_uninventory
+        <set>
+            <if test="ew.qualitity != null">
+                qualitity = qualitity - #{ew.qualitity},
+            </if>
+            <if test="ew.version != null">
+                version = version + 1,
+            </if>
+            <if test="ew.remark != null and ew.remark !=''">
+                remark = #{ew.remark},
+            </if>
+            update_time = now()
+        </set>
+        where product_model_id = #{ew.productModelId} and qualitity >= #{ew.qualitity}
+    </update>
+    <update id="updateAddStockUnInventory">
+        update stock_uninventory
+        <set>
+            <if test="ew.qualitity != null">
+                qualitity = qualitity + #{ew.qualitity},
+            </if>
+            <if test="ew.version != null">
+                version = version + 1,
+            </if>
+            <if test="ew.remark != null and ew.remark !=''">
+                remark = #{ew.remark},
+            </if>
+            update_time = now()
+        </set>
+        where product_model_id = #{ew.productModelId}
+    </update>
+    <select id="pageStockUninventory" resultType="com.ruoyi.stock.dto.StockUninventoryDto">
+        select su.id,
+        su.qualitity,
+        COALESCE(su.locked_quantity, 0) as locked_quantity,
+        su.product_model_id,
+        su.create_time,
+        su.update_time,
+        su.version,
+        su.update_time,
+        (su.qualitity - COALESCE(su.locked_quantity, 0)) as un_locked_quantity,
+        pm.model,
+        pm.unit,
+        p.product_name
+        from stock_uninventory su
+        left join product_model pm on su.product_model_id = pm.id
+        left join product p on pm.product_id = p.id
+        where 1 = 1
+        <if test="ew.productName != null and ew.productName !=''">
+            and p.product_name like concat('%',#{ew.productName},'%')
+        </if>
+    </select>
+    <select id="listStockInventoryExportData" resultType="com.ruoyi.stock.execl.StockInventoryExportData">
+        select su.*,
+        pm.model,
+        pm.unit,
+        p.product_name
+        from stock_uninventory su
+        left join product_model pm on su.product_model_id = pm.id
+        left join product p on pm.product_id = p.id
+        where 1 = 1
+        <if test="ew.productName != null and ew.productName !=''">
+            and p.product_name like concat('%',#{ew.productName},'%')
+        </if>
+    </select>
+
+</mapper>

--
Gitblit v1.9.3