From 901bab9b8b1c3773a644bdc74f5eafd666591aeb Mon Sep 17 00:00:00 2001
From: liding <756868258@qq.com>
Date: 星期四, 17 九月 2026 13:37:24 +0800
Subject: [PATCH] feat: 1委托单 2下单详情 3检验任务 4检验任务复核 5检验任务详情 6标准维护 7检验单详情 8报告编制

---
 basic-server/src/main/java/com/ruoyi/basic/pojo/StandardMethodAttachment.java                 |   24 
 basic-server/src/main/java/com/ruoyi/basic/controller/StandardMethodController.java           |   25 
 inspect-server/src/main/java/com/ruoyi/inspect/dto/SaveOrderDevicesDto.java                   |   12 
 inspect-server/src/main/java/com/ruoyi/inspect/dto/WriteInspectionConditionDto.java           |   70 +
 inspect-server/src/main/java/com/ruoyi/inspect/service/InsOrderPlanService.java               |   15 
 basic-server/src/main/java/com/ruoyi/basic/utils/StandardTreePathParser.java                  |   78 +
 inspect-server/src/main/java/com/ruoyi/inspect/controller/InsOrderController.java             |    3 
 inspect-server/src/main/java/com/ruoyi/inspect/dto/InsPlaceOrderDto.java                      |    9 
 basic-server/src/main/java/com/ruoyi/basic/dto/StandardLibraryTreeNodeDto.java                |   30 
 basic-server/src/main/java/com/ruoyi/basic/pojo/Laboratory.java                               |    3 
 inspect-server/src/main/java/com/ruoyi/inspect/mapper/InsOrderTemplateDeviceMapper.java       |   29 
 basic-server/src/main/java/com/ruoyi/basic/dto/TemplateBindingItemDto.java                    |    9 
 basic-server/src/main/resources/mapper/StandardMethodMapper.xml                               |   18 
 basic-server/src/main/java/com/ruoyi/basic/service/impl/StandardProductListServiceImpl.java   |  388 ++---
 inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsOrderPlanServiceImpl.java      | 1032 +++++++++++++--
 basic-server/src/main/resources/mapper/StandardTreeMapper.xml                                 |  156 ++
 inspect-server/src/main/java/com/ruoyi/inspect/service/InsOrderService.java                   |    2 
 basic-server/src/main/java/com/ruoyi/basic/mapper/StandardMethodMapper.java                   |    2 
 cnas-require/src/main/java/com/ruoyi/require/service/impl/FeStandardSubstanceServiceImpl.java |   21 
 inspect-server/src/main/resources/mapper/InsSampleMapper.xml                                  |    8 
 basic-server/src/main/java/com/ruoyi/basic/pojo/StandardTree.java                             |    8 
 inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrder.java                             |   20 
 basic-server/src/main/java/com/ruoyi/basic/pojo/StandardMethodIndustry.java                   |   22 
 basic-server/src/main/java/com/ruoyi/basic/utils/StandardTreePath.java                        |   18 
 inspect-server/src/main/java/com/ruoyi/inspect/vo/IfsOrderVO.java                             |    4 
 basic-server/src/main/java/com/ruoyi/basic/dto/AddStandardTreeNodeDto.java                    |   17 
 inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsProduct.java                           |   11 
 basic-server/src/main/java/com/ruoyi/basic/dto/StandardLibraryTreeRowDto.java                 |   30 
 basic-server/src/main/java/com/ruoyi/basic/mapper/StandardTreeTemplateBindingMapper.java      |    8 
 inspect-server/src/main/java/com/ruoyi/inspect/dto/OrderTemplateSelectionDto.java             |   15 
 basic-server/src/main/java/com/ruoyi/basic/pojo/StandardMethod.java                           |   13 
 inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsProductServiceImpl.java        |   91 +
 basic-server/src/main/java/com/ruoyi/basic/utils/StandardTemplateInspectionItemParser.java    |   81 +
 inspect-server/src/main/java/com/ruoyi/inspect/service/InsReportService.java                  |    3 
 basic-server/src/main/java/com/ruoyi/basic/mapper/StandardMethodIndustryMapper.java           |    7 
 inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsReportServiceImpl.java         |   41 
 inspect-server/src/main/java/com/ruoyi/inspect/dto/SaveTemplateContextDto.java                |   14 
 inspect-server/src/main/java/com/ruoyi/inspect/mapper/InsReportMapper.java                    |    4 
 inspect-server/src/main/java/com/ruoyi/inspect/dto/VerifyPlanDto.java                         |   16 
 basic-server/src/main/java/com/ruoyi/basic/dto/StandardTreeTemplateBindingDto.java            |   11 
 basic-server/src/main/java/com/ruoyi/basic/service/StandardMethodService.java                 |   12 
 inspect-server/src/main/java/com/ruoyi/inspect/vo/TemplateConditionVo.java                    |   36 
 basic-server/src/main/java/com/ruoyi/basic/dto/UpdateStandardTreeNodeDto.java                 |   19 
 basic-server/src/main/java/com/ruoyi/basic/service/impl/StandardMethodServiceImpl.java        |  251 +++
 basic-server/src/main/java/com/ruoyi/basic/mapper/StandardTreeMapper.java                     |   35 
 inspect-server/src/main/java/com/ruoyi/inspect/controller/InsReportController.java            |    8 
 inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrderTemplateDevice.java               |   28 
 inspect-server/src/main/resources/mapper/InsOrderMapper.xml                                   |    2 
 inspect-server/src/main/resources/mapper/InsReportMapper.xml                                  |   13 
 inspect-server/src/main/java/com/ruoyi/inspect/vo/OrderTemplateSummaryVo.java                 |   13 
 basic-server/src/main/java/com/ruoyi/basic/service/impl/StandardTemplateServiceImpl.java      |   12 
 inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsOrderServiceImpl.java          |  252 +++
 basic-server/src/main/java/com/ruoyi/basic/pojo/StandardTreeTemplateBinding.java              |   37 
 basic-server/src/main/java/com/ruoyi/basic/mapper/StandardMethodAttachmentMapper.java         |    7 
 basic-server/src/main/java/com/ruoyi/basic/service/StandardTreeService.java                   |   23 
 inspect-server/src/main/java/com/ruoyi/inspect/controller/InsOrderPlanController.java         |   42 
 basic-server/src/main/java/com/ruoyi/basic/dto/StandardTreeTemplateBindingVo.java             |   12 
 basic-server/src/main/java/com/ruoyi/basic/controller/StandardTreeController.java             |   32 
 basic-server/src/main/java/com/ruoyi/basic/service/StandardProductListService.java            |    2 
 inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrderStandardTemplate.java             |   24 
 basic-server/src/main/java/com/ruoyi/basic/dto/TemplateInspectionItemDto.java                 |   12 
 inspect-server/src/main/java/com/ruoyi/inspect/service/InsProductService.java                 |    4 
 basic-server/src/main/java/com/ruoyi/basic/service/impl/StandardTreeServiceImpl.java          |  470 +++++-
 63 files changed, 3,062 insertions(+), 652 deletions(-)

diff --git a/basic-server/src/main/java/com/ruoyi/basic/controller/StandardMethodController.java b/basic-server/src/main/java/com/ruoyi/basic/controller/StandardMethodController.java
index a76b0ea..bf1f960 100644
--- a/basic-server/src/main/java/com/ruoyi/basic/controller/StandardMethodController.java
+++ b/basic-server/src/main/java/com/ruoyi/basic/controller/StandardMethodController.java
@@ -50,4 +50,29 @@
         return Result.success(standardMethodService.upStandardMethod(standardMethod));
     }
 
+    @ApiOperation(value = "鑾峰彇琛屼笟閫夐」")
+    @GetMapping("/selectIndustryOptions")
+    public Result selectIndustryOptions() {
+        return Result.success(standardMethodService.selectIndustryOptions());
+    }
+
+    @ApiOperation(value = "鏌ヨ鏍囧噯闄勪欢")
+    @GetMapping("/selectAttachments")
+    public Result selectAttachments(Integer standardMethodId) {
+        return Result.success(standardMethodService.selectAttachments(standardMethodId));
+    }
+
+    @ApiOperation(value = "涓婁紶鏍囧噯闄勪欢")
+    @PostMapping("/uploadAttachment")
+    public Result uploadAttachment(@RequestParam("standardMethodId") Integer standardMethodId,
+                                   @RequestParam("file") MultipartFile file) {
+        return Result.success(standardMethodService.uploadAttachment(standardMethodId, file));
+    }
+
+    @ApiOperation(value = "鍒犻櫎鏍囧噯闄勪欢")
+    @DeleteMapping("/deleteAttachment")
+    public Result deleteAttachment(Long id) {
+        return Result.success(standardMethodService.deleteAttachment(id));
+    }
+
 }
diff --git a/basic-server/src/main/java/com/ruoyi/basic/controller/StandardTreeController.java b/basic-server/src/main/java/com/ruoyi/basic/controller/StandardTreeController.java
index eaea6c0..70ea6a1 100644
--- a/basic-server/src/main/java/com/ruoyi/basic/controller/StandardTreeController.java
+++ b/basic-server/src/main/java/com/ruoyi/basic/controller/StandardTreeController.java
@@ -4,13 +4,15 @@
 import com.alibaba.fastjson.JSONArray;
 import com.ruoyi.basic.service.*;
 import com.ruoyi.common.core.domain.Result;
+import com.ruoyi.basic.dto.AddStandardTreeNodeDto;
 import com.ruoyi.basic.dto.CopyStandardProductListDto;
-import com.ruoyi.basic.dto.FactoryDto;
 import com.ruoyi.basic.dto.InsSampleReceiveDto;
 import com.ruoyi.basic.dto.ResetTreeDragDTO;
+import com.ruoyi.basic.dto.StandardLibraryTreeNodeDto;
+import com.ruoyi.basic.dto.UpdateStandardTreeNodeDto;
+import com.ruoyi.basic.dto.StandardTreeTemplateBindingDto;
 import com.ruoyi.basic.pojo.StandardProductList;
 import com.ruoyi.basic.pojo.StandardProductListSupplierAsk;
-import com.ruoyi.basic.pojo.StandardTree;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiImplicitParam;
 import io.swagger.annotations.ApiImplicitParams;
@@ -50,8 +52,8 @@
 
     @ApiOperation(value = "娣诲姞鏍囧噯鏍�")
     @PostMapping("/addStandardTree")
-    public Result addStandardTree(@RequestBody StandardTree standardTree) {
-        return Result.success(standardTreeService.addStandardTree(standardTree));
+    public Result addStandardTree(@RequestBody AddStandardTreeNodeDto dto) {
+        return Result.success(standardTreeService.addStandardTree(dto));
     }
 
     @ApiOperation(value = "鏍规嵁鏍囧噯鏍戣繘琛屾爣鍑嗘煡璇�")
@@ -93,8 +95,8 @@
 
     @ApiOperation(value = "閫氳繃妫�楠屾爣鍑嗘煡璇㈡楠岄」鐩�")
     @GetMapping("/selectStandardProductListByMethodId")
-    public Result selectStandardProductListByMethodId(Integer id, String tree) {
-        return Result.success(standardProductListService.selectStandardProductListByMethodId(id, tree));
+    public Result selectStandardProductListByMethodId(Integer id, String tree, Integer page, String keyword) {
+        return Result.success(standardProductListService.selectStandardProductListByMethodId(id, tree, page, keyword));
     }
 
     @ApiOperation(value = "鎵归噺鏌ヨ妫�楠岄」鐩�")
@@ -149,7 +151,7 @@
 
     @ApiOperation(value = "鏍囧噯鏍戞帓搴�")
     @PostMapping("/updateTreeSort")
-    public Result updateTreeSort(@RequestBody List<FactoryDto> list) {
+    public Result updateTreeSort(@RequestBody List<StandardLibraryTreeNodeDto> list) {
         return Result.success(standardTreeService.updateTreeSort(list));
     }
 
@@ -187,8 +189,20 @@
 
     @ApiOperation(value = "淇敼鏍囧噯鏍�")
     @PostMapping("/updateStandardTree")
-    public Result updateStandardTree(@RequestBody StandardTree standardTree) {
-        return Result.success(standardTreeService.updateStandardTree(standardTree));
+    public Result updateStandardTree(@RequestBody UpdateStandardTreeNodeDto dto) {
+        return Result.success(standardTreeService.updateStandardTree(dto));
+    }
+
+    @ApiOperation(value = "鏌ヨ鏍囧噯缁戝畾鐨勫師濮嬭褰曟ā鏉�")
+    @GetMapping("/selectTemplateBindings")
+    public Result selectTemplateBindings(Integer standardTreeId) {
+        return Result.success(standardTreeService.selectTemplateBindings(standardTreeId));
+    }
+
+    @ApiOperation(value = "淇濆瓨鏍囧噯缁戝畾鐨勫師濮嬭褰曟ā鏉�")
+    @PostMapping("/saveTemplateBindings")
+    public Result saveTemplateBindings(@RequestBody StandardTreeTemplateBindingDto dto) {
+        return Result.success(standardTreeService.saveTemplateBindings(dto));
     }
 
     @ApiOperation(value = "妫�楠岄」瑕佹眰鍊煎鍒跺姣�")
diff --git a/basic-server/src/main/java/com/ruoyi/basic/dto/AddStandardTreeNodeDto.java b/basic-server/src/main/java/com/ruoyi/basic/dto/AddStandardTreeNodeDto.java
new file mode 100644
index 0000000..aa184f8
--- /dev/null
+++ b/basic-server/src/main/java/com/ruoyi/basic/dto/AddStandardTreeNodeDto.java
@@ -0,0 +1,17 @@
+package com.ruoyi.basic.dto;
+
+import lombok.Data;
+
+/**
+ * 鏂板鏍囧噯鏍戣妭鐐硅姹�
+ */
+@Data
+public class AddStandardTreeNodeDto {
+
+    /**
+     * 鐖惰妭鐐硅矾寰勶細涓虹┖琛ㄧず鏂板琛屼笟锛涗负鍗曚釜琛屼笟鍚嶇О琛ㄧず鍦ㄨ琛屼笟涓嬫柊澧炴爣鍑�
+     */
+    private String parentTree;
+
+    private String name;
+}
diff --git a/basic-server/src/main/java/com/ruoyi/basic/dto/StandardLibraryTreeNodeDto.java b/basic-server/src/main/java/com/ruoyi/basic/dto/StandardLibraryTreeNodeDto.java
new file mode 100644
index 0000000..6b95316
--- /dev/null
+++ b/basic-server/src/main/java/com/ruoyi/basic/dto/StandardLibraryTreeNodeDto.java
@@ -0,0 +1,30 @@
+package com.ruoyi.basic.dto;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 鏍囧噯搴撳浐瀹氫袱绾ф爲鑺傜偣锛堣涓� -> 鏍囧噯锛�
+ */
+@Data
+public class StandardLibraryTreeNodeDto {
+
+    private Integer id;
+
+    private String label;
+
+    private String value;
+
+    private String code;
+
+    private String sampleEn;
+
+    private Integer standardMethodId;
+
+    private String standardCode;
+
+    private String standardName;
+
+    private List<StandardLibraryTreeNodeDto> children;
+}
diff --git a/basic-server/src/main/java/com/ruoyi/basic/dto/StandardLibraryTreeRowDto.java b/basic-server/src/main/java/com/ruoyi/basic/dto/StandardLibraryTreeRowDto.java
new file mode 100644
index 0000000..c1739b3
--- /dev/null
+++ b/basic-server/src/main/java/com/ruoyi/basic/dto/StandardLibraryTreeRowDto.java
@@ -0,0 +1,30 @@
+package com.ruoyi.basic.dto;
+
+import lombok.Data;
+
+/**
+ * 鏍囧噯搴撲袱绾ф爲鏌ヨ鐨勬墎骞宠缁撴灉锛堣涓� + 鏍囧噯锛�
+ */
+@Data
+public class StandardLibraryTreeRowDto {
+
+    private Integer industryId;
+
+    private String industryName;
+
+    private Integer industrySort;
+
+    private Integer standardId;
+
+    private Integer standardMethodId;
+
+    private String standardCode;
+
+    private String standardName;
+
+    private String standardNameEn;
+
+    private String standardLabel;
+
+    private Integer standardSort;
+}
diff --git a/basic-server/src/main/java/com/ruoyi/basic/dto/StandardTreeTemplateBindingDto.java b/basic-server/src/main/java/com/ruoyi/basic/dto/StandardTreeTemplateBindingDto.java
new file mode 100644
index 0000000..ec0a020
--- /dev/null
+++ b/basic-server/src/main/java/com/ruoyi/basic/dto/StandardTreeTemplateBindingDto.java
@@ -0,0 +1,11 @@
+package com.ruoyi.basic.dto;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class StandardTreeTemplateBindingDto {
+    private Integer standardTreeId;
+    private List<TemplateBindingItemDto> templates;
+}
diff --git a/basic-server/src/main/java/com/ruoyi/basic/dto/StandardTreeTemplateBindingVo.java b/basic-server/src/main/java/com/ruoyi/basic/dto/StandardTreeTemplateBindingVo.java
new file mode 100644
index 0000000..31b0672
--- /dev/null
+++ b/basic-server/src/main/java/com/ruoyi/basic/dto/StandardTreeTemplateBindingVo.java
@@ -0,0 +1,12 @@
+package com.ruoyi.basic.dto;
+
+import lombok.Data;
+
+@Data
+public class StandardTreeTemplateBindingVo {
+    private Integer templateId;
+    private String name;
+    private String number;
+    private String remark;
+    private Integer sort;
+}
diff --git a/basic-server/src/main/java/com/ruoyi/basic/dto/TemplateBindingItemDto.java b/basic-server/src/main/java/com/ruoyi/basic/dto/TemplateBindingItemDto.java
new file mode 100644
index 0000000..3c4381f
--- /dev/null
+++ b/basic-server/src/main/java/com/ruoyi/basic/dto/TemplateBindingItemDto.java
@@ -0,0 +1,9 @@
+package com.ruoyi.basic.dto;
+
+import lombok.Data;
+
+@Data
+public class TemplateBindingItemDto {
+    private Integer templateId;
+    private Integer sort;
+}
diff --git a/basic-server/src/main/java/com/ruoyi/basic/dto/TemplateInspectionItemDto.java b/basic-server/src/main/java/com/ruoyi/basic/dto/TemplateInspectionItemDto.java
new file mode 100644
index 0000000..cb43b8f
--- /dev/null
+++ b/basic-server/src/main/java/com/ruoyi/basic/dto/TemplateInspectionItemDto.java
@@ -0,0 +1,12 @@
+package com.ruoyi.basic.dto;
+
+import lombok.Data;
+
+/** 鍘熷璁板綍妯℃澘涓爣璁颁负鈥滄楠岄」鈥濈殑涓�琛屻�� */
+@Data
+public class TemplateInspectionItemDto {
+    private Integer templateId;
+    private Integer templateSort;
+    private Integer templateRowIndex;
+    private String inspectionItem;
+}
diff --git a/basic-server/src/main/java/com/ruoyi/basic/dto/UpdateStandardTreeNodeDto.java b/basic-server/src/main/java/com/ruoyi/basic/dto/UpdateStandardTreeNodeDto.java
new file mode 100644
index 0000000..d1d9da6
--- /dev/null
+++ b/basic-server/src/main/java/com/ruoyi/basic/dto/UpdateStandardTreeNodeDto.java
@@ -0,0 +1,19 @@
+package com.ruoyi.basic.dto;
+
+import lombok.Data;
+
+/**
+ * 淇敼鏍囧噯鏍戣妭鐐硅姹�
+ */
+@Data
+public class UpdateStandardTreeNodeDto {
+
+    /**
+     * 鐖惰妭鐐硅矾寰勶細涓虹┖琛ㄧず淇敼琛屼笟锛涗负琛屼笟鍚嶇О琛ㄧず淇敼璇ヨ涓氫笅鐨勬爣鍑�
+     */
+    private String parentTree;
+
+    private String oldLabel;
+
+    private String name;
+}
diff --git a/basic-server/src/main/java/com/ruoyi/basic/mapper/StandardMethodAttachmentMapper.java b/basic-server/src/main/java/com/ruoyi/basic/mapper/StandardMethodAttachmentMapper.java
new file mode 100644
index 0000000..7acf48d
--- /dev/null
+++ b/basic-server/src/main/java/com/ruoyi/basic/mapper/StandardMethodAttachmentMapper.java
@@ -0,0 +1,7 @@
+package com.ruoyi.basic.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.basic.pojo.StandardMethodAttachment;
+
+public interface StandardMethodAttachmentMapper extends BaseMapper<StandardMethodAttachment> {
+}
diff --git a/basic-server/src/main/java/com/ruoyi/basic/mapper/StandardMethodIndustryMapper.java b/basic-server/src/main/java/com/ruoyi/basic/mapper/StandardMethodIndustryMapper.java
new file mode 100644
index 0000000..3bfa2c9
--- /dev/null
+++ b/basic-server/src/main/java/com/ruoyi/basic/mapper/StandardMethodIndustryMapper.java
@@ -0,0 +1,7 @@
+package com.ruoyi.basic.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.basic.pojo.StandardMethodIndustry;
+
+public interface StandardMethodIndustryMapper extends BaseMapper<StandardMethodIndustry> {
+}
diff --git a/basic-server/src/main/java/com/ruoyi/basic/mapper/StandardMethodMapper.java b/basic-server/src/main/java/com/ruoyi/basic/mapper/StandardMethodMapper.java
index 8af3fc9..e693de6 100644
--- a/basic-server/src/main/java/com/ruoyi/basic/mapper/StandardMethodMapper.java
+++ b/basic-server/src/main/java/com/ruoyi/basic/mapper/StandardMethodMapper.java
@@ -29,6 +29,8 @@
 
     List<StandardMethodList> selectStandardMethodLists3(@Param("tree") String tree);
 
+    List<StandardMethodList> selectStandardMethodListsByIndustry(@Param("industry") String industry);
+
     List<StandardMethodList> selectListEnum();
 
     Integer getStandardMethodId(@Param("code") String code);
diff --git a/basic-server/src/main/java/com/ruoyi/basic/mapper/StandardTreeMapper.java b/basic-server/src/main/java/com/ruoyi/basic/mapper/StandardTreeMapper.java
index cf27259..77ac5e8 100644
--- a/basic-server/src/main/java/com/ruoyi/basic/mapper/StandardTreeMapper.java
+++ b/basic-server/src/main/java/com/ruoyi/basic/mapper/StandardTreeMapper.java
@@ -24,9 +24,37 @@
 @Mapper
 public interface StandardTreeMapper extends BaseMapper<StandardTree> {
 
-    List<FactoryDto> selectStandardTreeList();
+    List<StandardLibraryTreeRowDto> selectStandardLibraryTree();
 
     List<StandardTree> selectStandardTreeList2(String sampleType);
+
+    /**
+     * 琛屼笟鏀瑰悕锛氱骇鑱旀洿鏂版爣鍑嗗簱閰嶇疆涓殑琛屼笟鍚嶇О锛屽苟閲嶅缓涓ょ骇瑙勮寖璺緞
+     */
+    int updateProductListIndustry(@Param("oldName") String oldName, @Param("newName") String newName);
+
+    /**
+     * 鏍囧噯鏀瑰悕锛氱骇鑱旀洿鏂版爣鍑嗗簱閰嶇疆涓殑鏍囧噯鍚嶇О锛屽苟閲嶅缓涓ょ骇瑙勮寖璺緞
+     */
+    int updateProductListStandard(@Param("industry") String industry, @Param("oldName") String oldName, @Param("newName") String newName);
+
+    /**
+     * 琛屼笟鏀瑰悕锛氱骇鑱旀洿鏂版湭瀹屾垚璁㈠崟鐨勮涓氬悕绉�
+     */
+    int updateInsOrderIndustry(@Param("oldName") String oldName, @Param("newName") String newName);
+
+    int updateInsSampleIndustry(@Param("oldName") String oldName, @Param("newName") String newName);
+
+    int updateInsProductIndustry(@Param("oldName") String oldName, @Param("newName") String newName);
+
+    /**
+     * 鏍囧噯鏀瑰悕锛氱骇鑱旀洿鏂版湭瀹屾垚璁㈠崟鐨勬爣鍑嗗悕绉�
+     */
+    int updateInsOrderStandard(@Param("industry") String industry, @Param("oldName") String oldName, @Param("newName") String newName);
+
+    int updateInsSampleStandard(@Param("industry") String industry, @Param("oldName") String oldName, @Param("newName") String newName);
+
+    int updateInsProductStandard(@Param("industry") String industry, @Param("oldName") String oldName, @Param("newName") String newName);
 
     StandardProductList selectStandardProductById(Integer id);
 
@@ -36,6 +64,11 @@
 
     List<StandardProductList> selectStandardProductListByTree2(@Param("tree") String tree, @Param("sample") String sample, @Param("model") String model, @Param("trees") String trees, @Param("laboratory") String laboratory);
 
+    /**
+     * 鎸夎涓氭煡璇㈠熀纭�妫�楠岄」锛岀敓鎴愪袱绾ф爣鍑嗛厤缃紙sample=鏍囧噯銆乵odel=NULL銆乼ree=瑙勮寖璺緞锛�
+     */
+    List<StandardProductList> selectStandardProductListByIndustry(@Param("industry") String industry, @Param("standard") String standard, @Param("tree") String tree);
+
 
     List<SampleTypeDto> getStandardTree2();
 
diff --git a/basic-server/src/main/java/com/ruoyi/basic/mapper/StandardTreeTemplateBindingMapper.java b/basic-server/src/main/java/com/ruoyi/basic/mapper/StandardTreeTemplateBindingMapper.java
new file mode 100644
index 0000000..366cd3b
--- /dev/null
+++ b/basic-server/src/main/java/com/ruoyi/basic/mapper/StandardTreeTemplateBindingMapper.java
@@ -0,0 +1,8 @@
+package com.ruoyi.basic.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.basic.pojo.StandardTreeTemplateBinding;
+
+/** 鏍囧噯妯℃澘缁戝畾 Mapper銆� */
+public interface StandardTreeTemplateBindingMapper extends BaseMapper<StandardTreeTemplateBinding> {
+}
diff --git a/basic-server/src/main/java/com/ruoyi/basic/pojo/Laboratory.java b/basic-server/src/main/java/com/ruoyi/basic/pojo/Laboratory.java
index 92226bc..dc4c0f5 100644
--- a/basic-server/src/main/java/com/ruoyi/basic/pojo/Laboratory.java
+++ b/basic-server/src/main/java/com/ruoyi/basic/pojo/Laboratory.java
@@ -39,6 +39,9 @@
     @ApiModelProperty(value = "鍦板潃")
     private String address;
 
+    @ApiModelProperty(value = "琛屼笟鎺掑簭")
+    private Integer sort;
+
     @ApiModelProperty(value = "鍒涘缓浜�")
     @TableField(fill = FieldFill.INSERT)
     private Integer createUser;
diff --git a/basic-server/src/main/java/com/ruoyi/basic/pojo/StandardMethod.java b/basic-server/src/main/java/com/ruoyi/basic/pojo/StandardMethod.java
index 6d5ec08..e123057 100644
--- a/basic-server/src/main/java/com/ruoyi/basic/pojo/StandardMethod.java
+++ b/basic-server/src/main/java/com/ruoyi/basic/pojo/StandardMethod.java
@@ -7,6 +7,7 @@
 
 import java.io.Serializable;
 import java.time.LocalDateTime;
+import java.util.List;
 
 /**
  * 鏍囧噯鏂规硶
@@ -26,6 +27,18 @@
     @ApiModelProperty("妫�楠屽璞�")
     private String structureTestObjectId;
 
+    /** 鏍囧噯搴撲竴绾ц涓欼D锛堜粎鎺ュ彛浜や簰锛屼笉钀絪tandard_method琛級 */
+    @TableField(exist = false)
+    private List<Integer> industryIds;
+
+    /** 宸茬粦瀹氳涓氬悕绉帮紙鍒楄〃灞曠ず锛� */
+    @TableField(exist = false)
+    private String industryNames;
+
+    /** 闄勪欢鏁伴噺锛堝垪琛ㄥ睍绀猴級 */
+    @TableField(exist = false)
+    private Integer attachmentCount;
+
     /**
      * 鏍囧噯缂栧彿
      */
diff --git a/basic-server/src/main/java/com/ruoyi/basic/pojo/StandardMethodAttachment.java b/basic-server/src/main/java/com/ruoyi/basic/pojo/StandardMethodAttachment.java
new file mode 100644
index 0000000..8dd9007
--- /dev/null
+++ b/basic-server/src/main/java/com/ruoyi/basic/pojo/StandardMethodAttachment.java
@@ -0,0 +1,24 @@
+package com.ruoyi.basic.pojo;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import java.time.LocalDateTime;
+
+@Data
+@TableName("standard_method_attachment")
+public class StandardMethodAttachment {
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    private Integer standardMethodId;
+    private String fileName;
+    private String fileUrl;
+    private Integer sort;
+    @TableField(fill = FieldFill.INSERT)
+    private Integer createUser;
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Integer updateUser;
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+}
diff --git a/basic-server/src/main/java/com/ruoyi/basic/pojo/StandardMethodIndustry.java b/basic-server/src/main/java/com/ruoyi/basic/pojo/StandardMethodIndustry.java
new file mode 100644
index 0000000..3ef8ebf
--- /dev/null
+++ b/basic-server/src/main/java/com/ruoyi/basic/pojo/StandardMethodIndustry.java
@@ -0,0 +1,22 @@
+package com.ruoyi.basic.pojo;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import java.time.LocalDateTime;
+
+@Data
+@TableName("standard_method_industry")
+public class StandardMethodIndustry {
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    private Integer standardMethodId;
+    private Integer industryId;
+    @TableField(fill = FieldFill.INSERT)
+    private Integer createUser;
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Integer updateUser;
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+}
diff --git a/basic-server/src/main/java/com/ruoyi/basic/pojo/StandardTree.java b/basic-server/src/main/java/com/ruoyi/basic/pojo/StandardTree.java
index 8c2b6cf..fd113c1 100644
--- a/basic-server/src/main/java/com/ruoyi/basic/pojo/StandardTree.java
+++ b/basic-server/src/main/java/com/ruoyi/basic/pojo/StandardTree.java
@@ -62,6 +62,14 @@
     @Length(max= 255,message="缂栫爜闀垮害涓嶈兘瓒呰繃255")
     private String model;
     /**
+    * 鏍囧噯鎺掑簭
+    */
+    @ApiModelProperty("鏍囧噯鎺掑簭")
+    private Integer sort;
+
+    /** 鏍囧噯缁存姢ID锛涜鑼冨寲浜岀骇鏍囧噯浣跨敤 */
+    private Integer standardMethodId;
+    /**
     *
     */
     @ApiModelProperty("")
diff --git a/basic-server/src/main/java/com/ruoyi/basic/pojo/StandardTreeTemplateBinding.java b/basic-server/src/main/java/com/ruoyi/basic/pojo/StandardTreeTemplateBinding.java
new file mode 100644
index 0000000..f653b69
--- /dev/null
+++ b/basic-server/src/main/java/com/ruoyi/basic/pojo/StandardTreeTemplateBinding.java
@@ -0,0 +1,37 @@
+package com.ruoyi.basic.pojo;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/** 鏍囧噯锛堜簩绾ц妭鐐癸級涓庡師濮嬭褰曟ā鏉跨殑缁戝畾鍏崇郴銆� */
+@Data
+@TableName("standard_tree_template_binding")
+public class StandardTreeTemplateBinding {
+
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+
+    private Integer standardTreeId;
+
+    private Integer templateId;
+
+    private Integer sort;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Integer createUser;
+
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Integer updateUser;
+
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+}
diff --git a/basic-server/src/main/java/com/ruoyi/basic/service/StandardMethodService.java b/basic-server/src/main/java/com/ruoyi/basic/service/StandardMethodService.java
index b6dbea4..d0cde18 100644
--- a/basic-server/src/main/java/com/ruoyi/basic/service/StandardMethodService.java
+++ b/basic-server/src/main/java/com/ruoyi/basic/service/StandardMethodService.java
@@ -5,6 +5,8 @@
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.ruoyi.basic.pojo.StandardMethod;
 import com.ruoyi.basic.pojo.StandardMethodList;
+import com.ruoyi.basic.pojo.StandardMethodAttachment;
+import com.ruoyi.basic.pojo.Laboratory;
 import org.springframework.web.multipart.MultipartFile;
 
 import java.io.IOException;
@@ -22,7 +24,7 @@
 
     List<StandardMethod> selectStandardMethods();
 
-    int addStandardMethod(StandardMethod standardMethod);
+    Integer addStandardMethod(StandardMethod standardMethod);
 
     int delStandardMethod(Integer id);
 
@@ -32,4 +34,12 @@
 
     List<StandardMethodList> selectStandardMethodEnum();
 
+    List<StandardMethodAttachment> selectAttachments(Integer standardMethodId);
+
+    StandardMethodAttachment uploadAttachment(Integer standardMethodId, MultipartFile file);
+
+    boolean deleteAttachment(Long id);
+
+    List<Laboratory> selectIndustryOptions();
+
 }
diff --git a/basic-server/src/main/java/com/ruoyi/basic/service/StandardProductListService.java b/basic-server/src/main/java/com/ruoyi/basic/service/StandardProductListService.java
index 00b4d25..a750f5b 100644
--- a/basic-server/src/main/java/com/ruoyi/basic/service/StandardProductListService.java
+++ b/basic-server/src/main/java/com/ruoyi/basic/service/StandardProductListService.java
@@ -31,7 +31,7 @@
      * @param tree 鏍囧噯鏍戣矾寰勶紝浣跨敤 {@code " - "} 鍒嗛殧灞傜骇
      * @return {@code productList} 涓虹敓鎴愬悗鐨勬楠岄」鐩垪琛紝{@code total} 涓哄垪琛ㄦ暟閲�
      */
-    Map<String, Object> selectStandardProductListByMethodId(Integer id, String tree);
+    Map<String, Object> selectStandardProductListByMethodId(Integer id, String tree, Integer page, String keyword);
 
     IPage<StandardProductList> selectStandardProductByMethodId(Integer id, String tree, Integer page, String laboratory, String item, String items);
 
diff --git a/basic-server/src/main/java/com/ruoyi/basic/service/StandardTreeService.java b/basic-server/src/main/java/com/ruoyi/basic/service/StandardTreeService.java
index 4083e8c..aca97aa 100644
--- a/basic-server/src/main/java/com/ruoyi/basic/service/StandardTreeService.java
+++ b/basic-server/src/main/java/com/ruoyi/basic/service/StandardTreeService.java
@@ -1,8 +1,13 @@
 package com.ruoyi.basic.service;
 
 import com.baomidou.mybatisplus.extension.service.IService;
-import com.ruoyi.basic.dto.FactoryDto;
+import com.ruoyi.basic.dto.AddStandardTreeNodeDto;
 import com.ruoyi.basic.dto.SampleTypeDto;
+import com.ruoyi.basic.dto.StandardLibraryTreeNodeDto;
+import com.ruoyi.basic.dto.UpdateStandardTreeNodeDto;
+import com.ruoyi.basic.dto.StandardTreeTemplateBindingDto;
+import com.ruoyi.basic.dto.StandardTreeTemplateBindingVo;
+import com.ruoyi.basic.dto.TemplateInspectionItemDto;
 import com.ruoyi.basic.pojo.StandardTree;
 import org.springframework.web.multipart.MultipartFile;
 
@@ -16,10 +21,10 @@
 */
 public interface StandardTreeService extends IService<StandardTree> {
 
-    List<FactoryDto> selectStandardTreeList();
+    List<StandardLibraryTreeNodeDto> selectStandardTreeList();
 
 
-    int addStandardTree(StandardTree standardTree);
+    int addStandardTree(AddStandardTreeNodeDto dto);
 
     int delStandardTree(String tree);
 
@@ -28,11 +33,17 @@
     int upStandardProducts(Map<String, Object> product);
 
     /**
-     * 鏍囧噯鏁版帓搴�
+     * 鏍囧噯鏍戞帓搴�
      * @param list
      * @return
      */
-    boolean updateTreeSort(List<FactoryDto> list);
+    boolean updateTreeSort(List<StandardLibraryTreeNodeDto> list);
 
-    int updateStandardTree(StandardTree standardTree);
+    int updateStandardTree(UpdateStandardTreeNodeDto dto);
+
+    List<StandardTreeTemplateBindingVo> selectTemplateBindings(Integer standardTreeId);
+
+    boolean saveTemplateBindings(StandardTreeTemplateBindingDto dto);
+
+    List<TemplateInspectionItemDto> selectTemplateInspectionItems(Integer standardTreeId);
 }
diff --git a/basic-server/src/main/java/com/ruoyi/basic/service/impl/StandardMethodServiceImpl.java b/basic-server/src/main/java/com/ruoyi/basic/service/impl/StandardMethodServiceImpl.java
index df3cd31..67128b6 100644
--- a/basic-server/src/main/java/com/ruoyi/basic/service/impl/StandardMethodServiceImpl.java
+++ b/basic-server/src/main/java/com/ruoyi/basic/service/impl/StandardMethodServiceImpl.java
@@ -12,21 +12,36 @@
 import com.ruoyi.basic.mapper.StandardMethodMapper;
 import com.ruoyi.basic.mapper.StandardProductListMapper;
 import com.ruoyi.basic.mapper.StructureItemParameterMapper;
+import com.ruoyi.basic.mapper.StandardMethodIndustryMapper;
+import com.ruoyi.basic.mapper.StandardMethodAttachmentMapper;
+import com.ruoyi.basic.mapper.LaboratoryMapper;
+import com.ruoyi.basic.mapper.StandardTreeMapper;
+import com.ruoyi.basic.mapper.StandardTreeTemplateBindingMapper;
 import com.ruoyi.basic.pojo.StandardMethod;
+import com.ruoyi.basic.pojo.StandardMethodIndustry;
+import com.ruoyi.basic.pojo.StandardMethodAttachment;
+import com.ruoyi.basic.pojo.Laboratory;
+import com.ruoyi.basic.pojo.StandardTree;
+import com.ruoyi.basic.pojo.StandardTreeTemplateBinding;
 import com.ruoyi.basic.pojo.StandardProductList;
 import com.ruoyi.basic.pojo.StructureItemParameter;
 import com.ruoyi.basic.service.StandardMethodService;
 import com.ruoyi.basic.service.StandardProductListService;
 import com.ruoyi.basic.service.StructureItemParameterService;
-import lombok.AllArgsConstructor;
+import com.ruoyi.basic.utils.StandardTreePath;
+import com.ruoyi.basic.utils.StandardTreePathParser;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.multipart.MultipartFile;
+import com.ruoyi.common.exception.base.BaseException;
 
+import java.io.File;
 import java.io.IOException;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
 import java.util.*;
 import java.util.concurrent.CompletableFuture;
-import java.util.stream.Collectors;
 
 /**
  * @author Administrator
@@ -34,7 +49,6 @@
  * @createDate 2024-03-03 19:21:41
  */
 @Service
-@AllArgsConstructor
 public class StandardMethodServiceImpl extends ServiceImpl<StandardMethodMapper, StandardMethod>
         implements StandardMethodService {
 
@@ -47,9 +61,51 @@
     private StructureItemParameterMapper structureItemParameterMapper;
     private StructureItemParameterService structureItemParameterService;
 
+    private StandardMethodIndustryMapper standardMethodIndustryMapper;
+    private StandardMethodAttachmentMapper standardMethodAttachmentMapper;
+    private LaboratoryMapper laboratoryMapper;
+    private StandardTreeMapper standardTreeMapper;
+    private StandardTreeTemplateBindingMapper standardTreeTemplateBindingMapper;
+
+    @Value("${file.path}")
+    private String filePath;
+
+    @Value("${wordUrl}")
+    private String wordUrl;
+
+    /**
+     * 涓氬姟渚濊禆鐢辨瀯閫犲櫒娉ㄥ叆锛涙枃浠惰矾寰勭敱 @Value 浠庨厤缃鍙栵紝涓嶈兘鍙備笌 Lombok 鐨勫叏鍙傛瀯閫犲櫒锛�
+     * 鍚﹀垯 Spring 浼氭妸 String 璇涓洪渶瑕佹敞鍏ョ殑 Bean銆�
+     */
+    public StandardMethodServiceImpl(StandardMethodMapper standardMethodMapper,
+                                     StandardProductListMapper standardProductListMapper,
+                                     StandardProductListService standardProductListService,
+                                     StructureItemParameterMapper structureItemParameterMapper,
+                                     StructureItemParameterService structureItemParameterService,
+                                     StandardMethodIndustryMapper standardMethodIndustryMapper,
+                                     StandardMethodAttachmentMapper standardMethodAttachmentMapper,
+                                     LaboratoryMapper laboratoryMapper,
+                                     StandardTreeMapper standardTreeMapper,
+                                     StandardTreeTemplateBindingMapper standardTreeTemplateBindingMapper) {
+        this.standardMethodMapper = standardMethodMapper;
+        this.standardProductListMapper = standardProductListMapper;
+        this.standardProductListService = standardProductListService;
+        this.structureItemParameterMapper = structureItemParameterMapper;
+        this.structureItemParameterService = structureItemParameterService;
+        this.standardMethodIndustryMapper = standardMethodIndustryMapper;
+        this.standardMethodAttachmentMapper = standardMethodAttachmentMapper;
+        this.laboratoryMapper = laboratoryMapper;
+        this.standardTreeMapper = standardTreeMapper;
+        this.standardTreeTemplateBindingMapper = standardTreeTemplateBindingMapper;
+    }
+
     @Override
     public IPage<StandardMethod> selectStandardMethodList(Page page, StandardMethod standardMethod) {
-        return standardMethodMapper.selectStandardMethodList(page, QueryWrappers.queryWrappers(standardMethod));
+        IPage<StandardMethod> result = standardMethodMapper.selectStandardMethodList(page, QueryWrappers.queryWrappers(standardMethod));
+        for (StandardMethod method : result.getRecords()) {
+            method.setIndustryIds(selectIndustryIds(method.getId()));
+        }
+        return result;
     }
 
     @Override
@@ -58,25 +114,167 @@
     }
 
     @Override
-    public int addStandardMethod(StandardMethod standardMethod) {
-        int insert = standardMethodMapper.insert(standardMethod);
-        return insert;
+    @Transactional(rollbackFor = Exception.class)
+    public Integer addStandardMethod(StandardMethod standardMethod) {
+        standardMethod.setStructureTestObjectId(null);
+        standardMethodMapper.insert(standardMethod);
+        syncIndustries(standardMethod, Collections.<Integer>emptySet());
+        return standardMethod.getId();
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public int delStandardMethod(Integer id) {
-        int i = standardMethodMapper.deleteById(id);
-        return i;
+        StandardMethod method = standardMethodMapper.selectById(id);
+        if (method == null) {
+            throw new BaseException("鏍囧噯涓嶅瓨鍦�");
+        }
+        for (Integer industryId : selectIndustryIds(id)) {
+            removeIndustryTree(method, industryId);
+        }
+        standardMethodIndustryMapper.delete(Wrappers.<StandardMethodIndustry>lambdaQuery()
+                .eq(StandardMethodIndustry::getStandardMethodId, id));
+        standardMethodAttachmentMapper.delete(Wrappers.<StandardMethodAttachment>lambdaQuery()
+                .eq(StandardMethodAttachment::getStandardMethodId, id));
+        return standardMethodMapper.deleteById(id);
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public int upStandardMethod(StandardMethod standardMethod) {
         StandardMethod oldStandardMethod = standardMethodMapper.selectById(standardMethod.getId());
+        if (oldStandardMethod == null) {
+            throw new BaseException("鏍囧噯涓嶅瓨鍦�");
+        }
         if (!oldStandardMethod.getCode().equals(standardMethod.getCode())) {
             CompletableFuture.supplyAsync(() -> replaceMethod(oldStandardMethod.getCode(), standardMethod.getCode()));
         }
         int i = standardMethodMapper.updateById(standardMethod);
+        syncIndustries(standardMethod, new LinkedHashSet<>(selectIndustryIds(oldStandardMethod.getId())));
         return i;
+    }
+
+    private List<Integer> selectIndustryIds(Integer methodId) {
+        List<StandardMethodIndustry> list = standardMethodIndustryMapper.selectList(
+                Wrappers.<StandardMethodIndustry>lambdaQuery()
+                        .eq(StandardMethodIndustry::getStandardMethodId, methodId));
+        List<Integer> ids = new ArrayList<>();
+        for (StandardMethodIndustry item : list) {
+            ids.add(item.getIndustryId());
+        }
+        return ids;
+    }
+
+    private void syncIndustries(StandardMethod method, Set<Integer> oldIds) {
+        Set<Integer> newIds = method.getIndustryIds() == null ? new LinkedHashSet<Integer>()
+                : new LinkedHashSet<>(method.getIndustryIds());
+        for (Integer industryId : newIds) {
+            if (industryId == null || laboratoryMapper.selectById(industryId) == null) {
+                throw new BaseException("琛屼笟涓嶅瓨鍦�");
+            }
+        }
+        for (Integer oldId : oldIds) {
+            if (!newIds.contains(oldId)) {
+                removeIndustryTree(method, oldId);
+                standardMethodIndustryMapper.delete(Wrappers.<StandardMethodIndustry>lambdaQuery()
+                        .eq(StandardMethodIndustry::getStandardMethodId, method.getId())
+                        .eq(StandardMethodIndustry::getIndustryId, oldId));
+            }
+        }
+        for (Integer industryId : newIds) {
+            Laboratory lab = laboratoryMapper.selectById(industryId);
+            Long count = standardMethodIndustryMapper.selectCount(Wrappers.<StandardMethodIndustry>lambdaQuery()
+                    .eq(StandardMethodIndustry::getStandardMethodId, method.getId())
+                    .eq(StandardMethodIndustry::getIndustryId, industryId));
+            if (count == 0) {
+                StandardMethodIndustry relation = new StandardMethodIndustry();
+                relation.setStandardMethodId(method.getId());
+                relation.setIndustryId(industryId);
+                standardMethodIndustryMapper.insert(relation);
+            }
+            StandardTree tree = standardTreeMapper.selectOne(Wrappers.<StandardTree>lambdaQuery()
+                    .eq(StandardTree::getLaboratory, lab.getLaboratoryName())
+                    .eq(StandardTree::getStandardMethodId, method.getId())
+                    .isNull(StandardTree::getSampleType).isNull(StandardTree::getModel));
+            if (tree == null) {
+                tree = new StandardTree();
+                tree.setLaboratory(lab.getLaboratoryName());
+                tree.setSample(buildLabel(method));
+                tree.setStandardMethodId(method.getId());
+                tree.setSort(0);
+                standardTreeMapper.insert(tree);
+            } else if (!buildLabel(method).equals(tree.getSample())) {
+                StandardTree update = new StandardTree();
+                update.setId(tree.getId());
+                update.setSample(buildLabel(method));
+                standardTreeMapper.updateById(update);
+            }
+        }
+    }
+
+    private void removeIndustryTree(StandardMethod method, Integer industryId) {
+        Laboratory lab = laboratoryMapper.selectById(industryId);
+        if (lab == null) return;
+        StandardTree tree = standardTreeMapper.selectOne(Wrappers.<StandardTree>lambdaQuery()
+                .eq(StandardTree::getLaboratory, lab.getLaboratoryName())
+                .eq(StandardTree::getStandardMethodId, method.getId())
+                .isNull(StandardTree::getSampleType).isNull(StandardTree::getModel));
+        if (tree != null) {
+            standardTreeTemplateBindingMapper.delete(Wrappers.<StandardTreeTemplateBinding>lambdaQuery()
+                    .eq(StandardTreeTemplateBinding::getStandardTreeId, tree.getId()));
+            standardProductListMapper.delete(Wrappers.<StandardProductList>lambdaQuery()
+                    .eq(StandardProductList::getLaboratory, lab.getLaboratoryName())
+                    .eq(StandardProductList::getSample, tree.getSample())
+                    .isNull(StandardProductList::getSampleType).isNull(StandardProductList::getModel));
+            standardTreeMapper.deleteById(tree.getId());
+        }
+    }
+
+    private String buildLabel(StandardMethod method) {
+        return method.getCode().trim();
+    }
+
+    @Override
+    public List<StandardMethodAttachment> selectAttachments(Integer standardMethodId) {
+        return standardMethodAttachmentMapper.selectList(Wrappers.<StandardMethodAttachment>lambdaQuery()
+                .eq(StandardMethodAttachment::getStandardMethodId, standardMethodId)
+                .orderByAsc(StandardMethodAttachment::getSort, StandardMethodAttachment::getId));
+    }
+
+    @Override
+    public StandardMethodAttachment uploadAttachment(Integer standardMethodId, MultipartFile file) {
+        if (standardMethodMapper.selectById(standardMethodId) == null) throw new BaseException("鏍囧噯涓嶅瓨鍦�");
+        if (file == null || file.isEmpty() || file.getSize() > 10 * 1024 * 1024) throw new BaseException("闄勪欢涓嶈兘涓虹┖涓斾笉鑳借秴杩�10MB");
+        String originalName = file.getOriginalFilename();
+        if (originalName == null || !originalName.matches("(?i).+\\.(jpg|jpeg|png|gif|doc|docx|xls|xlsx|ppt|pptx|pdf|zip|rar)$")) throw new BaseException("涓嶆敮鎸佺殑闄勪欢鏍煎紡");
+        try {
+            boolean image = originalName.matches("(?i).+\\.(jpg|jpeg|png|gif)$");
+            File directory = new File(image ? filePath : wordUrl);
+            if (!directory.exists()) directory.mkdirs();
+            String fileUrl = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyMMddHHmmssSSS")) + "-" + originalName;
+            file.transferTo(new File(directory, fileUrl));
+            StandardMethodAttachment attachment = new StandardMethodAttachment();
+            attachment.setStandardMethodId(standardMethodId);
+            attachment.setFileName(originalName);
+            attachment.setFileUrl(fileUrl);
+            attachment.setSort(selectAttachments(standardMethodId).size());
+            standardMethodAttachmentMapper.insert(attachment);
+            return attachment;
+        } catch (IOException e) {
+            throw new BaseException("闄勪欢涓婁紶澶辫触");
+        }
+    }
+
+    @Override
+    public boolean deleteAttachment(Long id) {
+        return standardMethodAttachmentMapper.deleteById(id) > 0;
+    }
+
+    @Override
+    public List<Laboratory> selectIndustryOptions() {
+        return laboratoryMapper.selectList(Wrappers.<Laboratory>lambdaQuery()
+                .select(Laboratory::getId, Laboratory::getLaboratoryName, Laboratory::getSort)
+                .orderByAsc(Laboratory::getSort, Laboratory::getId));
     }
 
     //缂栬緫method鍚庡叏閮ㄦ浛鎹�
@@ -172,33 +370,16 @@
         if (tree == null || tree.trim().isEmpty()) {
             return Collections.emptyMap();
         }
-        String[] trees = tree.split(" - ");
-        Map<String, List<?>> map = new HashMap<>();
-        String str = "";
-        List<StandardMethodList> standardMethodLists = new ArrayList<>();
-        switch (trees.length){
-            case 5:
-                str += "\"" + trees[2] + "\",\"" + trees[3] + "\",\"" + trees[4] + "\"";
-                standardMethodLists.addAll(standardMethodMapper.selectStandardMethodLists(str));
-                standardMethodLists.addAll(standardMethodMapper.selectStandardMethodLists("\"" + trees[2] + "\",\"" + trees[3] + "\""));
-                standardMethodLists.addAll(standardMethodMapper.selectStandardMethodLists("\"" + trees[2] + "\""));
-                break;
-            case 4:
-                str += "\"" + trees[2] + "\",\"" + trees[3] + "\"";
-                standardMethodLists.addAll(standardMethodMapper.selectStandardMethodLists(str));
-                standardMethodLists.addAll(standardMethodMapper.selectStandardMethodLists("\"" + trees[2] + "\""));
-                break;
-            case 3:
-                str += "\"" + trees[2] + "\"";
-                standardMethodLists.addAll(standardMethodMapper.selectStandardMethodLists3(str));
-                break;
-            default:
-                map.put("standardMethodList", null);
-                return map;
+        StandardTreePath path = StandardTreePathParser.parse(tree);
+        String industry = path.getIndustry();
+        List<StandardMethodList> standardMethodLists = new ArrayList<>(standardMethodMapper.selectStandardMethodListsByIndustry(industry));
+        // 鎸� ID 鍘婚噸锛屼繚鎸佺ǔ瀹氭帓搴�
+        Map<Integer, StandardMethodList> byId = new LinkedHashMap<>();
+        for (StandardMethodList m : standardMethodLists) {
+            byId.putIfAbsent(m.getId(), m);
         }
-        standardMethodLists.addAll(standardMethodMapper.selectStandardMethodListsByNull(str));
-        List<StandardMethodList> collect = standardMethodLists.stream().distinct().collect(Collectors.toList());
-        map.put("standardMethodList", collect);
+        Map<String, List<?>> map = new HashMap<>();
+        map.put("standardMethodList", new ArrayList<>(byId.values()));
         return map;
     }
 
diff --git a/basic-server/src/main/java/com/ruoyi/basic/service/impl/StandardProductListServiceImpl.java b/basic-server/src/main/java/com/ruoyi/basic/service/impl/StandardProductListServiceImpl.java
index 53da251..356fe9a 100644
--- a/basic-server/src/main/java/com/ruoyi/basic/service/impl/StandardProductListServiceImpl.java
+++ b/basic-server/src/main/java/com/ruoyi/basic/service/impl/StandardProductListServiceImpl.java
@@ -1,7 +1,6 @@
 package com.ruoyi.basic.service.impl;
 
 import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.JSONArray;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.*;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -10,7 +9,6 @@
 import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.basic.dto.CopyStandardProductListDto;
 import com.ruoyi.basic.dto.InsSampleReceiveDto;
-import com.ruoyi.basic.dto.ProductDto;
 import com.ruoyi.basic.dto.ResetTreeDragDTO;
 import com.ruoyi.basic.mapper.IfsInventoryQuantityMapper;
 import com.ruoyi.basic.mapper.StandardProductListMapper;
@@ -18,8 +16,9 @@
 import com.ruoyi.basic.mapper.StandardTreeMapper;
 import com.ruoyi.basic.pojo.StandardProductList;
 import com.ruoyi.basic.pojo.StandardProductListSupplierAsk;
-import com.ruoyi.basic.pojo.StandardTree;
 import com.ruoyi.basic.service.StandardProductListService;
+import com.ruoyi.basic.utils.StandardTreePath;
+import com.ruoyi.basic.utils.StandardTreePathParser;
 import lombok.AllArgsConstructor;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -57,13 +56,36 @@
 
     @Override
     public List<StandardProductList> selectStandardProductList(InsSampleReceiveDto insSample) {
-        // 鏄惁娌℃湁浜у搧
+        if (insSample == null || insSample.getStandardMethodListId() == null) {
+            throw new BaseException("妫�楠屾爣鍑嗕笉鑳戒负绌�");
+        }
+        // 鏂扮枂鏂拌仛涓ょ骇鏍戜紭鍏堟寜鈥滆涓� + 鏍囧噯鈥濊鑼冭矾寰勬煡璇紱鏃犳硶瑙f瀽鏃朵繚鐣欏巻鍙蹭簲绾ч�昏緫銆�
+        boolean twoLevelOrder = false;
         boolean isNoSample = false;
-//        String[] models = insSample.getModel().split("-(?=[^-]*$)");//鎷嗗垎鏈�鍚庝竴涓��-銆�
         String model = insSample.getModel();
         String modelNum = insSample.getModelNum();
-        List<StandardProductList> list = standardProductListMapper.selectDetail(insSample.getStandardMethodListId(), 1, model, insSample.getIsCableTag());
-        if (list.size() == 0) {
+        List<StandardProductList> list = Collections.emptyList();
+        if (StringUtils.isNotBlank(insSample.getFactory())) {
+            try {
+                StandardTreePath path = StandardTreePathParser.parse(insSample.getFactory());
+                if (path.getStandard() != null) {
+                    String canonicalTree = StandardTreePathParser.canonical(path.getIndustry(), path.getStandard());
+                    list = standardProductListMapper.selectDetail2(
+                            insSample.getStandardMethodListId(), 1, canonicalTree, insSample.getIsCableTag());
+                    insSample.setLaboratory(path.getIndustry());
+                    insSample.setSampleType(null);
+                    insSample.setSample(path.getStandard());
+                    insSample.setModel(null);
+                    twoLevelOrder = true;
+                }
+            } catch (BaseException ignored) {
+                // 鍘嗗彶浜旂骇璺緞缁х画璧板師鏈夊吋瀹规煡璇€��
+            }
+        }
+        if (!twoLevelOrder) {
+            list = standardProductListMapper.selectDetail(insSample.getStandardMethodListId(), 1, model, insSample.getIsCableTag());
+        }
+        if (!twoLevelOrder && list.size() == 0) {
             if (Objects.equals(insSample.getFactory(), "") || insSample.getFactory() == null) {
                 return null;
             }
@@ -85,8 +107,8 @@
                 }
             }
         }
-        String[] split1 = insSample.getFactory().split(" - ");
-        if (!isNoSample) {
+        if (!twoLevelOrder && !isNoSample) {
+            String[] split1 = insSample.getFactory().split(" - ");
             //鍒ゆ柇闀垮害
             if (split1.length > 4) {
                 if (ObjectUtils.isNotEmpty(split1[3])) {
@@ -96,9 +118,14 @@
                 }
             }
         }
+        final boolean currentTwoLevelOrder = twoLevelOrder;
         list = list.stream().filter(a -> {
                     try {
                         if (a.getSection() != null && !Objects.equals(a.getSection(), "")) {
+                            // 涓ょ骇涓嬪崟涓嶅啀杈撳叆鍨嬪彿锛屼笉鑳藉洜鏃犳硶璁$畻鍨嬪彿鍖洪棿鑰屼涪寮冩暣涓楠岄」銆�
+                            if (currentTwoLevelOrder && StringUtils.isBlank(modelNum)) {
+                                return true;
+                            }
                             List<String> sections = JSON.parseArray(a.getSection(), String.class);// 鍖洪棿
                             List<String> cores = JSON.parseArray(a.getCores(), String.class); // 鑺暟
                             List<String> conductorMaterials = JSON.parseArray(a.getConductorMaterial(), String.class); // 瀵间綋鏉愯川
@@ -256,234 +283,151 @@
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public Map<String, Object> selectStandardProductListByMethodId(Integer id, String tree) {
-        // 灏嗏�滃伐鍘� - 瀹為獙瀹� - 鏍峰搧绫诲埆 - 鏍峰搧 - 鍨嬪彿鈥濊矾寰勬媶鎴愬悇绾ц妭鐐广��
-        String[] trees = tree.split(" - ");
-        // 鍥涚骇鍙婁互涓婅妭鐐归渶瑕佹寜鏁版嵁搴撲腑宸蹭繚瀛樼殑 sort 鎺掑簭杩斿洖銆�
-        boolean isDrag = false;
-        // 瀛樻斁鏍规嵁缁撴瀯鍙傛暟琛ㄩ噸鏂扮敓鎴愮殑妫�楠岄」鐩��
-        List<StandardProductList> list = new ArrayList<>();
-        if (trees.length == 3) {
-            // 涓夌骇鑺傜偣鍙畾浣嶅埌鏍峰搧绫诲埆锛岄渶瑕佺户缁睍寮�璇ョ被鍒笅鐨勬牱鍝佸拰鍨嬪彿銆�
-            List<StandardTree> treeList = new ArrayList<>();
-            StandardTree standardTree = new StandardTree();
-            standardTree.setFactory(trees[0]);
-            standardTree.setLaboratory(trees[1]);
-            standardTree.setSampleType(trees[2]);
-            // 鏌ヨ璇ユ牱鍝佺被鍒叧鑱旂殑浜у搧锛堟牱鍝侊級鍚嶇О銆�
-            List<ProductDto> pList = standardTreeMapper.selectPList(trees[2]);
-            if (pList.isEmpty() || pList.get(0) == null) {
-                // 娌℃湁浜у搧鏃讹紝鐩存帴浣跨敤璇ュ疄楠屽鍜屾牱鍝佺被鍒凡閰嶇疆鐨勬爣鍑嗘爲鑺傜偣銆�
-                List<StandardTree> treeList1 = standardTreeMapper.selectList(Wrappers.<StandardTree>lambdaQuery().eq(StandardTree::getLaboratory, trees[1]).eq(StandardTree::getSampleType, trees[2]));
-                if (treeList1.isEmpty()) {
-                    // 鏍囧噯鏍戜篃涓嶅瓨鍦ㄦ椂锛屼繚鐣欎笁绾ц妭鐐逛綔涓哄厹搴曟煡璇㈡潯浠躲��
-                    treeList.add(standardTree);
-                } else {
-                    treeList.addAll(treeList1);
-                }
-            } else {
-                // 鎸変骇鍝侀�愪釜鏌ユ壘宸查厤缃殑鍨嬪彿锛涙湭閰嶇疆鏃跺垱寤轰竴涓彧鏈夋牱鍝佷俊鎭殑鍏滃簳鑺傜偣銆�
-                for (ProductDto p : pList) {
-                    standardTree.setSample(p.getName());
-                    List<StandardTree> treeList1 = standardTreeMapper.selectList(Wrappers.<StandardTree>lambdaQuery().eq(StandardTree::getLaboratory, trees[1]).eq(StandardTree::getSampleType, trees[2]).eq(StandardTree::getSample, p.getName()));
-                    if (treeList1.isEmpty()) {
-                        treeList.add(JSON.parseObject(JSON.toJSONString(standardTree), StandardTree.class));
-                    } else {
-                        treeList.addAll(treeList1);
-                    }
-                }
-            }
-            // 涓烘瘡涓睍寮�鍚庣殑鈥滄牱鍝� + 鍨嬪彿鈥濊妭鐐规煡璇㈤�傜敤鐨勬楠岄」鐩��
-            for (StandardTree standardTree2 : treeList) {
-                String tree2 = trees[0] + " - " + trees[1] + " - " + trees[2] + " - " + standardTree2.getSample() + " - " + standardTree2.getModel();
-                // 鏌ヨ閫傜敤浜庢牱鍝佺被鍒殑閫氱敤椤圭洰鍙婂綋鍓嶆牱鍝�/鍨嬪彿鐨勯」鐩��
-                list.addAll(standardTreeMapper.selectStandardProductListByTree("\"" + trees[2] + "\"", standardTree2.getSample(), standardTree2.getModel(), tree2, trees[1]));
-                // 鏌ヨ鍚屾椂闄愬畾鏍峰搧绫诲埆鍜屾牱鍝佸悕绉扮殑椤圭洰銆�
-                list.addAll(standardTreeMapper.selectStandardProductListByTree2("\"" + trees[2] + "\",\"" + standardTree2.getSample() + "\"", standardTree2.getSample(), standardTree2.getModel(), tree2, trees[1]));
-            }
-        } else if (trees.length == 4) {
-            // 鍥涚骇鑺傜偣宸插畾浣嶅埌鏍峰搧锛屽悗缁寜宸蹭繚瀛樼殑 sort 鎺掑簭杩斿洖銆�
-            isDrag = true;
-            // 鏌ヨ璇ユ牱鍝佸凡閰嶇疆鐨勫瀷鍙疯妭鐐广��
-            List<StandardTree> treeList = standardTreeMapper.selectList(Wrappers.<StandardTree>lambdaQuery().eq(StandardTree::getLaboratory, trees[1]).eq(StandardTree::getSampleType, trees[2]).eq(StandardTree::getSample, trees[3]));
-            if (treeList.isEmpty()) {
-                // 娌℃湁鍨嬪彿閰嶇疆鏃讹紝鍒涘缓涓嶅惈鍨嬪彿鐨勮妭鐐癸紝浠嶅彲鏌ヨ閫氱敤椤圭洰銆�
-                StandardTree standardTree = new StandardTree();
-                standardTree.setFactory(trees[0]);
-                standardTree.setLaboratory(trees[1]);
-                standardTree.setSampleType(trees[2]);
-                standardTree.setSample(trees[3]);
-                treeList.add(standardTree);
-            }
-            // 閽堝鏍峰搧鐨勬瘡涓瀷鍙凤紝鍚堝苟閫氱敤椤圭洰鍜屾牱鍝佷笓灞為」鐩��
-            for (StandardTree standardTree : treeList) {
-                String str = tree + " - " + standardTree.getModel();
-                list.addAll(standardTreeMapper.selectStandardProductListByTree("\"" + trees[2] + "\"", standardTree.getSample(), standardTree.getModel(), str, trees[1]));
-                list.addAll(standardTreeMapper.selectStandardProductListByTree2("\"" + trees[2] + "\",\"" + trees[3] + "\"", standardTree.getSample(), standardTree.getModel(), str, trees[1]));
-            }
-        } else {
-            // 浜旂骇鑺傜偣宸叉槑纭牱鍝佸拰鍨嬪彿锛岀洿鎺ユ煡璇㈣鑺傜偣閫傜敤鐨勯」鐩��
-            isDrag = true;
-            list.addAll(standardTreeMapper.selectStandardProductListByTree("\"" + trees[2] + "\"", trees[3].equals("null") ? null : trees[3], trees[4], tree, trees[1]));
-            list.addAll(standardTreeMapper.selectStandardProductListByTree2("\"" + trees[2] + "\",\"" + trees[3] + "\"", trees[3].equals("null") ? null : trees[3], trees[4], tree, trees[1]));
+    public Map<String, Object> selectStandardProductListByMethodId(Integer id, String tree, Integer page, String keyword) {
+        if (id == null) {
+            throw new BaseException("妫�楠屾爣鍑嗕笉鑳戒负绌�");
         }
-        // 涓烘柊鐢熸垚鐨勯」鐩鍏堝垎閰嶉洩鑺� ID锛涘尮閰嶅埌鏃ч厤缃悗浼氭浛鎹负鏃ц褰� ID銆�
-        for (StandardProductList productList : list) {
-            productList.setId(IdWorker.getId());
+        StandardTreePath path = StandardTreePathParser.parse(tree);
+        if (path.getStandard() == null) {
+            throw new BaseException("璇烽�夋嫨鏍囧噯鑺傜偣");
         }
-        List<StandardProductList> standardProductLists;
+        String canonicalTree = StandardTreePathParser.canonical(path.getIndustry(), path.getStandard());
+        List<StandardProductList> generated = standardTreeMapper.selectStandardProductListByIndustry(
+                path.getIndustry(), path.getStandard(), canonicalTree);
+        List<StandardProductList> saved = standardProductListMapper.selectList(
+                Wrappers.<StandardProductList>lambdaQuery()
+                        .eq(StandardProductList::getStandardMethodListId, id)
+                        .eq(StandardProductList::getTree, canonicalTree)
+                        .orderByAsc(StandardProductList::getSort));
 
-        if (isDrag) {
-            // 鏌ヨ褰撳墠鑺傜偣鍙婂叾瀛愯妭鐐圭殑鏃ч厤缃紝骞舵寜 sort 璇诲彇浠ヤ繚鐣欐嫋鎷藉悗鐨勯『搴忋��
-            standardProductLists = standardProductListMapper.selectList(Wrappers.<StandardProductList>lambdaQuery()
-                    .eq(StandardProductList::getStandardMethodListId, id)
-                    .like(StandardProductList::getTree, tree)
-                    .orderByAsc(StandardProductList::getSort));
-        } else {
-            // 涓夌骇鑺傜偣娌℃湁鎷栨嫿椤哄簭锛屽彧璇诲彇鐢ㄤ簬瑕嗙洊榛樿鍊肩殑鏃ч厤缃��
-            standardProductLists = standardProductListMapper.selectList(Wrappers.<StandardProductList>lambdaQuery()
-                    .eq(StandardProductList::getStandardMethodListId, id)
-                    .like(StandardProductList::getTree, tree));
-        }
-
-        // 灏嗘棫椤圭洰涓汉宸ョ淮鎶ょ殑閰嶇疆瑕嗙洊鍒版湰娆¢噸鏂扮敓鎴愮殑瀵瑰簲椤圭洰銆�
-        for (StandardProductList sp : standardProductLists) {
-            for (StandardProductList pl : list) {
-                // 鍗婂緞鍊欓�夊�煎彧鏈変竴涓椂锛屽皢鍏朵綔涓洪粯璁ゅ崐寰勩��
-                String radiusList = pl.getRadiusList();
-                if (StringUtils.isNotBlank(radiusList) && !radiusList.equals("null") && !radiusList.equals("\"\"")) {
-                    JSONArray jsonArray = JSON.parseArray(radiusList);
-                    List<String> radius = jsonArray.toJavaList(String.class);
-                    if (CollectionUtils.isNotEmpty(radius) && radius.size() == 1) {
-                        pl.setRadius(radius.get(0));
-                    }
-                }
-                // 浠ユ楠岄」鐩�佸瓙椤广�佸瀷鍙枫�佹牱鍝佸拰缁撴瀯鍙傛暟 ID 鍒ゆ柇鏂版棫椤圭洰鏄惁瀵瑰簲銆�
-                if (Objects.equals(sp.getInspectionItem(), pl.getInspectionItem())
-                        && Objects.equals((sp.getInspectionItemSubclass() == null) ? "" : sp.getInspectionItemSubclass(), pl.getInspectionItemSubclass() == null ? "" : pl.getInspectionItemSubclass())
-//                        && Objects.equals(sp.getSample(), pl.getSample())
-                        && Objects.equals(sp.getModel(), pl.getModel())
-                        && sp.getTree().indexOf(pl.getSample() == null ? "null" : pl.getSample()) > -1
-                        && Objects.equals(sp.getStructureItemParameterId(), pl.getStructureItemParameterId())) {
-                    // 澶嶇敤鏃� ID 鍜屾帓搴忥紝纭繚鏈鍚屾鍚庡師鏈夐厤缃粛鍏宠仈鍒板悓涓�鏉¢」鐩褰曘��
-                    pl.setId(sp.getId());
-                    pl.setSort(sp.getSort());
-                    // 鏃ц褰曞瓨鍦ㄥ�兼椂浼樺厛淇濈暀锛涚姸鎬佷负绌烘椂鎸夋爣鍑� ID 璁剧疆榛樿鐘舵�併��
-                    if (sp.getState() != null && !sp.getState().equals("")) {
-                        pl.setState(sp.getState());
-                    } else {
-                        pl.setState(id == 0 ? 1 : 0);
-                    }
-                    if (sp.getMethodS() != null && !sp.getMethodS().equals("")) {
-                        pl.setMethod(sp.getMethodS());
-                    }
-                    if (sp.getRadius() != null && !sp.getRadius().equals("")) {
-                        pl.setRadius(sp.getRadius());
-                    }
-                    if (sp.getRates() != null && !sp.getRates().equals("")) {
-                        pl.setRates(sp.getRates());
-                    }
-                    if (sp.getAsk() != null && !sp.getAsk().equals("")) {
-                        pl.setAsk(sp.getAsk());
-                    }
-                    if (sp.getTell() != null && !sp.getTell().equals("")) {
-                        pl.setTell(sp.getTell());
-                    }
-                    if (sp.getPrice() != null && !sp.getPrice().equals("")) {
-                        pl.setPrice(sp.getPrice());
-                    }
-                    if (sp.getManHour() != null && !sp.getManHour().equals("")) {
-                        pl.setManHour(sp.getManHour());
-                    }
-                    if (sp.getSection() != null && !sp.getSection().equals("")) {
-                        pl.setSection(sp.getSection());
-                    }
-                    if (sp.getCores() != null && !sp.getCores().equals("")) {
-                        pl.setCores(sp.getCores());
-                    }
-                    if (sp.getConductorMaterial() != null && !sp.getConductorMaterial().equals("")) {
-                        pl.setConductorMaterial(sp.getConductorMaterial());
-                    }
-                    if (sp.getConductorType() != null && !sp.getConductorType().equals("")) {
-                        pl.setConductorType(sp.getConductorType());
-                    }
-                    if (sp.getTemplateId() != null && !sp.getTemplateId().equals("")) {
-                        pl.setTemplateId(sp.getTemplateId());
-                    }
-                    break;
-                }
+        Map<Integer, StandardProductList> savedByParameterId = new HashMap<>();
+        for (StandardProductList item : saved) {
+            if (item.getStructureItemParameterId() != null) {
+                savedByParameterId.put(item.getStructureItemParameterId(), item);
             }
         }
-
-        // 璁板綍鏈鍚屾鐨勫垱寤轰汉鍜屾洿鏂颁汉銆�
         Integer userId = SecurityUtils.getUserId().intValue();
-        if (trees.length == 5) {
-            // 浜旂骇鑺傜偣鍙噸寤哄綋鍓嶇簿纭矾寰勭殑鏁版嵁銆�
-            standardProductListMapper.delete(Wrappers.<StandardProductList>lambdaUpdate()
-                    .eq(StandardProductList::getStandardMethodListId, id)
-                    .eq(StandardProductList::getTree, tree));
-        } else {
-            // 涓夌骇銆佸洓绾ц妭鐐归噸寤哄綋鍓嶈矾寰勫強鍏舵墍鏈夊瓙鑺傜偣鐨勬暟鎹��
-            standardProductListMapper.delete(Wrappers.<StandardProductList>lambdaUpdate()
-                    .eq(StandardProductList::getStandardMethodListId, id)
-                    .like(StandardProductList::getTree, tree));
-        }
-
-        // 琛ラ綈鎵归噺淇濆瓨鎵�闇�鐨勬爲鑺傜偣銆佺敤鎴峰拰妫�楠屾爣鍑嗗瓧娈点��
-        List<StandardProductList> productLists = list.stream().map(a -> {
-            a.setFactory(trees[0]);
-            a.setLaboratory(trees[1]);
-            a.setSampleType(trees[2]);
-            a.setCreateUser(userId);
-            a.setUpdateUser(userId);
-            a.setStandardMethodListId(id);
-            return a;
-        }).collect(Collectors.toList());
-        if (CollectionUtils.isNotEmpty(productLists)) {
-            // 鎵归噺鍐欏叆閲嶆柊鐢熸垚鍚庣殑妫�楠岄」鐩��
-            baseMapper.saveBatchProductLists(productLists);
-        }
-
-        // 鍏堟寜宸ユ椂鍒嗙粍涓殑鏁板瓧鍗囧簭鎺掑垪锛涚┖鍒嗙粍鎺掑埌鏈�鍚庛��
-        Collections.sort(list, (o1, o2) -> {
-            String field1 = o1.getManHourGroup();
-            String field2 = o2.getManHourGroup();
-
-            boolean isEmpty1 = field1 == null || field1.isEmpty();
-            boolean isEmpty2 = field2 == null || field2.isEmpty();
-
-            if (isEmpty1 && isEmpty2) {
-                return 0;
-            } else if (isEmpty1) {
-                return 1;
-            } else if (isEmpty2) {
-                return -1;
+        for (StandardProductList item : generated) {
+            applySingleRadiusDefault(item);
+            StandardProductList old = savedByParameterId.get(item.getStructureItemParameterId());
+            if (old != null && sameInspectionItem(old, item)) {
+                mergeSavedConfiguration(old, item);
             } else {
-                int num1 = extractNumber(field1);
-                int num2 = extractNumber(field2);
-                return Integer.compare(num1, num2);
+                item.setId(IdWorker.getId());
+                item.setState(id == 0 ? 1 : 0);
             }
-        });
-        if (isDrag) {
-            // 鍥涚骇銆佷簲绾ц妭鐐规渶缁堟寜宸蹭繚瀛樼殑鎷栨嫿鎺掑簭瑕嗙洊宸ユ椂鍒嗙粍鎺掑簭銆�
-            list.sort((o1, o2) -> (o1.getSort() == null ? 0 : o1.getSort())
-                    - (o2.getSort() == null ? 0 : o2.getSort()));
+            item.setFactory(null);
+            item.setLaboratory(path.getIndustry());
+            item.setSampleType(null);
+            item.setSample(path.getStandard());
+            item.setModel(null);
+            item.setTree(canonicalTree);
+            item.setCreateUser(userId);
+            item.setUpdateUser(userId);
+            item.setStandardMethodListId(id);
         }
 
-        // 鎸夊墠绔害瀹氳繑鍥為」鐩垪琛ㄥ拰鏁伴噺銆�
+        standardProductListMapper.delete(Wrappers.<StandardProductList>lambdaQuery()
+                .eq(StandardProductList::getStandardMethodListId, id)
+                .eq(StandardProductList::getTree, canonicalTree));
+        if (CollectionUtils.isNotEmpty(generated)) {
+            baseMapper.saveBatchProductLists(generated);
+        }
+
+        generated.sort((left, right) -> {
+            int leftSort = left.getSort() == null ? Integer.MAX_VALUE : left.getSort();
+            int rightSort = right.getSort() == null ? Integer.MAX_VALUE : right.getSort();
+            if (leftSort != rightSort) {
+                return Integer.compare(leftSort, rightSort);
+            }
+            return compareManHourGroup(left.getManHourGroup(), right.getManHourGroup());
+        });
+
+        String search = keyword == null ? "" : keyword.trim().toLowerCase(Locale.ROOT);
+        List<StandardProductList> filtered = generated.stream()
+                .filter(item -> search.isEmpty()
+                        || containsIgnoreCase(item.getInspectionItem(), search)
+                        || containsIgnoreCase(item.getInspectionItemSubclass(), search))
+                .collect(Collectors.toList());
+        int total = filtered.size();
+        int currentPage = page == null || page < 1 ? 1 : page;
+        int fromIndex = Math.min((currentPage - 1) * 100, total);
+        int toIndex = Math.min(fromIndex + 100, total);
+
         Map<String, Object> map = new HashMap<>();
-        map.put("productList", list);
-        map.put("total", list.size());
+        map.put("productList", new ArrayList<>(filtered.subList(fromIndex, toIndex)));
+        map.put("total", total);
         return map;
     }
 
-    private int extractNumber(String s) {
-        // 浠庡瓧绗︿覆涓彁鍙栨暟瀛楃殑閫昏緫锛岃繖閲屽亣璁惧瓧娈电殑鏍煎紡鏄� "text<number>"
-        String number = s;
-        if (!s.matches("\\d+")) {
-            number = s.replaceAll("\\D", "");
+    private boolean sameInspectionItem(StandardProductList left, StandardProductList right) {
+        return Objects.equals(left.getInspectionItem(), right.getInspectionItem())
+                && Objects.equals(defaultString(left.getInspectionItemSubclass()), defaultString(right.getInspectionItemSubclass()));
+    }
+
+    private void applySingleRadiusDefault(StandardProductList item) {
+        String radiusList = item.getRadiusList();
+        if (StringUtils.isBlank(radiusList) || "null".equals(radiusList) || "\"\"".equals(radiusList)) {
+            return;
         }
-        return Integer.parseInt(number);
+        try {
+            List<String> values = JSON.parseArray(radiusList, String.class);
+            if (values != null && values.size() == 1) {
+                item.setRadius(values.get(0));
+            }
+        } catch (Exception ignored) {
+            // 鍘嗗彶鑴忔暟鎹笉褰卞搷妫�楠岄」鍒楄〃鐢熸垚銆�
+        }
+    }
+
+    private void mergeSavedConfiguration(StandardProductList old, StandardProductList target) {
+        target.setId(old.getId());
+        target.setSort(old.getSort());
+        target.setState(old.getState() == null ? 0 : old.getState());
+        if (StringUtils.isNotBlank(old.getMethodS())) {
+            target.setMethodS(old.getMethodS());
+            target.setMethod(old.getMethodS());
+        }
+        if (StringUtils.isNotBlank(old.getRadius())) target.setRadius(old.getRadius());
+        if (StringUtils.isNotBlank(old.getRates())) target.setRates(old.getRates());
+        if (StringUtils.isNotBlank(old.getAsk())) target.setAsk(old.getAsk());
+        if (StringUtils.isNotBlank(old.getTell())) target.setTell(old.getTell());
+        if (StringUtils.isNotBlank(old.getPrice())) target.setPrice(old.getPrice());
+        if (StringUtils.isNotBlank(old.getManHour())) target.setManHour(old.getManHour());
+        if (StringUtils.isNotBlank(old.getSection())) target.setSection(old.getSection());
+        if (StringUtils.isNotBlank(old.getCores())) target.setCores(old.getCores());
+        if (StringUtils.isNotBlank(old.getConductorMaterial())) target.setConductorMaterial(old.getConductorMaterial());
+        if (StringUtils.isNotBlank(old.getConductorType())) target.setConductorType(old.getConductorType());
+        if (old.getTemplateId() != null) target.setTemplateId(old.getTemplateId());
+    }
+
+    private int compareManHourGroup(String left, String right) {
+        boolean leftEmpty = StringUtils.isBlank(left);
+        boolean rightEmpty = StringUtils.isBlank(right);
+        if (leftEmpty && rightEmpty) return 0;
+        if (leftEmpty) return 1;
+        if (rightEmpty) return -1;
+        return Integer.compare(extractNumber(left), extractNumber(right));
+    }
+
+    private boolean containsIgnoreCase(String value, String search) {
+        return value != null && value.toLowerCase(Locale.ROOT).contains(search);
+    }
+
+    private String defaultString(String value) {
+        return value == null ? "" : value;
+    }
+
+    private int extractNumber(String s) {
+        String number = s == null ? "" : s.replaceAll("\\D", "");
+        if (number.isEmpty()) {
+            return Integer.MAX_VALUE;
+        }
+        try {
+            return Integer.parseInt(number);
+        } catch (NumberFormatException ignored) {
+            return Integer.MAX_VALUE;
+        }
     }
 
     @Override
diff --git a/basic-server/src/main/java/com/ruoyi/basic/service/impl/StandardTemplateServiceImpl.java b/basic-server/src/main/java/com/ruoyi/basic/service/impl/StandardTemplateServiceImpl.java
index 5278795..5e7530a 100644
--- a/basic-server/src/main/java/com/ruoyi/basic/service/impl/StandardTemplateServiceImpl.java
+++ b/basic-server/src/main/java/com/ruoyi/basic/service/impl/StandardTemplateServiceImpl.java
@@ -7,7 +7,9 @@
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.ruoyi.basic.mapper.StandardTemplateMapper;
+import com.ruoyi.basic.mapper.StandardTreeTemplateBindingMapper;
 import com.ruoyi.basic.pojo.StandardTemplate;
+import com.ruoyi.basic.pojo.StandardTreeTemplateBinding;
 import com.ruoyi.basic.service.StandardTemplateService;
 import com.ruoyi.common.numgen.NumberGenerator;
 import com.ruoyi.common.utils.GZipUtil;
@@ -29,6 +31,8 @@
     implements StandardTemplateService{
 
     private StandardTemplateMapper standardTemplateMapper;
+
+    private StandardTreeTemplateBindingMapper standardTreeTemplateBindingMapper;
 
 
     private final NumberGenerator<StandardTemplate> numberGenerator;
@@ -71,12 +75,18 @@
         if (count > 0) {
             throw new RuntimeException("璇ユā鏉垮凡缁戝畾妫�楠岄」鐩弬鏁帮紝绂佹鍒犻櫎");
         }
+        Long bindingCount = standardTreeTemplateBindingMapper.selectCount(Wrappers.<StandardTreeTemplateBinding>lambdaQuery()
+                .eq(StandardTreeTemplateBinding::getTemplateId, id));
+        if (bindingCount > 0) {
+            throw new RuntimeException("璇ユā鏉垮凡缁戝畾鏍囧噯搴擄紝绂佹鍒犻櫎");
+        }
         return standardTemplateMapper.deleteById(id);
     }
 
     @Override
     public List<StandardTemplate> getStandardTemplate() {
-        return standardTemplateMapper.selectList(Wrappers.<StandardTemplate>lambdaQuery().select(StandardTemplate::getId,StandardTemplate::getName));
+        return standardTemplateMapper.selectList(Wrappers.<StandardTemplate>lambdaQuery()
+                .select(StandardTemplate::getId, StandardTemplate::getName, StandardTemplate::getNumber, StandardTemplate::getRemark));
     }
 
     @Override
diff --git a/basic-server/src/main/java/com/ruoyi/basic/service/impl/StandardTreeServiceImpl.java b/basic-server/src/main/java/com/ruoyi/basic/service/impl/StandardTreeServiceImpl.java
index 51bd971..280d3c5 100644
--- a/basic-server/src/main/java/com/ruoyi/basic/service/impl/StandardTreeServiceImpl.java
+++ b/basic-server/src/main/java/com/ruoyi/basic/service/impl/StandardTreeServiceImpl.java
@@ -1,32 +1,50 @@
 package com.ruoyi.basic.service.impl;
 
-import cn.hutool.core.collection.CollUtil;
 import com.alibaba.fastjson.JSON;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
-import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
 import com.baomidou.mybatisplus.core.toolkit.StringUtils;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.ruoyi.basic.dto.FactoryDto;
-import com.ruoyi.basic.dto.LaboratoryDto;
+import com.ruoyi.basic.dto.AddStandardTreeNodeDto;
 import com.ruoyi.basic.dto.SampleTypeDto;
+import com.ruoyi.basic.dto.StandardLibraryTreeNodeDto;
+import com.ruoyi.basic.dto.StandardLibraryTreeRowDto;
+import com.ruoyi.basic.dto.UpdateStandardTreeNodeDto;
+import com.ruoyi.basic.dto.StandardTreeTemplateBindingDto;
+import com.ruoyi.basic.dto.StandardTreeTemplateBindingVo;
+import com.ruoyi.basic.dto.TemplateBindingItemDto;
+import com.ruoyi.basic.dto.TemplateInspectionItemDto;
+import com.ruoyi.basic.mapper.LaboratoryMapper;
 import com.ruoyi.basic.mapper.StandardProductListMapper;
 import com.ruoyi.basic.mapper.StandardTreeMapper;
+import com.ruoyi.basic.mapper.StandardTreeTemplateBindingMapper;
+import com.ruoyi.basic.mapper.StructureItemParameterMapper;
+import com.ruoyi.basic.mapper.StructureTestObjectMapper;
+import com.ruoyi.basic.pojo.Laboratory;
 import com.ruoyi.basic.pojo.StandardProductList;
 import com.ruoyi.basic.pojo.StandardTree;
+import com.ruoyi.basic.pojo.StandardTemplate;
+import com.ruoyi.basic.pojo.StandardTreeTemplateBinding;
+import com.ruoyi.basic.pojo.StructureItemParameter;
 import com.ruoyi.basic.pojo.StructureTestObject;
-import com.ruoyi.basic.service.StandardProductListService;
 import com.ruoyi.basic.service.StandardTreeService;
-import com.ruoyi.basic.service.StructureTestObjectService;
+import com.ruoyi.basic.service.StandardTemplateService;
+import com.ruoyi.basic.utils.StandardTreePath;
+import com.ruoyi.basic.utils.StandardTreePathParser;
+import com.ruoyi.basic.utils.StandardTemplateInspectionItemParser;
 import com.ruoyi.common.exception.base.BaseException;
 import lombok.AllArgsConstructor;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.util.ArrayList;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
+import java.util.HashSet;
+import java.util.Comparator;
+import java.util.Set;
 
 /**
  * @author Administrator
@@ -43,62 +61,155 @@
 
     private StandardProductListMapper standardProductListMapper;
 
-    private StandardProductListService standardProductListService;
+    private StructureItemParameterMapper structureItemParameterMapper;
 
-    private StructureTestObjectService structureTestObjectService;
+    private StructureTestObjectMapper structureTestObjectMapper;
+
+    private LaboratoryMapper laboratoryMapper;
+
+    private StandardTreeTemplateBindingMapper standardTreeTemplateBindingMapper;
+
+    private StandardTemplateService standardTemplateService;
 
 
     @Override
-    public List<FactoryDto> selectStandardTreeList() {
-        List<FactoryDto> factoryDtos = standardTreeMapper.selectStandardTreeList();
-        for (FactoryDto factoryDto : factoryDtos) {
-            for (LaboratoryDto laboratoryDto : factoryDto.getChildren()) {
-                laboratoryDto.getChildren().sort((o1, o2) -> (o1.getSort() == null ? 0 : o1.getSort())
-                        - (o2.getSort() == null ? 0 : o2.getSort()));
-                for (SampleTypeDto sampleTypeDto : laboratoryDto.getChildren()) {
-//                    if (sampleTypeDto.getChildren().size() == 0) {
-                    sampleTypeDto.getChildren().addAll(standardTreeMapper.getStandardTree3(sampleTypeDto.getValue()));
-//                    }
-                }
+    public List<StandardLibraryTreeNodeDto> selectStandardTreeList() {
+        List<StandardLibraryTreeRowDto> rows = standardTreeMapper.selectStandardLibraryTree();
+        List<StandardLibraryTreeNodeDto> industries = new ArrayList<>();
+        Map<Integer, StandardLibraryTreeNodeDto> industryMap = new LinkedHashMap<>();
+        for (StandardLibraryTreeRowDto row : rows) {
+            StandardLibraryTreeNodeDto industry = industryMap.get(row.getIndustryId());
+            if (industry == null) {
+                industry = new StandardLibraryTreeNodeDto();
+                industry.setId(row.getIndustryId());
+                industry.setLabel(row.getIndustryName());
+                industry.setValue(row.getIndustryName());
+                industry.setCode("[1]");
+                industry.setChildren(new ArrayList<>());
+                industryMap.put(row.getIndustryId(), industry);
+                industries.add(industry);
+            }
+            if (row.getStandardId() != null) {
+                StandardLibraryTreeNodeDto standard = new StandardLibraryTreeNodeDto();
+                standard.setId(row.getStandardId());
+                standard.setLabel(row.getStandardCode());
+                standard.setValue(row.getStandardLabel());
+                standard.setCode("[2]");
+                standard.setSampleEn(row.getStandardNameEn());
+                standard.setStandardMethodId(row.getStandardMethodId());
+                standard.setStandardCode(row.getStandardCode());
+                standard.setStandardName(row.getStandardName());
+                standard.setChildren(null);
+                industry.getChildren().add(standard);
             }
         }
-        return factoryDtos;
+        return industries;
     }
 
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public int addStandardTree(StandardTree standardTree) {
-        LambdaQueryWrapper<StandardTree> wrapper = Wrappers.<StandardTree>lambdaQuery()
-                .eq(StandardTree::getFactory, standardTree.getFactory())
-                .eq(StandardTree::getLaboratory, standardTree.getLaboratory())
-                .eq(StandardTree::getSampleType, standardTree.getSampleType())
-                .eq(StandardTree::getSample, standardTree.getSample())
-                .eq(StandardTree::getModel, standardTree.getModel());
-        if (StringUtils.isNotBlank(standardTree.getSample())) {
-            wrapper.eq(StandardTree::getSample, standardTree.getSample());
+    public int addStandardTree(AddStandardTreeNodeDto dto) {
+        if (dto == null) {
+            throw new BaseException("鏍囧噯鏍戣矾寰勬牸寮忛敊璇�");
         }
-
-        StandardTree tree = standardTreeMapper.selectOne(wrapper);
-        if (tree != null) {
-            throw new BaseException("璇ュ瀷鍙峰凡瀛樺湪");
+        String name = StandardTreePathParser.normalizeNodeName(dto.getName());
+        if (StringUtils.isBlank(dto.getParentTree())) {
+            // 鏂板琛屼笟鑺傜偣
+            Long count = laboratoryMapper.selectCount(Wrappers.<Laboratory>lambdaQuery()
+                    .eq(Laboratory::getLaboratoryName, name));
+            if (count > 0) {
+                throw new BaseException("琛屼笟鍚嶇О宸插瓨鍦�");
+            }
+            Laboratory laboratory = new Laboratory();
+            laboratory.setLaboratoryName(name);
+            laboratory.setSort(0);
+            return laboratoryMapper.insert(laboratory);
         }
-        return standardTreeMapper.insert(standardTree);
+        throw new BaseException("浜岀骇鏍囧噯璇峰湪鈥滄爣鍑嗙淮鎶も�濅腑閫氳繃琛屼笟缁戝畾杩涜缁存姢");
+        /*
+        // 鍦ㄨ涓氫笅鏂板鏍囧噯鑺傜偣
+        StandardTreePath path = StandardTreePathParser.parse(dto.getParentTree());
+        if (path.getStandard() != null) {
+            throw new BaseException("鏍囧噯鑺傜偣涓嬩笉鑳界户缁柊澧�");
+        }
+        String industry = path.getIndustry();
+        Long labCount = laboratoryMapper.selectCount(Wrappers.<Laboratory>lambdaQuery()
+                .eq(Laboratory::getLaboratoryName, industry));
+        if (labCount == 0) {
+            throw new BaseException("琛屼笟涓嶅瓨鍦�");
+        }
+        Long stdCount = standardTreeMapper.selectCount(Wrappers.<StandardTree>lambdaQuery()
+                .eq(StandardTree::getLaboratory, industry)
+                .eq(StandardTree::getSample, name)
+                .isNull(StandardTree::getSampleType)
+                .isNull(StandardTree::getModel));
+        if (stdCount > 0) {
+            throw new BaseException("璇ヨ涓氫笅鏍囧噯鍚嶇О宸插瓨鍦�");
+        }
+        StandardTree tree = new StandardTree();
+        tree.setLaboratory(industry);
+        tree.setSample(name);
+        tree.setFactory(null);
+        tree.setSampleType(null);
+        tree.setModel(null);
+        tree.setSort(0);
+        return standardTreeMapper.insert(tree);
+        */
     }
 
     @Override
     @Transactional(rollbackFor = Exception.class)
     public int delStandardTree(String tree) {
-        String[] trees = tree.split(" - ");
-        switch (trees.length) {
-            case 5:
-                if (trees[3].equals("null")) {
-                    standardTreeMapper.delete(Wrappers.<StandardTree>lambdaUpdate().eq(StandardTree::getFactory, trees[0]).eq(StandardTree::getLaboratory, trees[1]).eq(StandardTree::getSampleType, trees[2]).isNull(StandardTree::getSample).eq(StandardTree::getModel, trees[4]));
-                } else {
-                    standardTreeMapper.delete(Wrappers.<StandardTree>lambdaUpdate().eq(StandardTree::getFactory, trees[0]).eq(StandardTree::getLaboratory, trees[1]).eq(StandardTree::getSampleType, trees[2]).eq(StandardTree::getSample, trees[3]).eq(StandardTree::getModel, trees[4]));
-                }
-                break;
+        StandardTreePath path = StandardTreePathParser.parse(tree);
+        String industry = path.getIndustry();
+        String standard = path.getStandard();
+        if (standard != null) {
+            throw new BaseException("浜岀骇鏍囧噯璇峰湪鈥滄爣鍑嗙淮鎶も�濅腑閫氳繃琛屼笟缁戝畾杩涜缁存姢");
         }
+        if (standard == null) {
+            // 鍒犻櫎琛屼笟锛氳涓氫笅浠嶆湁鏍囧噯鏃舵嫆缁濆垹闄�
+            Long count = standardTreeMapper.selectCount(Wrappers.<StandardTree>lambdaQuery()
+                    .eq(StandardTree::getLaboratory, industry)
+                    .isNull(StandardTree::getSampleType)
+                    .isNull(StandardTree::getModel));
+            if (count > 0) {
+                throw new BaseException("璇ヨ涓氫笅浠嶆湁鏍囧噯锛岃鍏堝垹闄ゆ爣鍑�");
+            }
+            Laboratory laboratory = laboratoryMapper.selectOne(Wrappers.<Laboratory>lambdaQuery()
+                    .eq(Laboratory::getLaboratoryName, industry));
+            if (laboratory == null) {
+                throw new BaseException("琛屼笟涓嶅瓨鍦�");
+            }
+            Long objectCount = structureTestObjectMapper.selectCount(Wrappers.<StructureTestObject>lambdaQuery()
+                    .eq(StructureTestObject::getLaboratoryId, laboratory.getId()));
+            if (objectCount > 0) {
+                throw new BaseException("璇ヨ涓氬凡缁戝畾妫�娴嬪璞★紝绂佹鍒犻櫎");
+            }
+            int deleted = laboratoryMapper.delete(Wrappers.<Laboratory>lambdaQuery()
+                    .eq(Laboratory::getLaboratoryName, industry));
+            if (deleted == 0) {
+                throw new BaseException("琛屼笟涓嶅瓨鍦�");
+            }
+            return deleted;
+        }
+        // 鍒犻櫎鏍囧噯鍙婂叾鏍囧噯搴撻厤缃紙涓嶅彲杈撅紝淇濈暀鍘嗗彶瀹炵幇璇存槑锛�
+        StandardTree standardTree = standardTreeMapper.selectOne(Wrappers.<StandardTree>lambdaQuery()
+                .eq(StandardTree::getLaboratory, industry)
+                .eq(StandardTree::getSample, standard)
+                .isNull(StandardTree::getSampleType)
+                .isNull(StandardTree::getModel));
+        if (standardTree == null) {
+            throw new BaseException("鏍囧噯涓嶅瓨鍦�");
+        }
+        standardTreeTemplateBindingMapper.delete(Wrappers.<StandardTreeTemplateBinding>lambdaQuery()
+                .eq(StandardTreeTemplateBinding::getStandardTreeId, standardTree.getId()));
+        standardTreeMapper.deleteById(standardTree.getId());
+        standardProductListMapper.delete(Wrappers.<StandardProductList>lambdaQuery()
+                .eq(StandardProductList::getLaboratory, industry)
+                .eq(StandardProductList::getSample, standard)
+                .isNull(StandardProductList::getSampleType)
+                .isNull(StandardProductList::getModel));
         return 1;
     }
 
@@ -125,82 +236,225 @@
     }
 
     @Override
-    public boolean updateTreeSort(List<FactoryDto> list) {
-        List<StructureTestObject> testObjects = new ArrayList<>();
-        for (FactoryDto factoryDto : list) {
-            for (LaboratoryDto laboratoryDto : factoryDto.getChildren()) {
-                List<SampleTypeDto> children = laboratoryDto.getChildren();
-                int sort = 0;
-                // 寰幆绗笁灞�
-                for (SampleTypeDto child : children) {
-                    StructureTestObject structureTestObject = new StructureTestObject();
-                    structureTestObject.setSort(sort);
-                    structureTestObject.setId(child.getSampleTypeId());
-                    testObjects.add(structureTestObject);
-                    sort++;
+    @Transactional(rollbackFor = Exception.class)
+    public boolean updateTreeSort(List<StandardLibraryTreeNodeDto> list) {
+        List<StandardTree> trees = new ArrayList<>();
+        int industrySort = 0;
+        for (StandardLibraryTreeNodeDto industry : list) {
+            Laboratory lab = new Laboratory();
+            lab.setId(industry.getId());
+            lab.setSort(industrySort);
+            laboratoryMapper.updateById(lab);
+            industrySort++;
+
+            List<StandardLibraryTreeNodeDto> children = industry.getChildren();
+            if (children != null) {
+                int standardSort = 0;
+                for (StandardLibraryTreeNodeDto standard : children) {
+                    StandardTree tree = new StandardTree();
+                    tree.setId(standard.getId());
+                    tree.setSort(standardSort);
+                    trees.add(tree);
+                    standardSort++;
                 }
             }
         }
-        structureTestObjectService.updateBatchById(testObjects);
-
+        if (!trees.isEmpty()) {
+            this.updateBatchById(trees);
+        }
         return true;
     }
 
     /**
-     * 淇敼鏍囧噯鏁�
-     * @param standardTree
-     * @return
+     * 淇敼鏍囧噯鏍戣妭鐐瑰悕
      */
     @Override
-    public int updateStandardTree(StandardTree standardTree) {
-        // 淇敼鍚嶇О鍖归厤鐨勬爣鍑嗘爲涓嬬殑妫�楠岄」鐩�
-        // 鏌ヨ鎵�鏈夊璞�+鍚嶇О鐨勬爲
-
-        LambdaUpdateWrapper<StandardProductList> updateWrapper = Wrappers.<StandardProductList>lambdaUpdate()
-                .eq(StandardProductList::getFactory, standardTree.getFactory())
-                .eq(StandardProductList::getLaboratory, standardTree.getLaboratory())
-                .eq(StandardProductList::getSampleType, standardTree.getSampleType())
-                .eq(StandardProductList::getModel, standardTree.getOldModel());
-        if (StringUtils.isNotBlank(standardTree.getSample()) && !standardTree.getSample().equals("null")) {
-            updateWrapper.eq(StandardProductList::getSample, standardTree.getSample());
-        } else {
-            updateWrapper.isNull(StandardProductList::getSample);
+    @Transactional(rollbackFor = Exception.class)
+    public int updateStandardTree(UpdateStandardTreeNodeDto dto) {
+        if (dto == null) {
+            throw new BaseException("鏍囧噯鏍戣矾寰勬牸寮忛敊璇�");
         }
-
-        List<StandardProductList> standardProductLists = standardProductListService.list(updateWrapper);
-        if (CollectionUtils.isNotEmpty(standardProductLists)) {
-            for (StandardProductList standardProductList : standardProductLists) {
-                // 淇敼鏍峰搧鍚嶇О
-                standardProductList.setModel(standardTree.getModel());
-                // 淇敼鏍戝悕绉�
-                // 闇�瑕佹埅鍙栫鍥涚骇, 閬垮厤涓夊洓绾у悕绉颁竴鏍蜂慨鏀归敊璇�
-                String[] trees = standardProductList.getTree().split(" - ");
-                trees[4] = standardTree.getModel();
-                List<String> list = CollUtil.newArrayList(trees);
-                String newName = CollUtil.join(list, " - ");
-                standardProductList.setTree(newName);
-            }
-            standardProductListService.updateBatchById(standardProductLists);
+        String name = StandardTreePathParser.normalizeNodeName(dto.getName());
+        String oldLabel = StandardTreePathParser.normalizeNodeName(dto.getOldLabel());
+        if (StringUtils.isBlank(dto.getParentTree())) {
+            return updateIndustry(oldLabel, name);
         }
-
-        // 淇敼鏍囧噯鏁版楠岄」鐩�
-        LambdaUpdateWrapper<StandardTree> wrapper = Wrappers.<StandardTree>lambdaUpdate()
-                .eq(StandardTree::getFactory, standardTree.getFactory())
-                .eq(StandardTree::getLaboratory, standardTree.getLaboratory())
-                .eq(StandardTree::getSampleType, standardTree.getSampleType())
-                .eq(StandardTree::getModel, standardTree.getOldModel())
-                .set(StandardTree::getModel, standardTree.getModel());
-        if (StringUtils.isNotBlank(standardTree.getSample()) && !standardTree.getSample().equals("null")) {
-            wrapper.eq(StandardTree::getSample, standardTree.getSample());
-        } else {
-            wrapper.isNull(StandardTree::getSample);
-        }
-        return standardTreeMapper.update(null, wrapper);
+        throw new BaseException("浜岀骇鏍囧噯璇峰湪鈥滄爣鍑嗙淮鎶も�濅腑閫氳繃琛屼笟缁戝畾杩涜缁存姢");
     }
 
+    private int updateIndustry(String oldName, String newName) {
+        if (Objects.equals(oldName, newName)) {
+            return ensureIndustryExists(oldName);
+        }
+        Long count = laboratoryMapper.selectCount(Wrappers.<Laboratory>lambdaQuery()
+                .eq(Laboratory::getLaboratoryName, newName));
+        if (count > 0) {
+            throw new BaseException("琛屼笟鍚嶇О宸插瓨鍦�");
+        }
+        Laboratory old = laboratoryMapper.selectOne(Wrappers.<Laboratory>lambdaQuery()
+                .eq(Laboratory::getLaboratoryName, oldName));
+        if (old == null) {
+            throw new BaseException("琛屼笟涓嶅瓨鍦�");
+        }
+        Laboratory update = new Laboratory();
+        update.setId(old.getId());
+        update.setLaboratoryName(newName);
+        if (laboratoryMapper.updateById(update) == 0) {
+            throw new BaseException("琛屼笟涓嶅瓨鍦�");
+        }
+        // 绾ц仈鏍囧噯鏍戣鑼冨寲璁板綍
+        standardTreeMapper.update(null, Wrappers.<StandardTree>lambdaUpdate()
+                .eq(StandardTree::getLaboratory, oldName)
+                .isNull(StandardTree::getSampleType)
+                .isNull(StandardTree::getModel)
+                .set(StandardTree::getLaboratory, newName));
+        // 绾ц仈鏍囧噯搴撻厤缃�
+        standardTreeMapper.updateProductListIndustry(oldName, newName);
+        // 绾ц仈鍩虹妫�楠岄」
+        structureItemParameterMapper.update(null, Wrappers.<StructureItemParameter>lambdaUpdate()
+                .eq(StructureItemParameter::getLaboratory, oldName)
+                .set(StructureItemParameter::getLaboratory, newName));
+        // 绾ц仈鏈畬鎴愯鍗�
+        standardTreeMapper.updateInsOrderIndustry(oldName, newName);
+        standardTreeMapper.updateInsSampleIndustry(oldName, newName);
+        standardTreeMapper.updateInsProductIndustry(oldName, newName);
+        return 1;
+    }
 
+    private int updateStandard(String industry, String oldName, String newName) {
+        if (Objects.equals(oldName, newName)) {
+            return ensureStandardExists(industry, oldName);
+        }
+        Long count = standardTreeMapper.selectCount(Wrappers.<StandardTree>lambdaQuery()
+                .eq(StandardTree::getLaboratory, industry)
+                .eq(StandardTree::getSample, newName)
+                .isNull(StandardTree::getSampleType)
+                .isNull(StandardTree::getModel));
+        if (count > 0) {
+            throw new BaseException("璇ヨ涓氫笅鏍囧噯鍚嶇О宸插瓨鍦�");
+        }
+        StandardTree old = standardTreeMapper.selectOne(Wrappers.<StandardTree>lambdaQuery()
+                .eq(StandardTree::getLaboratory, industry)
+                .eq(StandardTree::getSample, oldName)
+                .isNull(StandardTree::getSampleType)
+                .isNull(StandardTree::getModel));
+        if (old == null) {
+            throw new BaseException("鏍囧噯涓嶅瓨鍦�");
+        }
+        StandardTree update = new StandardTree();
+        update.setId(old.getId());
+        update.setSample(newName);
+        if (standardTreeMapper.updateById(update) == 0) {
+            throw new BaseException("鏍囧噯涓嶅瓨鍦�");
+        }
+        // 绾ц仈鏍囧噯搴撻厤缃�
+        standardTreeMapper.updateProductListStandard(industry, oldName, newName);
+        // 绾ц仈鏈畬鎴愯鍗�
+        standardTreeMapper.updateInsOrderStandard(industry, oldName, newName);
+        standardTreeMapper.updateInsSampleStandard(industry, oldName, newName);
+        standardTreeMapper.updateInsProductStandard(industry, oldName, newName);
+        return 1;
+    }
+
+    private int ensureIndustryExists(String industry) {
+        Long count = laboratoryMapper.selectCount(Wrappers.<Laboratory>lambdaQuery()
+                .eq(Laboratory::getLaboratoryName, industry));
+        if (count == 0) {
+            throw new BaseException("琛屼笟涓嶅瓨鍦�");
+        }
+        return 1;
+    }
+
+    private int ensureStandardExists(String industry, String standard) {
+        Long count = standardTreeMapper.selectCount(Wrappers.<StandardTree>lambdaQuery()
+                .eq(StandardTree::getLaboratory, industry)
+                .eq(StandardTree::getSample, standard)
+                .isNull(StandardTree::getSampleType)
+                .isNull(StandardTree::getModel));
+        if (count == 0) {
+            throw new BaseException("鏍囧噯涓嶅瓨鍦�");
+        }
+        return 1;
+    }
+
+    @Override
+    public List<StandardTreeTemplateBindingVo> selectTemplateBindings(Integer standardTreeId) {
+        ensureTwoLevelStandard(standardTreeId);
+        List<StandardTreeTemplateBinding> bindings = standardTreeTemplateBindingMapper.selectList(
+                Wrappers.<StandardTreeTemplateBinding>lambdaQuery()
+                        .eq(StandardTreeTemplateBinding::getStandardTreeId, standardTreeId)
+                        .orderByAsc(StandardTreeTemplateBinding::getSort, StandardTreeTemplateBinding::getId));
+        List<StandardTreeTemplateBindingVo> result = new ArrayList<>();
+        for (StandardTreeTemplateBinding binding : bindings) {
+            StandardTemplate template = standardTemplateService.getById(binding.getTemplateId());
+            if (template == null) {
+                continue;
+            }
+            StandardTreeTemplateBindingVo vo = new StandardTreeTemplateBindingVo();
+            vo.setTemplateId(template.getId());
+            vo.setName(template.getName());
+            vo.setNumber(template.getNumber());
+            vo.setRemark(template.getRemark());
+            vo.setSort(binding.getSort());
+            result.add(vo);
+        }
+        return result;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean saveTemplateBindings(StandardTreeTemplateBindingDto dto) {
+        if (dto == null || dto.getStandardTreeId() == null) {
+            throw new BaseException("鏍囧噯涓嶈兘涓虹┖");
+        }
+        ensureTwoLevelStandard(dto.getStandardTreeId());
+        List<TemplateBindingItemDto> templates = dto.getTemplates() == null ? new ArrayList<>() : dto.getTemplates();
+        Set<Integer> templateIds = new HashSet<>();
+        List<StandardTreeTemplateBinding> bindings = new ArrayList<>();
+        int index = 0;
+        for (TemplateBindingItemDto item : templates) {
+            if (item == null || item.getTemplateId() == null || !templateIds.add(item.getTemplateId())) {
+                throw new BaseException("鍘熷璁板綍妯℃澘涓嶈兘閲嶅缁戝畾");
+            }
+            StandardTemplate template = standardTemplateService.getById(item.getTemplateId());
+            if (template == null) {
+                throw new BaseException("鍘熷璁板綍妯℃澘涓嶅瓨鍦�");
+            }
+            StandardTreeTemplateBinding binding = new StandardTreeTemplateBinding();
+            binding.setStandardTreeId(dto.getStandardTreeId());
+            binding.setTemplateId(item.getTemplateId());
+            binding.setSort(item.getSort() == null ? index : item.getSort());
+            bindings.add(binding);
+            index++;
+        }
+        standardTreeTemplateBindingMapper.delete(Wrappers.<StandardTreeTemplateBinding>lambdaQuery()
+                .eq(StandardTreeTemplateBinding::getStandardTreeId, dto.getStandardTreeId()));
+        for (StandardTreeTemplateBinding binding : bindings) {
+            standardTreeTemplateBindingMapper.insert(binding);
+        }
+        return true;
+    }
+
+    @Override
+    public List<TemplateInspectionItemDto> selectTemplateInspectionItems(Integer standardTreeId) {
+        List<StandardTreeTemplateBindingVo> bindings = selectTemplateBindings(standardTreeId);
+        List<TemplateInspectionItemDto> result = new ArrayList<>();
+        for (StandardTreeTemplateBindingVo binding : bindings) {
+            List<TemplateInspectionItemDto> items = StandardTemplateInspectionItemParser.parse(binding.getTemplateId(),
+                    binding.getSort(), standardTemplateService.getStandTempThingById(binding.getTemplateId()));
+            result.addAll(items);
+        }
+        result.sort(Comparator.comparing(TemplateInspectionItemDto::getTemplateSort)
+                .thenComparing(TemplateInspectionItemDto::getTemplateRowIndex));
+        return result;
+    }
+
+    private StandardTree ensureTwoLevelStandard(Integer standardTreeId) {
+        StandardTree standardTree = this.getById(standardTreeId);
+        if (standardTree == null || StringUtils.isBlank(standardTree.getSample())
+                || standardTree.getSampleType() != null || standardTree.getModel() != null) {
+            throw new BaseException("璇烽�夋嫨鏍囧噯搴撲簩绾ф爣鍑�");
+        }
+        return standardTree;
+    }
 }
-
-
-
-
diff --git a/basic-server/src/main/java/com/ruoyi/basic/utils/StandardTemplateInspectionItemParser.java b/basic-server/src/main/java/com/ruoyi/basic/utils/StandardTemplateInspectionItemParser.java
new file mode 100644
index 0000000..d2fd833
--- /dev/null
+++ b/basic-server/src/main/java/com/ruoyi/basic/utils/StandardTemplateInspectionItemParser.java
@@ -0,0 +1,81 @@
+package com.ruoyi.basic.utils;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.ruoyi.basic.dto.TemplateInspectionItemDto;
+import com.ruoyi.common.exception.base.BaseException;
+import org.springframework.util.StringUtils;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/** 瑙f瀽 Luckysheet 鍘熷璁板綍妯℃澘涓殑鈥滄楠岄」鈥濇爣璁般�� */
+public final class StandardTemplateInspectionItemParser {
+    private static final String INSPECTION_ITEM_MARK = "妫�楠岄」";
+
+    private StandardTemplateInspectionItemParser() {
+    }
+
+    public static List<TemplateInspectionItemDto> parse(Integer templateId, Integer templateSort, String thing) {
+        if (!StringUtils.hasText(thing)) {
+            throw new BaseException("鍘熷璁板綍妯℃澘鍐呭涓嶈兘涓虹┖");
+        }
+        JSONArray sheets;
+        try {
+            JSONObject root = JSON.parseObject(thing);
+            sheets = root == null ? null : root.getJSONArray("data");
+            if (sheets == null) {
+                sheets = JSON.parseArray(thing);
+            }
+        } catch (Exception e) {
+            throw new BaseException("鍘熷璁板綍妯℃澘鏍煎紡閿欒");
+        }
+        List<TemplateInspectionItemDto> result = new ArrayList<>();
+        Set<Integer> rows = new HashSet<>();
+        if (sheets == null) {
+            throw new BaseException("鍘熷璁板綍妯℃澘鏍煎紡閿欒");
+        }
+        for (Object sheetObject : sheets) {
+            JSONObject sheet = (JSONObject) sheetObject;
+            JSONArray cells = sheet.getJSONArray("celldata");
+            if (cells == null) {
+                continue;
+            }
+            for (Object cellObject : cells) {
+                JSONObject cell = (JSONObject) cellObject;
+                JSONObject value = cell.getJSONObject("v");
+                if (value == null || !isInspectionItem(value)) {
+                    continue;
+                }
+                Integer row = cell.getInteger("r");
+                String item = value.getString("v");
+                if (!StringUtils.hasText(item)) {
+                    item = value.getString("m");
+                }
+                if (row == null || !StringUtils.hasText(item)) {
+                    throw new BaseException("鈥滄楠岄」鈥濇爣璁版墍鍦ㄥ崟鍏冩牸蹇呴』濉啓妫�楠岄」鍚嶇О");
+                }
+                if (!rows.add(row)) {
+                    throw new BaseException("鍚屼竴妯℃澘琛屽彧鑳介厤缃竴涓�滄楠岄」鈥濇爣璁�");
+                }
+                TemplateInspectionItemDto dto = new TemplateInspectionItemDto();
+                dto.setTemplateId(templateId);
+                dto.setTemplateSort(templateSort);
+                dto.setTemplateRowIndex(row);
+                dto.setInspectionItem(item.trim());
+                result.add(dto);
+            }
+        }
+        result.sort(Comparator.comparing(TemplateInspectionItemDto::getTemplateRowIndex));
+        return result;
+    }
+
+    private static boolean isInspectionItem(JSONObject value) {
+        JSONObject ps = value.getJSONObject("ps");
+        return ps != null && INSPECTION_ITEM_MARK.equals(ps.getString("value"));
+    }
+}
diff --git a/basic-server/src/main/java/com/ruoyi/basic/utils/StandardTreePath.java b/basic-server/src/main/java/com/ruoyi/basic/utils/StandardTreePath.java
new file mode 100644
index 0000000..401b59d
--- /dev/null
+++ b/basic-server/src/main/java/com/ruoyi/basic/utils/StandardTreePath.java
@@ -0,0 +1,18 @@
+package com.ruoyi.basic.utils;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 鏍囧噯鏍戣矾寰勮В鏋愮粨鏋�
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class StandardTreePath {
+
+    private String industry;
+
+    private String standard;
+}
diff --git a/basic-server/src/main/java/com/ruoyi/basic/utils/StandardTreePathParser.java b/basic-server/src/main/java/com/ruoyi/basic/utils/StandardTreePathParser.java
new file mode 100644
index 0000000..ae186b1
--- /dev/null
+++ b/basic-server/src/main/java/com/ruoyi/basic/utils/StandardTreePathParser.java
@@ -0,0 +1,78 @@
+package com.ruoyi.basic.utils;
+
+import com.ruoyi.common.exception.base.BaseException;
+
+import java.util.regex.Pattern;
+
+/**
+ * 鏍囧噯鏍戝浐瀹氫袱绾ц矾寰勮В鏋愬伐鍏枫��
+ *
+ * 鍏煎鍓嶇浼犻�掔殑涓ょ鍙跺瓙璺緞锛�
+ * 鏍囧噯搴撲晶锛氳涓� - null - 鏍囧噯
+ * 涓嬪崟渚э細琛屼笟 -  - 鏍囧噯
+ * 缁熶竴瑙f瀽涓� industry = 绗竴涓湁鏁堟锛宻tandard = 鏈�鍚庝竴涓湁鏁堟銆�
+ */
+public final class StandardTreePathParser {
+
+    private static final String SEPARATOR = " - ";
+
+    private static final String NULL_MARKER = "null";
+
+    private StandardTreePathParser() {
+    }
+
+    /**
+     * 瑙f瀽璺緞涓鸿涓� + 鏍囧噯銆傛爣鍑嗗彲鑳戒负 null锛堣〃绀轰粎琛屼笟鑺傜偣锛夈��
+     */
+    public static StandardTreePath parse(String tree) {
+        if (tree == null || tree.trim().isEmpty()) {
+            throw new BaseException("鏍囧噯鏍戣矾寰勬牸寮忛敊璇�");
+        }
+        String[] parts = tree.split(Pattern.quote(SEPARATOR), -1);
+        for (int i = 0; i < parts.length; i++) {
+            parts[i] = parts[i].trim();
+        }
+        if (parts.length == 1) {
+            validateSegment(parts[0]);
+            return new StandardTreePath(parts[0], null);
+        }
+        if (parts.length == 2) {
+            validateSegment(parts[0]);
+            validateSegment(parts[1]);
+            return new StandardTreePath(parts[0], parts[1]);
+        }
+        if (parts.length == 3 && (parts[1].isEmpty() || NULL_MARKER.equals(parts[1]))) {
+            validateSegment(parts[0]);
+            validateSegment(parts[2]);
+            return new StandardTreePath(parts[0], parts[2]);
+        }
+        throw new BaseException("鏍囧噯鏍戣矾寰勬牸寮忛敊璇�");
+    }
+
+    /**
+     * 鐢熸垚瑙勮寖璺緞锛氳涓� - null - 鏍囧噯
+     */
+    public static String canonical(String industry, String standard) {
+        String normalizedIndustry = trim(industry);
+        String normalizedStandard = trim(standard);
+        validateSegment(normalizedIndustry);
+        validateSegment(normalizedStandard);
+        return normalizedIndustry + SEPARATOR + NULL_MARKER + SEPARATOR + normalizedStandard;
+    }
+
+    private static String trim(String value) {
+        return value == null ? "" : value.trim();
+    }
+
+    public static String normalizeNodeName(String value) {
+        String normalized = trim(value);
+        validateSegment(normalized);
+        return normalized;
+    }
+
+    private static void validateSegment(String value) {
+        if (value == null || value.isEmpty() || NULL_MARKER.equals(value) || value.contains(SEPARATOR)) {
+            throw new BaseException("鏍囧噯鏍戣矾寰勬牸寮忛敊璇�");
+        }
+    }
+}
diff --git a/basic-server/src/main/resources/mapper/StandardMethodMapper.xml b/basic-server/src/main/resources/mapper/StandardMethodMapper.xml
index 1f50462..b48f4d0 100644
--- a/basic-server/src/main/resources/mapper/StandardMethodMapper.xml
+++ b/basic-server/src/main/resources/mapper/StandardMethodMapper.xml
@@ -21,7 +21,6 @@
                 sm.code,
                 sm.name,
                 sm.structure_test_object_id,
-                sto.specimen_name sample_type,
                 sm.remark,
                 u1.name create_user_name,
                 sm.create_time,
@@ -31,11 +30,14 @@
                 sm.field,
                 sm.name_en,
                 sm.is_use,
-                sm.qualification_id
+                sm.qualification_id,
+                COALESCE((SELECT GROUP_CONCAT(l.laboratory_name ORDER BY COALESCE(l.sort, 0), l.id SEPARATOR '銆�')
+                          FROM standard_method_industry smi JOIN laboratory l ON l.id = smi.industry_id
+                          WHERE smi.standard_method_id = sm.id), '') industry_names,
+                (SELECT COUNT(*) FROM standard_method_attachment sma WHERE sma.standard_method_id = sm.id) attachment_count
                 from standard_method sm
                 left join user u1 on sm.create_user = u1.id
                 left join user u2 on sm.update_user = u2.id
-                left join structure_test_object sto on sm.structure_test_object_id = sto.id
                 where sm.code != '鎶�鏈姹�'
                 ) a
         <if test="ew.customSqlSegment != null and ew.customSqlSegment != ''">
@@ -80,6 +82,16 @@
           and is_product = 1
           and structure_test_object_id LIKE CONCAT('%', #{tree}, '%')
     </select>
+    <select id="selectStandardMethodListsByIndustry" resultType="com.ruoyi.basic.pojo.StandardMethodList">
+        select sm.id, sm.code, sm.name, sm.remark
+        from standard_method sm
+                 inner join standard_method_industry smi on smi.standard_method_id = sm.id
+                 inner join laboratory l on l.id = smi.industry_id
+        where sm.is_use = 1
+          and sm.is_product = 1
+          and l.laboratory_name = #{industry}
+        order by sm.id
+    </select>
     <select id="selectListEnum" resultType="com.ruoyi.basic.pojo.StandardMethodList">
         select id,code,name from standard_method
         where is_product = 1
diff --git a/basic-server/src/main/resources/mapper/StandardTreeMapper.xml b/basic-server/src/main/resources/mapper/StandardTreeMapper.xml
index ae8c4d5..f13da93 100644
--- a/basic-server/src/main/resources/mapper/StandardTreeMapper.xml
+++ b/basic-server/src/main/resources/mapper/StandardTreeMapper.xml
@@ -10,6 +10,8 @@
         <result property="sampleType" column="sample_type" jdbcType="VARCHAR"/>
         <result property="sample" column="sample" jdbcType="VARCHAR"/>
         <result property="model" column="model" jdbcType="VARCHAR"/>
+        <result property="sort" column="sort" jdbcType="INTEGER"/>
+        <result property="standardMethodId" column="standard_method_id" jdbcType="INTEGER"/>
         <result property="createUser" column="create_user" jdbcType="INTEGER"/>
         <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
         <result property="updateUser" column="update_user" jdbcType="INTEGER"/>
@@ -89,24 +91,106 @@
         rates
     </sql>
 
-    <select id="selectStandardTreeList" resultMap="FactoryDto">
-        select ''                          factory,
-               l.laboratory_name laboratory,
-               sto.specimen_name sample_type,
-               p.name            sample,
-               st.model,
-               sto.code,
-               sto.id  sample_type_id,
-               sto.sort,
-               sto.specimen_name_en sample_type_en,
-               p.name_en sample_en
-        from laboratory l
-                 left join structure_test_object sto on sto.laboratory_id = l.id
-                 left join product p on p.object_id = sto.id
-                 left join standard_tree st on st.sample_type = sto.specimen_name
-            and st.sample = p.name
-        order by l.id, CAST(sto.code AS DECIMAL), p.id, ISNULL(st.id), st.id
+    <select id="selectStandardLibraryTree" resultType="com.ruoyi.basic.dto.StandardLibraryTreeRowDto">
+        SELECT l.id                                      AS industry_id,
+               l.laboratory_name                         AS industry_name,
+               l.sort                                    AS industry_sort,
+               st.id                                     AS standard_id,
+               sm.id                                     AS standard_method_id,
+               sm.code                                   AS standard_code,
+               sm.name                                   AS standard_name,
+               sm.name_en                                AS standard_name_en,
+               CONCAT(sm.code, ' - ', sm.name)           AS standard_label,
+               st.sort                                   AS standard_sort
+        FROM laboratory l
+        LEFT JOIN standard_tree st
+               ON st.laboratory = l.laboratory_name
+              AND st.sample_type IS NULL
+              AND st.model IS NULL
+              AND st.standard_method_id IS NOT NULL
+              AND EXISTS (SELECT 1 FROM standard_method sm0
+                          WHERE sm0.id = st.standard_method_id
+                            AND sm0.is_product = 1 AND sm0.is_use = 1)
+        LEFT JOIN standard_method sm ON sm.id = st.standard_method_id
+        ORDER BY COALESCE(l.sort, 0), l.id,
+                 COALESCE(st.sort, 0), st.id
     </select>
+
+    <update id="updateProductListIndustry">
+        UPDATE standard_product_list
+        SET laboratory = #{newName},
+            tree = CASE WHEN sample_type IS NULL AND model IS NULL AND sample IS NOT NULL
+                        THEN CONCAT(#{newName}, ' - null - ', sample)
+                        ELSE tree END
+        WHERE laboratory = #{oldName}
+    </update>
+
+    <update id="updateProductListStandard">
+        UPDATE standard_product_list
+        SET sample = #{newName},
+            tree = CONCAT(#{industry}, ' - null - ', #{newName})
+        WHERE laboratory = #{industry}
+          AND sample = #{oldName}
+          AND sample_type IS NULL
+          AND model IS NULL
+    </update>
+
+    <update id="updateInsOrderIndustry">
+        UPDATE ins_order
+        SET laboratory = #{newName}
+        WHERE laboratory = #{oldName}
+          AND (state IS NULL OR state NOT IN (3, -1))
+          AND (ins_state IS NULL OR ins_state != 5)
+    </update>
+
+    <update id="updateInsSampleIndustry">
+        UPDATE ins_sample s
+        JOIN ins_order o ON o.id = s.ins_order_id
+        SET s.laboratory = #{newName}
+        WHERE s.laboratory = #{oldName}
+          AND (o.state IS NULL OR o.state NOT IN (3, -1))
+          AND (o.ins_state IS NULL OR o.ins_state != 5)
+    </update>
+
+    <update id="updateInsProductIndustry">
+        UPDATE ins_product p
+        JOIN ins_sample s ON s.id = p.ins_sample_id
+        JOIN ins_order o ON o.id = s.ins_order_id
+        SET p.laboratory = #{newName}
+        WHERE p.laboratory = #{oldName}
+          AND (o.state IS NULL OR o.state NOT IN (3, -1))
+          AND (o.ins_state IS NULL OR o.ins_state != 5)
+    </update>
+
+    <update id="updateInsOrderStandard">
+        UPDATE ins_order
+        SET sample = #{newName}
+        WHERE laboratory = #{industry}
+          AND sample = #{oldName}
+          AND (state IS NULL OR state NOT IN (3, -1))
+          AND (ins_state IS NULL OR ins_state != 5)
+    </update>
+
+    <update id="updateInsSampleStandard">
+        UPDATE ins_sample s
+        JOIN ins_order o ON o.id = s.ins_order_id
+        SET s.sample = #{newName}
+        WHERE s.laboratory = #{industry}
+          AND s.sample = #{oldName}
+          AND (o.state IS NULL OR o.state NOT IN (3, -1))
+          AND (o.ins_state IS NULL OR o.ins_state != 5)
+    </update>
+
+    <update id="updateInsProductStandard">
+        UPDATE ins_product p
+        JOIN ins_sample s ON s.id = p.ins_sample_id
+        JOIN ins_order o ON o.id = s.ins_order_id
+        SET p.sample = #{newName}
+        WHERE p.laboratory = #{industry}
+          AND p.sample = #{oldName}
+          AND (o.state IS NULL OR o.state NOT IN (3, -1))
+          AND (o.ins_state IS NULL OR o.ins_state != 5)
+    </update>
 
     <select id="selectStandardProductById" resultType="com.ruoyi.basic.pojo.StandardProductList">
         select inspection_item,
@@ -194,6 +278,44 @@
         order by inspection_item_class, inspection_item, id asc
     </select>
 
+    <select id="selectStandardProductListByIndustry" resultType="com.ruoyi.basic.pojo.StandardProductList">
+        select inspection_item,
+               inspection_item_en,
+               inspection_item_subclass,
+               inspection_item_subclass_en,
+               inspection_item_class,
+               inspection_item_class_en,
+               method,
+               son_laboratory,
+               unit,
+               ask_tell as tell,
+               ask,
+               price,
+               man_hour,
+               man_hour_group,
+               man_day,
+               inspection_item_type,
+               inspection_value_type,
+               bsm,
+               template_id,
+               laboratory,
+               checkout_number,
+               section,
+               dic,
+               0 as state,
+               null as model,
+               #{standard} as sample,
+               #{tree} as tree,
+               id as structure_item_parameter_id,
+               radius_list,
+               rates
+        from structure_item_parameter
+        where (laboratory is null
+            or laboratory = ''
+            or laboratory = #{industry})
+        order by inspection_item_class, inspection_item, id asc
+    </select>
+
     <select id="getStandardTree2" resultMap="SampleTypeDto">
         select sto.specimen_name sample_type,
                p.name            sample,
diff --git a/cnas-require/src/main/java/com/ruoyi/require/service/impl/FeStandardSubstanceServiceImpl.java b/cnas-require/src/main/java/com/ruoyi/require/service/impl/FeStandardSubstanceServiceImpl.java
index d4aac72..015802e 100644
--- a/cnas-require/src/main/java/com/ruoyi/require/service/impl/FeStandardSubstanceServiceImpl.java
+++ b/cnas-require/src/main/java/com/ruoyi/require/service/impl/FeStandardSubstanceServiceImpl.java
@@ -4,7 +4,6 @@
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
 import com.baomidou.mybatisplus.core.toolkit.StringUtils;
-import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.ruoyi.framework.exception.ErrorException;
@@ -23,11 +22,8 @@
 import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
-import java.util.Set;
-import java.util.stream.Collectors;
 
 /**
  * <p>
@@ -85,26 +81,11 @@
             throw new ErrorException("瀵煎叆鏂囦欢娌℃湁鏍囧噯鐗╄川鏁版嵁");
         }
 
-        Set<String> manageNumbers = new HashSet<>();
-        for (int i = 0; i < rows.size(); i++) {
-            String manageNum = required(rows.get(i).getManageNum(), i + 2, "绠$悊缂栧彿");
-            if (!manageNumbers.add(manageNum)) {
-                throw new ErrorException("绗�" + (i + 2) + "琛岋細绠$悊缂栧彿" + manageNum + "鍦ㄦ枃浠朵腑閲嶅");
-            }
-        }
-        Set<String> existingNumbers = baseMapper.selectList(Wrappers.<FeStandardSubstance>lambdaQuery()
-                        .in(FeStandardSubstance::getManageNum, manageNumbers)
-                        .select(FeStandardSubstance::getManageNum))
-                .stream().map(FeStandardSubstance::getManageNum).collect(Collectors.toSet());
-
         List<FeStandardSubstance> substances = new ArrayList<>();
         for (int i = 0; i < rows.size(); i++) {
             int rowNumber = i + 2;
             FeStandardSubstanceImport row = rows.get(i);
             String manageNum = required(row.getManageNum(), rowNumber, "绠$悊缂栧彿");
-            if (existingNumbers.contains(manageNum)) {
-                throw new ErrorException("绗�" + rowNumber + "琛岋細绠$悊缂栧彿" + manageNum + "宸插瓨鍦�");
-            }
 
             FeStandardSubstance substance = new FeStandardSubstance();
             substance.setName(required(row.getName(), rowNumber, "鏍囧噯鐗╄川鍚嶇О"));
@@ -160,4 +141,4 @@
     private String trim(String value) {
         return value == null ? null : value.trim();
     }
-}
+}
\ No newline at end of file
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/controller/InsOrderController.java b/inspect-server/src/main/java/com/ruoyi/inspect/controller/InsOrderController.java
index 0c7f3a5..464d94c 100644
--- a/inspect-server/src/main/java/com/ruoyi/inspect/controller/InsOrderController.java
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/controller/InsOrderController.java
@@ -82,6 +82,9 @@
     @PreAuthorize("@ss.hasPermi('add:insOrder')")
     @PostMapping("/addInsOrder")
     public Result<?> addInsOrder(@RequestBody InsPlaceOrderDto insPlaceOrderDto) {
+        if (insPlaceOrderDto.getStandardTreeId() != null) {
+            return Result.success(insOrderService.addNewJuInsOrder(insPlaceOrderDto));
+        }
         return Result.success(insOrderService.addInsOrder(insPlaceOrderDto.getSampleList(), insPlaceOrderDto.getInsOrder()));
     }
 
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/controller/InsOrderPlanController.java b/inspect-server/src/main/java/com/ruoyi/inspect/controller/InsOrderPlanController.java
index 814ba70..cbce3a2 100644
--- a/inspect-server/src/main/java/com/ruoyi/inspect/controller/InsOrderPlanController.java
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/controller/InsOrderPlanController.java
@@ -9,6 +9,10 @@
 import com.ruoyi.inspect.dto.InsOrderPlanProductDto;
 import com.ruoyi.inspect.dto.InsProductBindingDto;
 import com.ruoyi.inspect.dto.SaveInsContextDto;
+import com.ruoyi.inspect.dto.SaveOrderDevicesDto;
+import com.ruoyi.inspect.dto.SaveTemplateContextDto;
+import com.ruoyi.inspect.dto.VerifyPlanDto;
+import com.ruoyi.inspect.dto.WriteInspectionConditionDto;
 import com.ruoyi.inspect.pojo.InsOrder;
 import com.ruoyi.inspect.pojo.InsOrderFactoryVerify;
 import com.ruoyi.inspect.pojo.InsOrderFile;
@@ -93,21 +97,21 @@
 
     @ApiOperation(value = "澶嶆牳妫�楠屼换鍔�")
     @PostMapping("/verifyPlan")
-    public Result<?> verifyPlan(Integer orderId, String laboratory, Integer type, String tell, Integer userId) {
-        return Result.success(insOrderPlanService.rawMaterialVerifyPlan(orderId, laboratory, type, tell, userId));
+    public Result<?> verifyPlan(@RequestBody VerifyPlanDto verifyPlanDto) {
+        return Result.success(insOrderPlanService.rawMaterialVerifyPlan(verifyPlanDto));
     }
 
     @ApiOperation(value = "鏍¢獙妫�楠屼换鍔℃彁浜�")
     @PostMapping("/checkSubmitPlan")
-    public Result<?> checkSubmitPlan(Integer orderId, String laboratory, Integer reportType) {
-        return Result.success(insOrderPlanService.checkSubmitPlan(orderId, laboratory, reportType));
+    public Result<?> checkSubmitPlan(Integer orderId, String laboratory) {
+        return Result.success(insOrderPlanService.checkSubmitPlan(orderId, laboratory));
     }
 
 
-    @ApiOperation(value = "妫�楠屼换鍔℃彁浜わ紙reportType锛�0妫�娴嬫姤鍛婏紝1妫�楠屾姤鍛婏級")
+    @ApiOperation(value = "妫�楠屼换鍔℃彁浜�")
     @PostMapping("/submitPlan")
-    public Result<?> submitPlan(Integer orderId, String laboratory, Integer verifyUser, String entrustCode, Boolean registerInsResults, Integer reportType) {
-        int num = insOrderPlanService.submitPlan(orderId, laboratory, verifyUser, entrustCode, registerInsResults, reportType);
+    public Result<?> submitPlan(Integer orderId, String laboratory, Integer verifyUser, String entrustCode, Boolean registerInsResults) {
+        int num = insOrderPlanService.submitPlan(orderId, laboratory, verifyUser, entrustCode, registerInsResults);
         return num == 1 ? Result.success() : Result.fail("鎻愪氦澶辫触锛岄儴鍒嗛」鐩繕鏈繘琛屾楠�");
     }
 
@@ -115,6 +119,26 @@
     @PostMapping("/saveInsContext")
     public Result<?> saveInsContext(@RequestBody SaveInsContextDto saveInsContextDto) {
         insOrderPlanService.saveInsContext(saveInsContextDto);
+        return Result.success();
+    }
+
+    @ApiOperation(value = "鎸夋ā鏉垮潗鏍囦繚瀛樻柊鑱氬師濮嬭褰�")
+    @PostMapping("/saveTemplateContext")
+    public Result<?> saveTemplateContext(@RequestBody SaveTemplateContextDto saveTemplateContextDto) {
+        insOrderPlanService.saveTemplateContext(saveTemplateContextDto);
+        return Result.success();
+    }
+
+    @ApiOperation(value = "鏌ヨ妫�楠屽崟閫夋嫨鐨勮澶�")
+    @GetMapping("/orderDevices")
+    public Result<?> getOrderDevices(Integer orderId) {
+        return Result.success(insOrderPlanService.getOrderDeviceIds(orderId));
+    }
+
+    @ApiOperation(value = "淇濆瓨妫�楠屽崟閫夋嫨鐨勮澶�")
+    @PutMapping("/orderDevices")
+    public Result<?> saveOrderDevices(@RequestBody SaveOrderDevicesDto saveOrderDevicesDto) {
+        insOrderPlanService.saveOrderDevices(saveOrderDevicesDto);
         return Result.success();
     }
 
@@ -153,8 +177,8 @@
 
     @ApiOperation(value = "濉啓娓╁害涓庢箍搴�")
     @PostMapping("/write")
-    public Result<?> write(@RequestBody InsOrder insOrder) {
-        return Result.success(insProductService.write(insOrder));
+    public Result<?> write(@RequestBody WriteInspectionConditionDto dto) {
+        return Result.success(insProductService.write(dto));
     }
 
 
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/controller/InsReportController.java b/inspect-server/src/main/java/com/ruoyi/inspect/controller/InsReportController.java
index a60e47d..1fef16d 100644
--- a/inspect-server/src/main/java/com/ruoyi/inspect/controller/InsReportController.java
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/controller/InsReportController.java
@@ -150,6 +150,14 @@
         return Result.success(insReportService.downAll(ids));
     }
 
+    @ApiOperation(value = "鎶ュ憡涓嬭浇")
+    @GetMapping("/download")
+    @PreAuthorize("@ss.hasPermi('business:reportPreparation')")
+    @PersonalScope(permsName = "business:reportPreparation", objectName = ReportPageDto.class, paramName = "createOrderUser")
+    public void download(Integer id, ReportPageDto reportPageDto, HttpServletResponse response) {
+        insReportService.download(id, reportPageDto.getCreateOrderUser(), response);
+    }
+
     @ApiOperation(value = "鎶ュ憡鎵归噺涓婁紶")
     @PostMapping("/upAll")
     public Result upAll(MultipartFile file) throws IOException {
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/dto/InsPlaceOrderDto.java b/inspect-server/src/main/java/com/ruoyi/inspect/dto/InsPlaceOrderDto.java
index 03c0cc6..689a9ea 100644
--- a/inspect-server/src/main/java/com/ruoyi/inspect/dto/InsPlaceOrderDto.java
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/dto/InsPlaceOrderDto.java
@@ -21,6 +21,15 @@
     @ApiModelProperty("璁㈠崟淇℃伅")
     private InsOrder insOrder;
 
+    @ApiModelProperty("鏍囧噯搴撲簩绾ф爣鍑咺D锛堟柊鑱氬崟鏍峰搧涓嬪崟锛�")
+    private Integer standardTreeId;
+
+    @ApiModelProperty("鏍峰搧缂栧彿锛堟柊鑱氬崟鏍峰搧涓嬪崟锛�")
+    private String sampleCode;
+
+    @ApiModelProperty("涓嬪崟鏃朵繚鐣欑殑鍘熷璁板綍妯℃澘")
+    private List<OrderTemplateSelectionDto> templateList;
+
     @ApiModelProperty("閾滃崟涓濊鍗�")
     private CopperInsOrderDto copperInsOrder;
 
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/dto/OrderTemplateSelectionDto.java b/inspect-server/src/main/java/com/ruoyi/inspect/dto/OrderTemplateSelectionDto.java
new file mode 100644
index 0000000..04c1629
--- /dev/null
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/dto/OrderTemplateSelectionDto.java
@@ -0,0 +1,15 @@
+package com.ruoyi.inspect.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/** 涓嬪崟鏃朵繚鐣欑殑鍘熷璁板綍妯℃澘銆� */
+@Data
+public class OrderTemplateSelectionDto {
+
+    @ApiModelProperty("妯℃澘ID")
+    private Integer templateId;
+
+    @ApiModelProperty("妯℃澘灞曠ず鍙婄敓鎴愰『搴�")
+    private Integer sort;
+}
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/dto/SaveOrderDevicesDto.java b/inspect-server/src/main/java/com/ruoyi/inspect/dto/SaveOrderDevicesDto.java
new file mode 100644
index 0000000..6260bf9
--- /dev/null
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/dto/SaveOrderDevicesDto.java
@@ -0,0 +1,12 @@
+package com.ruoyi.inspect.dto;
+
+import lombok.Data;
+
+import java.util.List;
+
+/** 妫�楠屽崟姒傚喌涓殑璁惧閫夋嫨銆� */
+@Data
+public class SaveOrderDevicesDto {
+    private Integer orderId;
+    private List<Integer> deviceIds;
+}
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/dto/SaveTemplateContextDto.java b/inspect-server/src/main/java/com/ruoyi/inspect/dto/SaveTemplateContextDto.java
new file mode 100644
index 0000000..7190653
--- /dev/null
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/dto/SaveTemplateContextDto.java
@@ -0,0 +1,14 @@
+package com.ruoyi.inspect.dto;
+
+import lombok.Data;
+
+import java.util.Map;
+
+/** 鏂拌仛鍘熷璁板綍妯℃澘鎸夊潗鏍囦繚瀛樸�� */
+@Data
+public class SaveTemplateContextDto {
+    private Integer orderId;
+    private Integer sampleId;
+    private Integer templateId;
+    private Map<String, Object> values;
+}
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/dto/VerifyPlanDto.java b/inspect-server/src/main/java/com/ruoyi/inspect/dto/VerifyPlanDto.java
new file mode 100644
index 0000000..10a2185
--- /dev/null
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/dto/VerifyPlanDto.java
@@ -0,0 +1,16 @@
+package com.ruoyi.inspect.dto;
+
+import lombok.Data;
+
+/** 妫�楠屽鏍告彁浜ゅ弬鏁般�� */
+@Data
+public class VerifyPlanDto {
+    private Integer orderId;
+    private String laboratory;
+    /** 1 閫氳繃锛�0 閫�鍥炪�� */
+    private Integer type;
+    private String tell;
+    private Integer userId;
+    /** 鏂拌仛妫�楠屽崟鏁翠綋缁撹锛�1 鍚堟牸锛�0 涓嶅悎鏍笺�� */
+    private Integer insResult;
+}
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/dto/WriteInspectionConditionDto.java b/inspect-server/src/main/java/com/ruoyi/inspect/dto/WriteInspectionConditionDto.java
new file mode 100644
index 0000000..518d940
--- /dev/null
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/dto/WriteInspectionConditionDto.java
@@ -0,0 +1,70 @@
+package com.ruoyi.inspect.dto;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.time.LocalDate;
+import java.util.List;
+
+/** 淇濆瓨璁㈠崟绾у娉ㄦ垨妯℃澘绾ф楠屾潯浠躲�� */
+@Data
+public class WriteInspectionConditionDto {
+
+    private Integer id;
+    private Integer templateId;
+    private String remark;
+    private String temperature;
+    private String humidity;
+
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private LocalDate detectionTime;
+
+    private String detectionPlace;
+    private List<Integer> deviceIds;
+    private Integer insResult;
+
+    @JsonIgnore
+    private boolean temperaturePresent;
+    @JsonIgnore
+    private boolean humidityPresent;
+    @JsonIgnore
+    private boolean detectionTimePresent;
+    @JsonIgnore
+    private boolean detectionPlacePresent;
+    @JsonIgnore
+    private boolean deviceIdsPresent;
+    @JsonIgnore
+    private boolean insResultPresent;
+
+    public void setTemperature(String temperature) {
+        this.temperature = temperature;
+        this.temperaturePresent = true;
+    }
+
+    public void setHumidity(String humidity) {
+        this.humidity = humidity;
+        this.humidityPresent = true;
+    }
+
+    public void setDetectionTime(LocalDate detectionTime) {
+        this.detectionTime = detectionTime;
+        this.detectionTimePresent = true;
+    }
+
+    public void setDetectionPlace(String detectionPlace) {
+        this.detectionPlace = detectionPlace;
+        this.detectionPlacePresent = true;
+    }
+
+    public void setDeviceIds(List<Integer> deviceIds) {
+        this.deviceIds = deviceIds;
+        this.deviceIdsPresent = true;
+    }
+
+    public void setInsResult(Integer insResult) {
+        this.insResult = insResult;
+        this.insResultPresent = true;
+    }
+}
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/mapper/InsOrderTemplateDeviceMapper.java b/inspect-server/src/main/java/com/ruoyi/inspect/mapper/InsOrderTemplateDeviceMapper.java
new file mode 100644
index 0000000..899d52c
--- /dev/null
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/mapper/InsOrderTemplateDeviceMapper.java
@@ -0,0 +1,29 @@
+package com.ruoyi.inspect.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.inspect.pojo.InsOrderTemplateDevice;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.List;
+
+@Mapper
+public interface InsOrderTemplateDeviceMapper extends BaseMapper<InsOrderTemplateDevice> {
+
+    @Select({"<script>",
+            "select id from device where id in",
+            "<foreach collection='deviceIds' item='deviceId' open='(' separator=',' close=')'>",
+            "#{deviceId}",
+            "</foreach>",
+            "</script>"})
+    List<Integer> selectExistingDeviceIds(@Param("deviceIds") List<Integer> deviceIds);
+
+    @Select("select distinct d.management_number " +
+            "from ins_order_template_device otd " +
+            "join device d on d.id = otd.device_id " +
+            "where otd.ins_order_id = #{orderId} " +
+            "and d.management_number is not null and d.management_number <> '' " +
+            "order by d.management_number")
+    List<String> selectManagementNumbersByOrderId(@Param("orderId") Integer orderId);
+}
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/mapper/InsReportMapper.java b/inspect-server/src/main/java/com/ruoyi/inspect/mapper/InsReportMapper.java
index 99c120f..dfca56d 100644
--- a/inspect-server/src/main/java/com/ruoyi/inspect/mapper/InsReportMapper.java
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/mapper/InsReportMapper.java
@@ -34,6 +34,10 @@
 
     String getLaboratoryByName(@Param("name") String name, @Param("type") String type);
 
+    /** 鎸夋姤鍛婄紪鍒跺垪琛ㄧ浉鍚岀殑鏁版嵁鑼冨洿鏌ヨ鍙笅杞界殑鎶ュ憡銆� */
+    InsReport selectDownloadableReport(@Param("id") Integer id,
+                                       @Param("createOrderUser") Integer createOrderUser);
+
     /**
      * 鎶ュ憡鎶ヨ〃瀵煎嚭
      * @return
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrder.java b/inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrder.java
index bf590ae..7a8f596 100644
--- a/inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrder.java
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrder.java
@@ -195,8 +195,25 @@
     @ApiModelProperty("婀垮害")
     private String humidity;
 
+    @TableField(exist = false)
+    @ApiModelProperty("褰撳墠濉啓鐜鏉′欢鐨勬ā鏉縄D")
+    private Integer templateId;
+
     @ApiModelProperty("鏍峰搧鐘舵��")
     private String sampleStatus;
+
+    @ApiModelProperty("閫佹牱鏃ユ湡")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private LocalDate sampleDate;
+
+    @ApiModelProperty("鐢熶骇鏃ユ湡")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private LocalDate productionDate;
+
+    @ApiModelProperty("鐢熶骇鎵瑰彿")
+    private String productionBatch;
 
     @ApiModelProperty("瀛e害鎶芥牱椤圭洰id")
     private Integer quarterItemId;
@@ -213,6 +230,9 @@
     @ApiModelProperty("绗竴娆℃彁浜ゆ椂闂�")
     private LocalDateTime firstSubmitDate;
 
+    @ApiModelProperty("棣栨淇濆瓨妫�楠屾ā鏉垮�兼椂闂�")
+    private LocalDateTime firstInspectDate;
+
     @ApiModelProperty("鏍锋満鍛�(闄や簡鍏朵粬鎴愬搧澶栬繖涓瓧娈靛繀閫�,鐢ㄤ簬浼佷笟寰俊鎺ㄩ��)")
     @TableField(exist = false)
     private Integer issueUser;
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrderStandardTemplate.java b/inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrderStandardTemplate.java
index e682cf6..9de0324 100644
--- a/inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrderStandardTemplate.java
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrderStandardTemplate.java
@@ -1,10 +1,12 @@
 package com.ruoyi.inspect.pojo;
 
 import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
 import java.time.LocalDateTime;
+import java.time.LocalDate;
 import java.util.Date;
 
 /**
@@ -54,5 +56,27 @@
     @ApiModelProperty("妯℃澘缂栧彿")
     private String number;
 
+    @ApiModelProperty("妯℃澘灞曠ず鍙婄敓鎴愰『搴�")
+    private Integer sort;
+
+    @ApiModelProperty("鍘熷璁板綍宸插~鍐欏崟鍏冩牸锛屾寜鍧愭爣淇濆瓨")
+    private String recordValues;
+
+    @ApiModelProperty("璇ユā鏉挎楠屾椂鐨勬俯搴�")
+    private String temperature;
+
+    @ApiModelProperty("璇ユā鏉挎楠屾椂鐨勬箍搴�")
+    private String humidity;
+
+    @ApiModelProperty("妫�娴嬫棩鏈�")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private LocalDate detectionTime;
+
+    @ApiModelProperty("妫�娴嬪湴鐐�")
+    private String detectionPlace;
+
+    @ApiModelProperty("妯℃澘妫�楠岀粨璁猴細0涓嶅悎鏍硷紝1鍚堟牸")
+    private Integer insResult;
+
 }
 
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrderTemplateDevice.java b/inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrderTemplateDevice.java
new file mode 100644
index 0000000..4b635b0
--- /dev/null
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrderTemplateDevice.java
@@ -0,0 +1,28 @@
+package com.ruoyi.inspect.pojo;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/** 璁㈠崟妯℃澘涓庢楠岃澶囧叧绯汇�� */
+@Data
+@TableName("ins_order_template_device")
+public class InsOrderTemplateDevice {
+
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty("璁㈠崟ID")
+    private Integer insOrderId;
+
+    @ApiModelProperty("妯℃澘ID")
+    private Integer templateId;
+
+    @ApiModelProperty("璁惧ID")
+    private Integer deviceId;
+
+    @ApiModelProperty("灞曠ず椤哄簭")
+    private Integer sort;
+}
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsProduct.java b/inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsProduct.java
index ba9cb6a..ad6ec1e 100644
--- a/inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsProduct.java
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsProduct.java
@@ -149,6 +149,9 @@
     @ApiModelProperty("澶栭敭锛歴tandard_template琛╥d")
     private Integer templateId;
 
+    @ApiModelProperty("鍘熷璁板綍妯℃澘涓殑妫�楠岄」琛屽彿")
+    private Integer templateRowIndex;
+
     @TableField(exist = false)
     @ApiModelProperty("妯℃澘鍐呭")
     private List<JSONObject> template;
@@ -165,6 +168,14 @@
     @ApiModelProperty("妯℃澘鍚嶇О")
     private String templateName;
 
+    @TableField(exist = false)
+    @ApiModelProperty("璁㈠崟妯℃澘椤电椤哄簭")
+    private Integer templateSort;
+
+    @TableField(exist = false)
+    @ApiModelProperty("璁㈠崟妯℃澘宸插~鍐欏崟鍏冩牸锛屾寜鍧愭爣淇濆瓨")
+    private String recordValues;
+
     @ApiModelProperty("瀛楀吀绫诲瀷")
     private String dic;
 
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/service/InsOrderPlanService.java b/inspect-server/src/main/java/com/ruoyi/inspect/service/InsOrderPlanService.java
index 0aa126a..47e7609 100644
--- a/inspect-server/src/main/java/com/ruoyi/inspect/service/InsOrderPlanService.java
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/service/InsOrderPlanService.java
@@ -5,6 +5,9 @@
 import com.ruoyi.inspect.dto.InsOrderPlanDTO;
 import com.ruoyi.inspect.dto.InsOrderPlanProductDto;
 import com.ruoyi.inspect.dto.SaveInsContextDto;
+import com.ruoyi.inspect.dto.SaveOrderDevicesDto;
+import com.ruoyi.inspect.dto.SaveTemplateContextDto;
+import com.ruoyi.inspect.dto.VerifyPlanDto;
 import com.ruoyi.inspect.pojo.InsOrderFactoryVerify;
 import com.ruoyi.inspect.pojo.InsOrderFile;
 import com.ruoyi.inspect.pojo.InsProduct;
@@ -28,15 +31,21 @@
 
     void saveInsContext(SaveInsContextDto saveInsContextDto);
 
+    void saveTemplateContext(SaveTemplateContextDto saveTemplateContextDto);
+
+    List<Integer> getOrderDeviceIds(Integer orderId);
+
+    void saveOrderDevices(SaveOrderDevicesDto saveOrderDevicesDto);
+
     Map<String, Object> doInsOrder(Integer id, String laboratory);
 
     int upPlanUser(Integer userId, Integer orderId,String sonLaboratory);
 
-    int submitPlan(Integer orderId, String laboratory, Integer verifyUser, String entrustCode, Boolean registerInsResults, Integer reportType);
+    int submitPlan(Integer orderId, String laboratory, Integer verifyUser, String entrustCode, Boolean registerInsResults);
 
     List<InsProduct> getInsProduct(InsOrderPlanProductDto insOrderPlanProductDto);
 
-    Map<String,Object> checkSubmitPlan(Integer orderId, String laboratory, Integer reportType);
+    Map<String,Object> checkSubmitPlan(Integer orderId, String laboratory);
 
     IPage<InsOrderFile> getFileList(Page page, InsOrderFile insOrderFile);
 
@@ -47,6 +56,8 @@
 
     int rawMaterialVerifyPlan(Integer orderId, String laboratory, Integer type, String tell, Integer userId);
 
+    int rawMaterialVerifyPlan(VerifyPlanDto verifyPlanDto);
+
     /**
      * 鐢电紗閰嶇疆. 鏌ョ湅鏍囪瘑
      * @param id
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/service/InsOrderService.java b/inspect-server/src/main/java/com/ruoyi/inspect/service/InsOrderService.java
index cb87b26..aba8a3e 100644
--- a/inspect-server/src/main/java/com/ruoyi/inspect/service/InsOrderService.java
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/service/InsOrderService.java
@@ -35,6 +35,8 @@
 
     int addInsOrder(List<SampleProductDto> list, InsOrder insOrder);
 
+    int addNewJuInsOrder(InsPlaceOrderDto insPlaceOrderDto);
+
     Map<String, Object> getInsOrder(Integer id);
 
     int upInsOrderOfState(InsOrder insOrder);
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/service/InsProductService.java b/inspect-server/src/main/java/com/ruoyi/inspect/service/InsProductService.java
index 2e0fbcb..f0b55fb 100644
--- a/inspect-server/src/main/java/com/ruoyi/inspect/service/InsProductService.java
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/service/InsProductService.java
@@ -4,7 +4,7 @@
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.ruoyi.inspect.dto.InsProductBindingDto;
-import com.ruoyi.inspect.pojo.InsOrder;
+import com.ruoyi.inspect.dto.WriteInspectionConditionDto;
 import com.ruoyi.inspect.pojo.InsProduct;
 import com.ruoyi.performance.pojo.AuxiliaryOutputWorkingHours;
 
@@ -22,7 +22,7 @@
 
     int updateInspected(Integer id,String ids);
 
-    boolean write(InsOrder insOrder);
+    boolean write(WriteInspectionConditionDto dto);
 
     IPage<InsProduct> selectNoProducts(Page page, InsProduct insProduct, Integer orderId, String ids);
 
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/service/InsReportService.java b/inspect-server/src/main/java/com/ruoyi/inspect/service/InsReportService.java
index 7c50a7c..33de910 100644
--- a/inspect-server/src/main/java/com/ruoyi/inspect/service/InsReportService.java
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/service/InsReportService.java
@@ -41,6 +41,9 @@
 
     String downAll(String ids);
 
+    /** 鍗曚唤鎶ュ憡 Word 涓嬭浇锛宑reateOrderUser 鐢ㄤ簬澶嶇敤鎶ュ憡缂栧埗鐨勬暟鎹寖鍥淬�� */
+    void download(Integer id, Integer createOrderUser, HttpServletResponse response);
+
     int upAll(MultipartFile file) throws IOException;
 
     /**
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsOrderPlanServiceImpl.java b/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsOrderPlanServiceImpl.java
index 2c9bf30..cdf9f44 100644
--- a/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsOrderPlanServiceImpl.java
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsOrderPlanServiceImpl.java
@@ -82,6 +82,7 @@
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 /**
  * 妫�楠屼换鍔�-涓氬姟瀹炵幇灞�
@@ -155,6 +156,8 @@
     @Resource
     private InsOrderDeviceRecordMapper insOrderDeviceRecordMapper;
     @Resource
+    private InsOrderTemplateDeviceMapper insOrderTemplateDeviceMapper;
+    @Resource
     private InsOrderDeviceRecordService insOrderDeviceRecordService;
     @Resource
     private ISysDictTypeService iSysDictTypeService;
@@ -222,14 +225,7 @@
 
     @Override
     public Map<String, Object> doInsOrder(Integer id, String laboratory) {
-        InsOrder insOrder = new InsOrder();
-        insOrder.setId(id);
         InsOrder order = insOrderMapper.selectById(id);
-        if (BeanUtil.isEmpty(order.getInsTime())) {
-            insOrder.setInsTime(LocalDateTime.now());
-            insOrderMapper.updateById(insOrder);
-            insOrderStateMapper.update(null, Wrappers.<InsOrderState>lambdaUpdate().eq(InsOrderState::getInsOrderId, id).eq(InsOrderState::getLaboratory, laboratory).set(InsOrderState::getInsTime, LocalDateTime.now()).set(InsOrderState::getInsState, 1));
-        }
         Map<String, Object> map = insOrderService.getInsOrderAndSample(id, laboratory);
         List<SampleProductDto> list = JSON.parseArray(JSON.toJSONString(map.get("sampleProduct")), SampleProductDto.class);
         map.put("sampleProduct", list);
@@ -268,60 +264,94 @@
                 insProducts = insSampleMapper.getInsProduct6(dto.getId(), dto.getLaboratory(), dto.getRawMaterialTag());
                 break;
         }
+        // 鏌ヨ璁㈠崟Id
+        InsOrder order = insOrderMapper.selectFirstSubmit(dto.getId());
+        if (order == null) {
+            return BeanUtil.isEmpty(insProducts) ? null : insProducts;
+        }
+        insProducts = mergeDirectTemplateSnapshots(order, dto.getId(), dto.getLaboratory(), insProducts);
         if (BeanUtil.isEmpty(insProducts)) {
             return null;
         }
-        // 鏌ヨ璁㈠崟Id
-        InsOrder order = insOrderMapper.selectFirstSubmit(dto.getId());
-        getTemplateThing(order, insProducts);
+        getTemplateThing(order, insProducts, dto.getLaboratory());
         return insProducts;
     }
 
+    /**
+     * 鐩寸粦妯℃澘璁㈠崟鐨勯〉绛炬潵婧愭槸璁㈠崟妯℃澘蹇収锛岃�屼笉鏄凡鐢熸垚鐨勬楠岄」銆�
+     *
+     * 浠嶄繚鐣欐瘡涓ā鏉夸笅宸叉湁鐨勬楠岄」璁板綍锛涘鏋滄煇涓ā鏉挎病鏈夆�滄楠岄」鈥濇爣璁帮紝琛ヤ竴鏉′粎鐢ㄤ簬
+     * 鎵胯浇妯℃澘鍐呭鐨勮繑鍥炲璞★紝浣垮墠绔篃鑳藉睍绀恒�佸~鍐欒妯℃澘銆�
+     */
+    private List<InsProduct> mergeDirectTemplateSnapshots(InsOrder order, Integer sampleId,
+                                                           String laboratory, List<InsProduct> products) {
+        List<InsProduct> source = products == null ? new ArrayList<>() : products;
+        List<InsOrderStandardTemplate> snapshots = insOrderStandardTemplateService.list(
+                Wrappers.<InsOrderStandardTemplate>lambdaQuery()
+                        .eq(InsOrderStandardTemplate::getInsOrderId, order.getId())
+                        .orderByAsc(InsOrderStandardTemplate::getSort, InsOrderStandardTemplate::getId));
+        if (snapshots.isEmpty()) {
+            return source;
+        }
+        InsSample sample = insSampleMapper.selectById(sampleId);
+        boolean directTemplateOrder = source.stream()
+                .anyMatch(product -> product.getTemplateRowIndex() != null)
+                || isDirectTemplateSample(order, sample);
+        if (!directTemplateOrder) {
+            return source;
+        }
+
+        Map<Integer, List<InsProduct>> productsByTemplate = source.stream()
+                .filter(product -> product.getTemplateId() != null)
+                .collect(Collectors.groupingBy(InsProduct::getTemplateId, LinkedHashMap::new, Collectors.toList()));
+        List<InsProduct> result = new ArrayList<>();
+        for (InsOrderStandardTemplate snapshot : snapshots) {
+            List<InsProduct> templateProducts = productsByTemplate.remove(snapshot.getTemplateId());
+            if (CollectionUtils.isNotEmpty(templateProducts)) {
+                templateProducts.forEach(product -> product.setTemplateSort(snapshot.getSort()));
+                result.addAll(templateProducts);
+                continue;
+            }
+            InsProduct templateCarrier = new InsProduct();
+            templateCarrier.setInsSampleId(sampleId);
+            templateCarrier.setTemplateId(snapshot.getTemplateId());
+            templateCarrier.setSonLaboratory(laboratory);
+            templateCarrier.setState(1);
+            templateCarrier.setSort(snapshot.getSort());
+            templateCarrier.setTemplateSort(snapshot.getSort());
+            result.add(templateCarrier);
+        }
+        // 鍏煎鍘嗗彶寮傚父鏁版嵁锛氳鍗曞揩鐓т互澶栫殑妫�楠岄」浠嶇劧杩斿洖锛屼絾鏀惧湪蹇収椤电涔嬪悗銆�
+        for (List<InsProduct> remaining : productsByTemplate.values()) {
+            result.addAll(remaining);
+        }
+        source.stream().filter(product -> product.getTemplateId() == null).forEach(result::add);
+        return result;
+    }
+
+    private boolean isDirectTemplateSample(InsOrder order, InsSample sample) {
+        return sample != null
+                && Objects.equals(sample.getInsOrderId(), order.getId())
+                && sample.getStandardMethodListId() == null
+                && StringUtils.isBlank(sample.getFactory())
+                && StringUtils.isBlank(sample.getSampleType())
+                && StringUtils.isBlank(sample.getModel())
+                && StringUtils.isBlank(order.getFactory())
+                && StringUtils.isBlank(order.getSampleType());
+    }
+
     @Override
-    public Map<String,Object> checkSubmitPlan(Integer orderId, String laboratory, Integer reportType) {
+    public Map<String,Object> checkSubmitPlan(Integer orderId, String laboratory) {
         Map<String, Object> map = new HashMap<>();
         List<String> collect = new ArrayList<>();
-        if (Objects.equals(DETECTION_REPORT, reportType)) {
-            map.put("errorMsg", collect);
+        if (isDirectTemplateOrder(orderId)) {
+            map.put("errorMsg", Collections.emptyList());
+            map.put("missingExportItems", getMissingTemplateExportValues(orderId));
             map.put("unInsOrderCount", 0L);
             return map;
         }
-        List<InsSample> insSamples = insSampleMapper.selectList(Wrappers.<InsSample>lambdaQuery().eq(InsSample::getInsOrderId, orderId).select(InsSample::getId));
-        List<Integer> ids = insSamples.stream().map(a -> a.getId()).collect(Collectors.toList());
-        List<InsProduct> insProducts = insProductMapper.selectList(Wrappers.<InsProduct>lambdaQuery()
-                .in(InsProduct::getInsSampleId, ids)
-                .eq(InsProduct::getSonLaboratory, laboratory)
-                .eq(InsProduct::getState, 1)
-                .eq(InsProduct::getInsResult, 0));
-
-        // 杩囨护鍒ゆ柇鏄湁澶嶆祴鐨勫�兼槸鍚堟牸鐨�
-        List<InsProduct> productList = insProducts.stream().filter(insProduct -> {
-            // 鏌ヨ涓嶅悎鏍煎娴�
-            Long count = insUnqualifiedRetestProductMapper.selectCount(Wrappers.<InsUnqualifiedRetestProduct>lambdaQuery()
-                    .eq(InsUnqualifiedRetestProduct::getInsProductId, insProduct.getId())
-                    .ne(InsUnqualifiedRetestProduct::getInsResult, 0));
-            if (count != 2) {
-                return true;
-            }
-            return false;
-        }).collect(Collectors.toList());
-        if (productList.size() > 0) {
-            collect = productList.stream().map(insProduct -> {
-                return insProduct.getInspectionItem() + "-" + insProduct.getInspectionItemSubclass();
-            }).collect(Collectors.toList());
-        }
-        //鏌ヨifs鎷嗗垎璁㈠崟鏄惁鏈夊凡涓嬪崟浣嗘槸鏈瀹岀殑鍗曞瓙
-        long count = 0L;
-        InsOrder insOrder = insOrderMapper.selectById(orderId);
-        if(Objects.nonNull(insOrder.getIfsInventoryId())){
-            IfsInventoryQuantity one = ifsInventoryQuantityMapper.selectById(insOrder.getIfsInventoryId());
-            //杩囨护鍑轰笉鍚堟牸鎴栨湭鎻愪氦鐨勫崟瀛�
-            count = ifsInventoryQuantityMapper.selectSplitOrderList(one.getPartNo(),one.getLineNo(),one.getReleaseNo(),one.getReceiptNo(),one.getOrderNo())
-                    .stream()
-                    .filter(f->(Objects.nonNull(f.getInsOrderId()) && !Objects.equals(f.getInsOrderId(),orderId)) && (Objects.isNull(f.getInsResult()) || 0==f.getInsResult())).count();
-        }
-        map.put("errorMsg",collect);
-        map.put("unInsOrderCount",count);
+        map.put("errorMsg", collect);
+        map.put("unInsOrderCount", 0L);
         return map;
     }
 
@@ -557,6 +587,254 @@
         });
     }
 
+    /**
+     * 鏂拌仛璁㈠崟鐨勫灞� key 鏄� templateId锛屼笉鏄� ins_product.id锛涘洜姝ゅ崟鐙寜妯℃澘蹇収淇濆瓨銆�
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void saveTemplateContext(SaveTemplateContextDto dto) {
+        if (dto.getOrderId() == null || dto.getSampleId() == null || dto.getTemplateId() == null) {
+            throw new ErrorException("璁㈠崟銆佹牱鍝佸拰妯℃澘涓嶈兘涓虹┖");
+        }
+        InsSample sample = insSampleMapper.selectById(dto.getSampleId());
+        if (sample == null || !Objects.equals(sample.getInsOrderId(), dto.getOrderId())) {
+            throw new ErrorException("鏍峰搧涓嶅睘浜庡綋鍓嶆楠屽崟");
+        }
+        InsOrderStandardTemplate snapshot = insOrderStandardTemplateService.getOne(
+                Wrappers.<InsOrderStandardTemplate>lambdaQuery()
+                        .eq(InsOrderStandardTemplate::getInsOrderId, dto.getOrderId())
+                        .eq(InsOrderStandardTemplate::getTemplateId, dto.getTemplateId())
+                        .last("limit 1"));
+        if (snapshot == null) {
+            throw new ErrorException("鏈壘鍒板綋鍓嶈鍗曠殑鍘熷璁板綍妯℃澘蹇収");
+        }
+        List<InsProduct> products = insProductMapper.selectList(Wrappers.<InsProduct>lambdaQuery()
+                .eq(InsProduct::getInsSampleId, dto.getSampleId())
+                .eq(InsProduct::getTemplateId, dto.getTemplateId())
+                .isNotNull(InsProduct::getTemplateRowIndex)
+                .eq(InsProduct::getState, 1));
+
+        JSONObject values = JSON.parseObject(JSON.toJSONString(dto.getValues() == null
+                ? Collections.emptyMap() : dto.getValues()));
+        List<String> exportValueCoordinates = getExportValueCoordinates(snapshot.getThing()).values().stream()
+                .flatMap(Collection::stream)
+                .distinct()
+                .collect(Collectors.toList());
+        if (exportValueCoordinates.isEmpty()) {
+            throw new ErrorException("妯℃澘蹇呴』鑷冲皯閰嶇疆涓�涓鍑哄�兼壒娉�");
+        }
+
+        // 瀵煎嚭鍊兼寜妯℃澘閰嶇疆锛屼笉鍐嶈姹傛瘡涓楠岄」鍖哄潡鍒嗗埆閰嶇疆銆備竴涓ā鏉垮彇绗竴涓鍑哄�间綔涓虹粨鏋溿��
+        String exportValueCoordinate = exportValueCoordinates.get(0);
+        JSONObject exportValueCell = values.getJSONObject(exportValueCoordinate);
+        String exportValue = exportValueCell == null || exportValueCell.get("v") == null
+                ? "" : String.valueOf(exportValueCell.get("v"));
+        Integer userId = SecurityUtils.getUserId().intValue();
+        for (InsProduct product : products) {
+            InsProduct update = new InsProduct();
+            update.setId(product.getId());
+            update.setLastValue(exportValue);
+            update.setUpdateUser(userId);
+            insProductMapper.updateById(update);
+            insProductUserMapper.insert(new InsProductUser(null, userId, LocalDateTime.now(), product.getId()));
+        }
+        snapshot.setRecordValues(values.toJSONString());
+        insOrderStandardTemplateService.updateById(snapshot);
+
+        if (hasSavedTemplateValue(values)) {
+            markInspectionStarted(dto.getOrderId(), products);
+        }
+
+        Long unfinished = insProductMapper.selectCount(Wrappers.<InsProduct>lambdaQuery()
+                .eq(InsProduct::getInsSampleId, dto.getSampleId())
+                .isNotNull(InsProduct::getTemplateRowIndex)
+                .eq(InsProduct::getState, 1)
+                .and(w -> w.isNull(InsProduct::getLastValue).or().eq(InsProduct::getLastValue, "")));
+        sample.setInsState(unfinished == 0 ? 2 : 1);
+        insSampleMapper.updateById(sample);
+    }
+
+    /**
+     * 绗竴娆℃垚鍔熶繚瀛樹换鎰忛潪绌烘ā鏉垮�兼椂锛岃褰曠湡瀹炴楠屽紑濮嬫椂闂村苟灏嗕换鍔′粠寰呮鏀逛负鍦ㄦ銆�
+     */
+    private void markInspectionStarted(Integer orderId, List<InsProduct> products) {
+        LocalDateTime now = LocalDateTime.now();
+        insOrderMapper.update(null, Wrappers.<InsOrder>lambdaUpdate()
+                .eq(InsOrder::getId, orderId)
+                .isNull(InsOrder::getFirstInspectDate)
+                .set(InsOrder::getFirstInspectDate, now)
+                // 淇濈暀鍘熷瓧娈典緵妫�楠屼换鍔″垪琛ㄤ腑鐨勨�滄楠屽紑濮嬫椂闂粹�濈户缁睍绀恒��
+                .set(InsOrder::getInsTime, now));
+
+        Set<String> laboratories = products.stream()
+                .map(InsProduct::getSonLaboratory)
+                .filter(StringUtils::isNotBlank)
+                .collect(Collectors.toSet());
+        if (!laboratories.isEmpty()) {
+            insOrderStateMapper.update(null, Wrappers.<InsOrderState>lambdaUpdate()
+                    .eq(InsOrderState::getInsOrderId, orderId)
+                    .in(InsOrderState::getLaboratory, laboratories)
+                    .eq(InsOrderState::getInsState, 0)
+                    .set(InsOrderState::getInsTime, now)
+                    .set(InsOrderState::getInsState, 1));
+        }
+    }
+
+    private boolean hasSavedTemplateValue(JSONObject values) {
+        if (values == null || values.isEmpty()) {
+            return false;
+        }
+        for (Object rawCell : values.values()) {
+            JSONObject cell = rawCell instanceof JSONObject
+                    ? (JSONObject) rawCell
+                    : JSON.parseObject(JSON.toJSONString(rawCell));
+            if (cell != null && hasMeaningfulValue(cell.get("v"))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean hasMeaningfulValue(Object value) {
+        if (value == null) {
+            return false;
+        }
+        if (value instanceof CharSequence) {
+            return StringUtils.isNotBlank(value.toString());
+        }
+        if (value instanceof Collection) {
+            return ((Collection<?>) value).stream().anyMatch(this::hasMeaningfulValue);
+        }
+        if (value instanceof Map) {
+            return ((Map<?, ?>) value).values().stream().anyMatch(this::hasMeaningfulValue);
+        }
+        return true;
+    }
+
+    private NavigableMap<Integer, List<String>> getExportValueCoordinates(String compressedThing) {
+        if (StrUtil.isBlank(compressedThing)) {
+            throw new ErrorException("鍘熷璁板綍妯℃澘涓虹┖");
+        }
+        String thing;
+        try {
+            thing = GZipUtil.uncompress(compressedThing);
+        } catch (Exception ignored) {
+            thing = compressedThing;
+        }
+        JSONArray cellData = JSON.parseObject(thing).getJSONArray("data").getJSONObject(0).getJSONArray("celldata");
+        NavigableMap<Integer, List<String>> result = new TreeMap<>();
+        Integer inspectionResultColumn = null;
+        for (Object item : cellData) {
+            JSONObject cell = JSON.parseObject(JSON.toJSONString(item));
+            Integer column = cell.getInteger("c");
+            JSONObject value = cell.getJSONObject("v");
+            String displayValue = value == null ? null : value.getString("v");
+            if (inspectionResultColumn == null && "妫�楠岀粨鏋�".equals(StringUtils.trim(displayValue))) {
+                inspectionResultColumn = column;
+            }
+        }
+        for (Object item : cellData) {
+            JSONObject cell = JSON.parseObject(JSON.toJSONString(item));
+            JSONObject value = cell.getJSONObject("v");
+            JSONObject ps = value == null ? null : value.getJSONObject("ps");
+            if (ps == null || !"瀵煎嚭鍊�".equals(ps.getString("value"))) {
+                continue;
+            }
+            Integer row = cell.getInteger("r");
+            Integer column = cell.getInteger("c");
+            if (row != null && column != null) {
+                // 鈥滃鍑哄�尖�濇爣鍦ㄦ娴嬮」鐩爣棰樺崟鍏冩牸鏃讹紝鐪熸瀵煎嚭鐨勫�兼槸鍚屼竴琛屸�滄楠岀粨鏋溾�濆垪鐨勫崟鍏冩牸銆�
+                String coordinate = row + "-" + column;
+                String resultCoordinate = row + "-" + inspectionResultColumn;
+                if (inspectionResultColumn != null && !Objects.equals(column, inspectionResultColumn)) {
+                    coordinate = resultCoordinate;
+                }
+                result.computeIfAbsent(row, ignored -> new ArrayList<>()).add(coordinate);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * 鐩寸粦妯℃澘璁㈠崟鍙牎楠屾ā鏉夸腑鏍囪涓衡�滃鍑哄�尖�濈殑鍗曞厓鏍硷紝涓嶄娇鐢ㄦ楠岄」鐘舵�佹垨 last_value 鍒ゅ畾銆�
+     */
+    private List<String> getMissingTemplateExportValues(Integer orderId) {
+        List<InsOrderStandardTemplate> snapshots = insOrderStandardTemplateService.list(
+                Wrappers.<InsOrderStandardTemplate>lambdaQuery()
+                        .eq(InsOrderStandardTemplate::getInsOrderId, orderId)
+                        .orderByAsc(InsOrderStandardTemplate::getSort, InsOrderStandardTemplate::getId));
+        List<String> missing = new ArrayList<>();
+        if (snapshots.isEmpty()) {
+            missing.add("褰撳墠璁㈠崟鏈壘鍒板師濮嬭褰曟ā鏉垮揩鐓�");
+            return missing;
+        }
+        for (InsOrderStandardTemplate snapshot : snapshots) {
+            Integer templateId = snapshot.getTemplateId();
+            List<String> coordinates = getExportValueCoordinates(snapshot.getThing()).values().stream()
+                    .flatMap(Collection::stream)
+                    .distinct()
+                    .collect(Collectors.toList());
+            String templateName = StringUtils.defaultIfBlank(snapshot.getName(), "妯℃澘ID " + templateId);
+            if (coordinates.isEmpty()) {
+                missing.add(templateName + "锛堟湭閰嶇疆瀵煎嚭鍊硷級");
+                continue;
+            }
+
+            JSONObject recordValues = StrUtil.isBlank(snapshot.getRecordValues())
+                    ? new JSONObject() : JSON.parseObject(snapshot.getRecordValues());
+            // 淇濆瓨鏃跺悓鏍峰彧鍙栨ā鏉夸腑鐨勭涓�涓鍑哄�硷紝鎻愪氦鏍¢獙涓庝繚瀛樺彛寰勪繚鎸佷竴鑷淬��
+            String coordinate = coordinates.get(0);
+            JSONObject cell = recordValues.getJSONObject(coordinate);
+            Object value = cell == null ? null : cell.get("v");
+            if (value == null || StringUtils.isBlank(String.valueOf(value))) {
+                missing.add(templateName + "锛�" + coordinate + "锛�");
+            }
+        }
+        return missing;
+    }
+
+    @Override
+    public List<Integer> getOrderDeviceIds(Integer orderId) {
+        return insOrderDeviceRecordMapper.selectList(Wrappers.<InsOrderDeviceRecord>lambdaQuery()
+                        .eq(InsOrderDeviceRecord::getInsOrderId, orderId))
+                .stream().map(InsOrderDeviceRecord::getDeviceId).filter(Objects::nonNull).distinct()
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void saveOrderDevices(SaveOrderDevicesDto dto) {
+        if (dto.getOrderId() == null || insOrderMapper.selectById(dto.getOrderId()) == null) {
+            throw new ErrorException("妫�楠屽崟涓嶅瓨鍦�");
+        }
+        Set<Integer> deviceIds = dto.getDeviceIds() == null ? Collections.emptySet() : dto.getDeviceIds().stream()
+                .filter(Objects::nonNull).collect(Collectors.toSet());
+        List<InsOrderDeviceRecord> oldRecords = insOrderDeviceRecordMapper.selectList(
+                Wrappers.<InsOrderDeviceRecord>lambdaQuery().eq(InsOrderDeviceRecord::getInsOrderId, dto.getOrderId()));
+        Set<Integer> oldIds = oldRecords.stream().map(InsOrderDeviceRecord::getDeviceId).collect(Collectors.toSet());
+        if (!oldIds.isEmpty()) {
+            insOrderDeviceRecordMapper.delete(Wrappers.<InsOrderDeviceRecord>lambdaQuery()
+                    .eq(InsOrderDeviceRecord::getInsOrderId, dto.getOrderId())
+                    .notIn(!deviceIds.isEmpty(), InsOrderDeviceRecord::getDeviceId, deviceIds));
+        }
+        InsOrder order = insOrderMapper.selectById(dto.getOrderId());
+        User user = userMapper.selectById(SecurityUtils.getUserId().intValue());
+        List<InsOrderDeviceRecord> additions = deviceIds.stream().filter(id -> !oldIds.contains(id)).map(id -> {
+            InsOrderDeviceRecord record = new InsOrderDeviceRecord();
+            record.setInsOrderId(dto.getOrderId());
+            record.setDeviceId(id);
+            record.setSampleCode(order.getEntrustCode());
+            record.setUseBefore(1);
+            record.setUseAfter(1);
+            record.setUsePersonId(user.getId());
+            record.setUsePerson(user.getName());
+            return record;
+        }).collect(Collectors.toList());
+        if (!additions.isEmpty()) {
+            insOrderDeviceRecordService.saveBatch(additions);
+        }
+    }
+
     private synchronized void addDeviceRecord(InsSample insSample, Integer userId) {
         InsOrder order = insOrderMapper.selectById(insSample.getInsOrderId());
         User user = userMapper.selectById(userId);
@@ -630,39 +908,55 @@
      * @param order
      * @param insProducts
      */
-    private void getTemplateThing(InsOrder order, List<InsProduct> insProducts) {
+    private void getTemplateThing(InsOrder order, List<InsProduct> insProducts, String laboratory) {
         Set<Integer> set = new HashSet<>();
-        // 鏌ヨ璁㈠崟鐘舵�佸垽鏂槸鍚︽槸鏌ュ巻鍙叉ā鏉�
-        if (order.getIsFirstSubmit() != null && order.getIsFirstSubmit().equals(1)) {
-            InsOrderState insOrderState = insOrderStateMapper.selectOne(Wrappers.<InsOrderState>lambdaQuery()
-                    .eq(InsOrderState::getInsOrderId, order.getId())
-                    .last("limit 1"));
-            if (insOrderState != null && (!insOrderState.getInsState().equals(3) || !insOrderState.getInsState().equals(5))) {
-                for (InsProduct product : insProducts) {
-                    if (product.getTemplateId() == null) {
-                        product.setTemplate(new ArrayList<>());
+        Map<Integer, String> recordValuesByTemplate = new HashMap<>();
+        for (InsOrderStandardTemplate snapshot : insOrderStandardTemplateService.list(
+                Wrappers.<InsOrderStandardTemplate>lambdaQuery()
+                        .eq(InsOrderStandardTemplate::getInsOrderId, order.getId()))) {
+            // record_values 鍦ㄩ娆″~鍐欏墠鍏佽涓虹┖锛汣ollectors.toMap 涓嶆帴鍙� null value銆�
+            if (snapshot.getTemplateId() != null && snapshot.getRecordValues() != null) {
+                recordValuesByTemplate.putIfAbsent(snapshot.getTemplateId(), snapshot.getRecordValues());
+            }
+        }
+        boolean directTemplateOrder = insProducts.stream()
+                .anyMatch(product -> product.getTemplateRowIndex() != null);
+        Long snapshotCount = insOrderStandardTemplateService.count(Wrappers.<InsOrderStandardTemplate>lambdaQuery()
+                .eq(InsOrderStandardTemplate::getInsOrderId, order.getId()));
+        InsOrderState insOrderState = insOrderStateMapper.selectOne(Wrappers.<InsOrderState>lambdaQuery()
+                .eq(InsOrderState::getInsOrderId, order.getId())
+                .eq(StringUtils.isNotBlank(laboratory), InsOrderState::getLaboratory, laboratory)
+                .last("limit 1"));
+        Integer insState = insOrderState == null ? null : insOrderState.getInsState();
+        boolean hasSnapshot = snapshotCount > 0 && (directTemplateOrder
+                || Objects.equals(order.getIsFirstSubmit(), 1));
+        // 寰呮楠屻�佹楠屼腑浣跨敤鍩虹閰嶇疆鐨勬渶鏂版ā鏉匡紱宸叉楠屽強鍚庣画鐘舵�佷娇鐢ㄨ鍗曞揩鐓с��
+        boolean useSnapshot = hasSnapshot && insState != null && insState >= 2;
+        if (useSnapshot) {
+            for (InsProduct product : insProducts) {
+                if (product.getTemplateId() == null) {
+                    product.setTemplate(new ArrayList<>());
+                    continue;
+                }
+                if (set.add(product.getTemplateId())) {
+                    InsOrderStandardTemplate one = insOrderStandardTemplateService.getOne(Wrappers.<InsOrderStandardTemplate>lambdaQuery()
+                            .eq(InsOrderStandardTemplate::getTemplateId, product.getTemplateId())
+                            .eq(InsOrderStandardTemplate::getInsOrderId, order.getId()));
+                    if (one == null || StrUtil.isEmpty(one.getThing())) {
+                        // 蹇収涓嶅畬鏁存椂鍏佽鍚庨潰鐨勬渶鏂版ā鏉挎煡璇㈠厹搴曘��
+                        set.remove(product.getTemplateId());
                         continue;
                     }
-                    String thing = null;
-                    if (product.getTemplateId() != null && set.add(product.getTemplateId())) {
-                        // 鏌ヨ鍘嗗彶妯℃澘
-                        InsOrderStandardTemplate one = insOrderStandardTemplateService.getOne(Wrappers.<InsOrderStandardTemplate>lambdaQuery()
-                                .eq(InsOrderStandardTemplate::getTemplateId, product.getTemplateId())
-                                .eq(InsOrderStandardTemplate::getInsOrderId, order.getId()));
-                        thing = one.getThing();
-                        if (StrUtil.isNotEmpty(thing)) {
-                            thing = GZipUtil.uncompress(thing);
-                            JSONObject sheet = JSON.parseObject(thing).getJSONArray("data").getJSONObject(0);
-                            JSONObject config = sheet.getJSONObject("config");
-                            List<JSONObject> cellData = JSON.parseArray(JSON.toJSONString(sheet.get("celldata")), JSONObject.class);
-                            Map<String, Object> style = new HashMap<>();
-                            style.put("rowlen", config.get("rowlen"));
-                            style.put("columnlen", config.get("columnlen"));
-                            product.setTemplate(cellData);
-                            product.setStyle(style);
-                            product.setTemplateName(one.getName());
-                        }
-                    }
+                    String thing = GZipUtil.uncompress(one.getThing());
+                    JSONObject sheet = JSON.parseObject(thing).getJSONArray("data").getJSONObject(0);
+                    JSONObject config = sheet.getJSONObject("config");
+                    List<JSONObject> cellData = JSON.parseArray(JSON.toJSONString(sheet.get("celldata")), JSONObject.class);
+                    Map<String, Object> style = new HashMap<>();
+                    style.put("rowlen", config.get("rowlen"));
+                    style.put("columnlen", config.get("columnlen"));
+                    product.setTemplate(cellData);
+                    product.setStyle(style);
+                    product.setTemplateName(one.getName());
                 }
             }
         }
@@ -686,6 +980,7 @@
                 product.setStyle(style);
                 product.setTemplateName(standardTemplateService.getStandTempNameById(product.getTemplateId()));
             }
+            product.setRecordValues(recordValuesByTemplate.get(product.getTemplateId()));
         }
     }
 
@@ -775,10 +1070,8 @@
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public int submitPlan(Integer orderId, String laboratory, Integer verifyUser, String entrustCode, Boolean registerInsResults, Integer reportType) {
-        if (!Objects.equals(DETECTION_REPORT, reportType) && !Objects.equals(INSPECTION_REPORT, reportType)) {
-            throw new ErrorException("璇烽�夋嫨鎶ュ憡绫诲瀷");
-        }
+    public int submitPlan(Integer orderId, String laboratory, Integer verifyUser, String entrustCode, Boolean registerInsResults) {
+        int reportType = DETECTION_REPORT;
         InsOrder order = insOrderMapper.selectOne(Wrappers.<InsOrder>lambdaQuery()
                 .eq(InsOrder::getId, orderId)
                 .last("FOR UPDATE"));
@@ -794,30 +1087,41 @@
         }
 
         // 2. 鍒ゆ柇璇ヨ鍗曟槸鍚︽槸绗竴娆$敓浜�(鍚庣画鎶ュ憡鐢熸垚鍙彇绗竴娆℃彁浜ゆ椂闂�)
-        if (!(order.getIsFirstSubmit() != null && order.getIsFirstSubmit().equals(1))) {
+        boolean firstSubmit = !Objects.equals(order.getIsFirstSubmit(), 1);
+        if (firstSubmit) {
             insOrderMapper.update(null, Wrappers.<InsOrder>lambdaUpdate()
                     .eq(InsOrder::getId, orderId)
                     .set(InsOrder::getIsFirstSubmit, 1)
                     .set(InsOrder::getFirstSubmitDate, LocalDateTime.now()));
         }
 
-        // 3. 鍒ゆ柇鏄惁鏈夋湭妫�椤�
+        // 3. 鐩寸粦妯℃澘浠呮牎楠屸�滃鍑哄�尖�濓紱鏅�氳鍗曟部鐢ㄥ師妫�楠岄」鏍¢獙銆�
         List<InsSample> insSamples = insSampleMapper.selectList(Wrappers.<InsSample>lambdaQuery()
                 .eq(InsSample::getInsOrderId, orderId).select(InsSample::getId));
         List<Integer> InsSampleIds = insSamples.stream().map(InsSample::getId).collect(Collectors.toList());
-        List<InsProduct> insProducts = insProductMapper.selectList(Wrappers.<InsProduct>lambdaQuery()
-                .in(InsProduct::getInsSampleId, InsSampleIds)
-                .eq(InsProduct::getSonLaboratory, laboratory)
-                .eq(InsProduct::getState, 1)
-                .and(wrapper -> {
-                    if (Objects.equals(reportType, DETECTION_REPORT)) {
-                        wrapper.isNull(InsProduct::getLastValue).or().eq(InsProduct::getLastValue, "");
-                    } else {
-                        wrapper.isNull(InsProduct::getInsResult).or().eq(InsProduct::getInsResult, 2);
-                    }
-                })
-                .ne(InsProduct::getIsBinding, 1));
-        if (Objects.equals(reportType, INSPECTION_REPORT)) {
+        boolean directTemplateOrder = isDirectTemplateOrder(orderId);
+        List<InsProduct> insProducts;
+        if (directTemplateOrder) {
+            List<String> missingExportValues = getMissingTemplateExportValues(orderId);
+            if (!missingExportValues.isEmpty()) {
+                throw new ErrorException("妯℃澘涓殑瀵煎嚭鍊间笉鑳戒负绌猴細\n" + String.join("\n", missingExportValues));
+            }
+            insProducts = Collections.emptyList();
+        } else {
+            insProducts = insProductMapper.selectList(Wrappers.<InsProduct>lambdaQuery()
+                    .in(InsProduct::getInsSampleId, InsSampleIds)
+                    .eq(InsProduct::getSonLaboratory, laboratory)
+                    .eq(InsProduct::getState, 1)
+                    .and(wrapper -> {
+                        if (Objects.equals(reportType, DETECTION_REPORT)) {
+                            wrapper.isNull(InsProduct::getLastValue).or().eq(InsProduct::getLastValue, "");
+                        } else {
+                            wrapper.isNull(InsProduct::getInsResult).or().eq(InsProduct::getInsResult, 2);
+                        }
+                    })
+                    .ne(InsProduct::getIsBinding, 1));
+        }
+        if (!directTemplateOrder && Objects.equals(reportType, INSPECTION_REPORT)) {
             List<InsProduct> insProducts1 = insProductMapper.selectFiberInsProduct(InsSampleIds, laboratory);
             insProducts.addAll(insProducts1);
         }
@@ -883,20 +1187,16 @@
             }
         });
 
-        // 8.鎻愪氦鐢熸垚鎶ュ憡
-        this.generateReport(orderId, reportType);
+        // 8.棣栨鎻愪氦鏃跺皢鏈妫�楠屼娇鐢ㄧ殑鏈�鏂版ā鏉垮喕缁撲负璁㈠崟蹇収銆�
+        Long snapshotCount = insOrderStandardTemplateService.count(Wrappers.<InsOrderStandardTemplate>lambdaQuery()
+                .eq(InsOrderStandardTemplate::getInsOrderId, orderId));
+        if (firstSubmit || snapshotCount == 0) {
+            this.templateCopy(orderId, InsSampleIds);
+        }
 
-        // 9.娣诲姞涓存椂pdf鐢熸垚鍦板潃
-        InsReport report = insReportMapper.selectOne(Wrappers.<InsReport>lambdaQuery()
-                .eq(InsReport::getInsOrderId, orderId));
-        String tempUrlPdf = this.wordToPdfTemp(report.getUrl().replace("/word", wordUrl));
-        report.setTempUrlPdf("/word/" + tempUrlPdf);
-        insReportMapper.updateById(report);
+        // 9. 妫�娴嬫姤鍛婁粎鍦ㄥ叏閮ㄥ鏍搁�氳繃鏃剁敓鎴愶紝鎻愪氦澶嶆牳闃舵涓嶇敓鎴愭姤鍛娿��
 
-        // 10.鍘熷璁板綍妯℃澘澶嶅埗(娣诲姞澶囦唤, 閬垮厤淇敼鍘熷妯℃澘褰卞搷鍒板凡缁忓畬鎴愮殑鍗曞瓙)
-        this.templateCopy(orderId, InsSampleIds);
-
-        // 11.鎴愬搧鎶芥牱娣诲姞鍚堟牸鐘舵��
+        // 10.鎴愬搧鎶芥牱娣诲姞鍚堟牸鐘舵��
         // 鍒ゆ柇鏄惁鏈夋娊鏍蜂俊鎭�
         if (isInspectionReport && order.getQuarterItemId() != null) {
             // 鍒ゆ柇鏄惁鏈変笉鍚堟牸
@@ -904,7 +1204,7 @@
         }
 
 
-        // 12.娣诲姞璁㈠崟璐圭敤缁熻淇℃伅
+        // 11.娣诲姞璁㈠崟璐圭敤缁熻淇℃伅
         List<InsProduct> productList = insProductMapper.selectProductByOrderId(orderId);
         // 鍒犻櫎鍘熸湰璐圭敤淇℃伅
         insOrderRatesService.remove(Wrappers.<InsOrderRates>lambdaQuery()
@@ -927,7 +1227,7 @@
         insOrderRatesService.saveBatch(orderRatesList);
 
 
-        // 13.鍙戦�佷紒涓氬井淇¢�氱煡
+        // 12.鍙戦�佷紒涓氬井淇¢�氱煡
         // 鏌ヨ鍘熸潗鏂�
         IfsInventoryQuantity ifsInventoryQuantity = ifsInventoryQuantityMapper.selectById(order.getIfsInventoryId());
         // 鏌ヨ鏍峰搧淇℃伅
@@ -946,20 +1246,20 @@
             }
             //鍙戦�佷紒涓氬井淇℃秷鎭�氱煡  鎻愪氦澶嶆牳
             try {
-                WxCpUtils.inform(sendUserAccount, message, null);
+//                WxCpUtils.inform(sendUserAccount, message, null);
             } catch (Exception e) {
                 throw new RuntimeException(e);
             }
         });
 
-        // 14.ifs绉诲簱(鍘熸潗鏂欓渶瑕佽繘琛岀Щ搴撴搷浣�) --> 鏈�鍚庢墽琛�,鍥犱负澶辫触鏃犳硶鍥炴粴
+        // 13.ifs绉诲簱(鍘熸潗鏂欓渶瑕佽繘琛岀Щ搴撴搷浣�) --> 鏈�鍚庢墽琛�,鍥犱负澶辫触鏃犳硶鍥炴粴
         if (ifsInventoryQuantity != null && isInspectionReport) {
             // 鐧昏妫�楠岀粨鏋�
             // 鍒ゆ柇鏄惁鏈変笉鍚堟牸, 鏈変笉鍚堟牸涓嶈兘绉诲簱
             // todo: ifs绉诲簱
             insReportService.isRawMaterial(order,registerInsResults,false);
 
-            // 15 鍒ゆ柇褰撳墠鏍峰搧鏄惁涓哄師鏉愭枡, 鍘熸潗鏂欓渶瑕佽繘琛屾暟鎹垎鏋�, 鍒ゆ柇涔嬪墠10鏉℃暟鎹悓涓�涓緵搴斿晢, 鍚屼竴涓楠岄」鐨勫亸宸槸鍚﹁秴杩�10%
+            // 14 鍒ゆ柇褰撳墠鏍峰搧鏄惁涓哄師鏉愭枡, 鍘熸潗鏂欓渶瑕佽繘琛屾暟鎹垎鏋�, 鍒ゆ柇涔嬪墠10鏉℃暟鎹悓涓�涓緵搴斿晢, 鍚屼竴涓楠岄」鐨勫亸宸槸鍚﹁秴杩�10%
             // 鏌ヨifs淇℃伅鑾峰彇鑾峰彇鍓�10涓緵搴斿晢涓�鏍风殑, 妫�楠岄」涓�鏍蜂俊鎭�
             threadPoolTaskExecutor.execute(() -> {
                 // 娣诲姞鍒嗘瀽鏁版嵁
@@ -967,7 +1267,7 @@
             });
 
 
-        } else if (isInspectionReport) {
+        } else if (isInspectionReport && !directTemplateOrder) {
             // 淇敼鎴愬搧鐘舵��
             // 鍒ゆ柇鏄惁鏈変笉鍚堟牸
             Long unqualifiedCount = insReportService.getUnqualifiedCount(order);
@@ -1124,29 +1424,78 @@
      * @param ids
      */
     private void templateCopy(Integer orderId, List<Integer> ids) {
-        // 鍒犻櫎鍘熸湰妯℃澘
-        insOrderStandardTemplateService.remove(Wrappers.<InsOrderStandardTemplate>lambdaQuery()
-                .eq(InsOrderStandardTemplate::getInsOrderId, orderId));
-        // 澶嶅埗妯℃澘
-        Set<Integer> set = new HashSet<>();
+        // 淇濈暀妯℃澘椤电椤哄簭锛屽苟鍦ㄩ娆℃彁浜ゅ鏍告椂鐢ㄦ楠屾湡闂撮噰鐢ㄧ殑鏈�鏂版ā鏉垮埛鏂板揩鐓с��
+        // 鐩寸粦妯℃澘璁㈠崟蹇呴』浠ュ師璁㈠崟蹇収涓烘ā鏉挎竻鍗曪紝涓嶈兘鍙寜 ins_product 閲嶅缓锛�
+        // 娌℃湁鈥滄楠岄」鈥濇爣璁扮殑妯℃澘涓嶄細鐢熸垚 ins_product锛屽惁鍒欐彁浜ゅ悗浼氳閿欒鍒犻櫎銆�
+        List<InsOrderStandardTemplate> oldSnapshots = insOrderStandardTemplateService.list(
+                Wrappers.<InsOrderStandardTemplate>lambdaQuery()
+                        .eq(InsOrderStandardTemplate::getInsOrderId, orderId)
+                        .orderByAsc(InsOrderStandardTemplate::getSort, InsOrderStandardTemplate::getId));
+        Map<Integer, Integer> templateSortMap = new HashMap<>();
+        Map<Integer, String> templateRecordValuesMap = new HashMap<>();
+        Map<Integer, String> templateTemperatureMap = new HashMap<>();
+        Map<Integer, String> templateHumidityMap = new HashMap<>();
+        Map<Integer, InsOrderStandardTemplate> oldSnapshotMap = new LinkedHashMap<>();
+        int nextTemplateSort = 0;
+        for (InsOrderStandardTemplate snapshot : oldSnapshots) {
+            if (snapshot.getTemplateId() != null) {
+                oldSnapshotMap.putIfAbsent(snapshot.getTemplateId(), snapshot);
+                if (snapshot.getSort() != null) {
+                    templateSortMap.putIfAbsent(snapshot.getTemplateId(), snapshot.getSort());
+                    nextTemplateSort = Math.max(nextTemplateSort, snapshot.getSort() + 1);
+                }
+                if (snapshot.getRecordValues() != null) {
+                    templateRecordValuesMap.putIfAbsent(snapshot.getTemplateId(), snapshot.getRecordValues());
+                }
+                if (snapshot.getTemperature() != null) {
+                    templateTemperatureMap.putIfAbsent(snapshot.getTemplateId(), snapshot.getTemperature());
+                }
+                if (snapshot.getHumidity() != null) {
+                    templateHumidityMap.putIfAbsent(snapshot.getTemplateId(), snapshot.getHumidity());
+                }
+            }
+        }
+
+        LinkedHashSet<Integer> templateIds = new LinkedHashSet<>();
+        if (isDirectTemplateOrder(orderId)) {
+            // 鍏堝姞鍏ヨ鍗曞師鏉ョ粦瀹氱殑鍏ㄩ儴妯℃澘锛屽寘鎷笉鐢熸垚妫�楠岄」璁板綍鐨勬ā鏉裤��
+            templateIds.addAll(oldSnapshotMap.keySet());
+        }
         List<InsProduct> insProductList = insProductMapper.selectList(Wrappers.<InsProduct>lambdaQuery()
                 .in(InsProduct::getInsSampleId, ids)
                 .select(InsProduct::getTemplateId));
+        insProductList.stream()
+                .map(InsProduct::getTemplateId)
+                .filter(Objects::nonNull)
+                .forEach(templateIds::add);
 
-        for (InsProduct product : insProductList) {
-            // 鏌ヨ妯℃澘id
-            if (product.getTemplateId() != null && set.add(product.getTemplateId())) {
-                StandardTemplate standardTemplate = standardTemplateService.getById(product.getTemplateId());
-                if (standardTemplate != null) {
-                    InsOrderStandardTemplate insOrderStandardTemplate = new InsOrderStandardTemplate();
-                    insOrderStandardTemplate.setInsOrderId(orderId);
-                    insOrderStandardTemplate.setTemplateId(standardTemplate.getId());
-                    insOrderStandardTemplate.setNumber(standardTemplate.getNumber());
-                    insOrderStandardTemplate.setName(standardTemplate.getName());
-                    insOrderStandardTemplate.setThing(standardTemplate.getThing());
-                    insOrderStandardTemplateService.save(insOrderStandardTemplate);
-                }
+        List<InsOrderStandardTemplate> refreshedSnapshots = new ArrayList<>();
+        for (Integer templateId : templateIds) {
+            StandardTemplate standardTemplate = standardTemplateService.getById(templateId);
+            InsOrderStandardTemplate oldSnapshot = oldSnapshotMap.get(templateId);
+            if (standardTemplate == null && oldSnapshot == null) {
+                continue;
             }
+            InsOrderStandardTemplate refreshed = new InsOrderStandardTemplate();
+            refreshed.setInsOrderId(orderId);
+            refreshed.setTemplateId(templateId);
+            refreshed.setNumber(standardTemplate == null ? oldSnapshot.getNumber() : standardTemplate.getNumber());
+            refreshed.setName(standardTemplate == null ? oldSnapshot.getName() : standardTemplate.getName());
+            refreshed.setRemark(standardTemplate == null ? oldSnapshot.getRemark() : standardTemplate.getRemark());
+            refreshed.setThing(standardTemplate == null ? oldSnapshot.getThing() : standardTemplate.getThing());
+            refreshed.setRecordValues(templateRecordValuesMap.get(templateId));
+            refreshed.setTemperature(templateTemperatureMap.get(templateId));
+            refreshed.setHumidity(templateHumidityMap.get(templateId));
+            Integer templateSort = templateSortMap.get(templateId);
+            refreshed.setSort(templateSort == null ? nextTemplateSort++ : templateSort);
+            refreshedSnapshots.add(refreshed);
+        }
+
+        // 鏂板揩鐓у凡瀹屾暣缁勮鍚庡啀鏇挎崲锛涗簨鍔″け璐ユ椂浼氭暣浣撳洖婊氥��
+        insOrderStandardTemplateService.remove(Wrappers.<InsOrderStandardTemplate>lambdaQuery()
+                .eq(InsOrderStandardTemplate::getInsOrderId, orderId));
+        if (!refreshedSnapshots.isEmpty()) {
+            insOrderStandardTemplateService.saveBatch(refreshedSnapshots);
         }
     }
 
@@ -1162,6 +1511,43 @@
     @Override
     @Transactional(rollbackFor = Exception.class)
     public int rawMaterialVerifyPlan(Integer orderId, String laboratory, Integer type, String tell, Integer userId) {
+        VerifyPlanDto dto = new VerifyPlanDto();
+        dto.setOrderId(orderId);
+        dto.setLaboratory(laboratory);
+        dto.setType(type);
+        dto.setTell(tell);
+        dto.setUserId(userId);
+        return rawMaterialVerifyPlan(dto);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public int rawMaterialVerifyPlan(VerifyPlanDto dto) {
+        Integer orderId = dto.getOrderId();
+        String laboratory = dto.getLaboratory();
+        Integer type = dto.getType();
+        String tell = dto.getTell();
+        Integer userId = dto.getUserId();
+        if (orderId == null || laboratory == null || type == null) {
+            throw new ErrorException("澶嶆牳鍙傛暟涓嶅畬鏁�");
+        }
+        InsOrderState currentState = insOrderStateMapper.selectOne(Wrappers.<InsOrderState>lambdaQuery()
+                .eq(InsOrderState::getInsOrderId, orderId)
+                .eq(InsOrderState::getLaboratory, laboratory)
+                .last("limit 1"));
+        InsReport existingReport = insReportMapper.selectOne(Wrappers.<InsReport>lambdaQuery()
+                .eq(InsReport::getInsOrderId, orderId)
+                .last("limit 1"));
+        if (currentState != null && Objects.equals(currentState.getInsState(), 5)
+                && existingReport != null && StringUtils.isNotBlank(existingReport.getUrl())) {
+            // 闃叉澶嶆牳椤甸潰閲嶅鐐瑰嚮鎴栫綉缁滈噸璇曢噸澶嶇敓鎴愭姤鍛娿�侀噸澶嶅彂璧锋姤鍛婂鎵广��
+            return 1;
+        }
+        boolean directTemplateOrder = isDirectTemplateOrder(orderId);
+        if (directTemplateOrder && Objects.equals(type, 1)
+                && !Objects.equals(dto.getInsResult(), 0) && !Objects.equals(dto.getInsResult(), 1)) {
+            throw new ErrorException("璇烽�夋嫨妫�楠屽崟缁撹");
+        }
         Integer num = (type == 1 ? 5 : 4);
         LocalDateTime now = LocalDateTime.now();
         insOrderStateMapper.update(null, Wrappers.<InsOrderState>lambdaUpdate()
@@ -1175,31 +1561,65 @@
                 .eq(InsOrderState::getInsOrderId, orderId)
                 .ne(InsOrderState::getInsState, 5));
         InsReport report = insReportMapper.selectOne(Wrappers.<InsReport>lambdaQuery()
-                .eq(InsReport::getInsOrderId, orderId));
+                .eq(InsReport::getInsOrderId, orderId)
+                .last("limit 1"));
+        Integer writeUserId = report != null && report.getWriteUserId() != null
+                ? report.getWriteUserId() : resolveInspectionUserId(orderId, laboratory);
         // 鏌ヨ璁㈠崟
         InsOrder order = insOrderMapper.selectById(orderId);
+        if (directTemplateOrder && Objects.equals(type, 1)) {
+            List<Integer> templateResults = insOrderStandardTemplateService.list(
+                            Wrappers.<InsOrderStandardTemplate>lambdaQuery()
+                                    .eq(InsOrderStandardTemplate::getInsOrderId, orderId)
+                                    .select(InsOrderStandardTemplate::getInsResult))
+                    .stream().map(InsOrderStandardTemplate::getInsResult)
+                    .collect(Collectors.toList());
+            if (templateResults.isEmpty()
+                    || templateResults.stream().anyMatch(result -> !Objects.equals(result, 0)
+                            && !Objects.equals(result, 1))) {
+                throw new ErrorException("璇峰厛濉啓鍏ㄩ儴妯℃澘鐨勬楠岀粨璁�");
+            }
+            Integer aggregateResult = templateResults.stream().allMatch(result -> Objects.equals(result, 1))
+                    ? 1 : 0;
+            insOrderMapper.update(null, Wrappers.<InsOrder>lambdaUpdate()
+                    .eq(InsOrder::getId, orderId)
+                    .set(InsOrder::getInsResult, aggregateResult));
+            order.setInsResult(aggregateResult);
+        }
         if (count == 0 && num == 5) {
-            // 淇敼鎶ュ憡鐘舵��
-            insReportMapper.update(null, Wrappers.<InsReport>lambdaUpdate()
-                    .eq(InsReport::getInsOrderId, orderId)
-                    .set(InsReport::getIsPass, 1));
-
             // 淇敼璁㈠崟鐘舵��
             insOrderMapper.update(null, Wrappers.<InsOrder>lambdaUpdate()
                     .eq(InsOrder::getId, orderId)
                     .set(InsOrder::getInsState, 5));
 
+            // 妫�楠屼汉鍛樻彁浜ゅ鏍告椂涓嶅啀鐢熸垚鎶ュ憡锛涗粎鍦ㄥ叏閮ㄨ瘯楠屽澶嶆牳閫氳繃鍚庣敓鎴愭娴嬫姤鍛娿��
+            if (report == null || StringUtils.isBlank(report.getUrl())) {
+                this.generateReport(orderId, DETECTION_REPORT, writeUserId);
+                report = insReportMapper.selectOne(Wrappers.<InsReport>lambdaQuery()
+                        .eq(InsReport::getInsOrderId, orderId)
+                        .last("limit 1"));
+            }
+            if (report == null || StringUtils.isBlank(report.getUrl())) {
+                throw new ErrorException("妫�娴嬫姤鍛婄敓鎴愬け璐�");
+            }
+            if (report.getTempUrlPdf() == null) {
+                String tempUrlPdf = this.wordToPdfTemp(report.getUrl().replace("/word", wordUrl));
+                report.setTempUrlPdf("/word/" + tempUrlPdf);
+            }
+            report.setIsPass(1);
+            insReportMapper.updateById(report);
+
             //鐢熸垚鍘熸潗鏂欒繘璐ч獙璇佸師濮嬭褰曞埌闄勪欢閲�
             if (order.getTypeSource() != null && order.getTypeSource().equals(1)) {
-                this.reportFactoryVerify(orderId, userId, report.getWriteUserId());
+                this.reportFactoryVerify(orderId, userId, writeUserId);
             }
 
             // 鏌ヨ妫�楠屼换鍔$殑妫�楠屼换鍔�
             // 鎶ュ憡鐩栦笂鎵瑰噯浜�
-            insReportService.writeReport(report.getId(), userId, report.getWriteUserId());
+            insReportService.writeReport(report.getId(), userId, writeUserId);
 
             // 妫�楠屼汉
-            String userName = insProductMapper.selectUserById(report.getWriteUserId()).get("name");
+            String userName = insProductMapper.selectUserById(writeUserId).get("name");
 
             // 澶嶆牳浜�
             Integer checkUserId = SecurityUtils.getUserId().intValue();
@@ -1248,7 +1668,7 @@
 
             // 鍙戦�佹秷鎭殑浜�
             // 鏌ヨ鍙戦�佷汉淇℃伅
-            String sendUserAccount = insProductMapper.selectUserById(report.getWriteUserId()).get("account");
+            String sendUserAccount = insProductMapper.selectUserById(writeUserId).get("account");
 
             // 鍙戦�佷紒涓氬井淇¢�氱煡(妫�楠屼换鍔¢��鍥�)
             threadPoolTaskExecutor.execute(() -> {
@@ -1280,11 +1700,43 @@
         return 1;
     }
 
+    private boolean isDirectTemplateOrder(Integer orderId) {
+        List<InsSample> samples = insSampleMapper.selectList(Wrappers.<InsSample>lambdaQuery()
+                .eq(InsSample::getInsOrderId, orderId));
+        if (samples.isEmpty()) {
+            return false;
+        }
+        Long snapshotCount = insOrderStandardTemplateService.count(
+                Wrappers.<InsOrderStandardTemplate>lambdaQuery()
+                        .eq(InsOrderStandardTemplate::getInsOrderId, orderId));
+        InsOrder order = snapshotCount == 0 ? null : insOrderMapper.selectById(orderId);
+        if (order != null && samples.stream().anyMatch(sample -> isDirectTemplateSample(order, sample))) {
+            return true;
+        }
+        // 鍏煎宸茬粡鐢熸垚浜嗘ā鏉胯鍙枫�佷絾璁㈠崟鍩虹瀛楁涓嶅畬鏁寸殑鍘嗗彶鐩寸粦璁㈠崟銆�
+        return insProductMapper.selectCount(Wrappers.<InsProduct>lambdaQuery()
+                .in(InsProduct::getInsSampleId, samples.stream().map(InsSample::getId).collect(Collectors.toList()))
+                .isNotNull(InsProduct::getTemplateRowIndex)) > 0;
+    }
+
+    private Integer resolveInspectionUserId(Integer orderId, String laboratory) {
+        InsSampleUser inspector = insSampleUserMapper.selectOne(Wrappers.<InsSampleUser>lambdaQuery()
+                .eq(InsSampleUser::getInsSampleId, orderId)
+                .eq(InsSampleUser::getState, 0)
+                .eq(StringUtils.isNotBlank(laboratory), InsSampleUser::getSonLaboratory, laboratory)
+                .orderByDesc(InsSampleUser::getId)
+                .last("limit 1"));
+        if (inspector == null || inspector.getUserId() == null) {
+            throw new ErrorException("鎵句笉鍒板綋鍓嶆楠屼换鍔$殑妫�楠屼汉锛屾棤娉曠敓鎴愭娴嬫姤鍛�");
+        }
+        return inspector.getUserId();
+    }
+
     /**
      * 鐢熸垚鎶ュ憡
      * @param orderId
      */
-    private void generateReport(Integer orderId, Integer reportType) {
+    private void generateReport(Integer orderId, Integer reportType, Integer writeUserId) {
         /*鏍峰搧涓嬬殑椤圭洰鍙鏈変竴涓」鐩笉鍚堟牸鍒欐楠岀粨鏋滀负0,鍚﹀垯涓�1*/
         //杩欓噷鐨刬nsSamples鏄鍗曚笅鐨勬墍鏈夋牱鍝佸寘鎷�("/")
         List<InsSample> insSamples = insSampleMapper.selectList(Wrappers.<InsSample>lambdaQuery().eq(InsSample::getInsOrderId, orderId));
@@ -1312,10 +1764,10 @@
         // 鍒ゆ柇鏄ぇ鎶ュ憡杩樻槸灏忔姤鍛�  杩涘巶灏忔姤鍛�, 鍏朵粬閮芥槸澶ф姤鍛�
         if (insOrder.getOrderType().equals(InsOrderTypeConstants.ENTER_THE_FACTORY)) {
             // 鐢熸垚灏忔姤鍛�
-            addSmallReport(orderId, insOrder, insSamples, reportType);
+            addSmallReport(orderId, insOrder, insSamples, reportType, writeUserId);
         } else {
             //鐢熸垚澶ф姤鍛�
-            addBitReport(orderId, insOrder, reportType);
+            addBitReport(orderId, insOrder, reportType, writeUserId);
         }
 
     }
@@ -1414,7 +1866,7 @@
             return null;
         }
         InsOrder order = insOrderMapper.selectFirstSubmit(dto.getId());
-        getTemplateThing(order, Collections.unmodifiableList(insProducts));
+        getTemplateThing(order, Collections.unmodifiableList(insProducts), dto.getLaboratory());
         return insProducts;
     }
 
@@ -1761,7 +2213,7 @@
      * @param insOrder
      * @param insSamples
      */
-    private void addSmallReport(Integer orderId, InsOrder insOrder, List<InsSample> insSamples, Integer reportType) {
+    private void addSmallReport(Integer orderId, InsOrder insOrder, List<InsSample> insSamples, Integer reportType, Integer writeUserId) {
         InsReport insReport = new InsReport();
         AtomicReference<String> resultCh = new AtomicReference<>("");
         boolean showResult = Objects.equals(reportType, INSPECTION_REPORT);
@@ -1954,12 +2406,9 @@
             template.writeAndClose(Files.newOutputStream(Paths.get(wordUrl, name)));
             insReport.setUrl("/word/" + name);
             insReport.setIsPass(0);
-            insReport.setWriteUserId(SecurityUtils.getUserId().intValue());//鎻愪氦浜�
+            insReport.setWriteUserId(writeUserId);//缂栧埗浜猴紙妫�楠屼汉锛�
             insReport.setWriteTime(LocalDateTime.now());//鎻愪氦鏃堕棿
-            // 鏌ヨ鎶ュ憡, 鍒ゆ柇涔嬪墠鏄惁娣诲姞杩�, 娣诲姞杩囧垹闄�
-            insReportMapper.delete(Wrappers.<InsReport>lambdaQuery()
-                    .eq(InsReport::getInsOrderId, insOrder.getId()));
-            insReportMapper.insert(insReport);
+            saveGeneratedReport(insReport);
             inputStream.close();
         } catch (IOException e) {
             throw new RuntimeException(e);
@@ -2454,7 +2903,7 @@
      * @param insOrder
      * @param
      */
-    private void addBitReport(Integer orderId, InsOrder insOrder, Integer reportType) {
+    private void addBitReport(Integer orderId, InsOrder insOrder, Integer reportType, Integer writeUserId) {
         //samples鏄笉鍖呮嫭甯︽湁"/"鐨勬牱鍝�
         List<SampleProductDto> samples = insSampleMapper.selectSampleProductListByOrderId(orderId);
         InsReport insReport = new InsReport();
@@ -2543,12 +2992,16 @@
                         }
                     }
                 }
-                insOrder.setSampleView(insOrder.getSample());
-                String sampleViewEn = insSampleMapper.getSampleEn(insOrder.getSample());
-                if (StringUtils.isBlank(sampleViewEn)) {
-                    sampleViewEn = insSampleMapper.getSampleEnByObject(insOrder.getSample());
+                if (StringUtils.isBlank(insOrder.getSampleView())) {
+                    insOrder.setSampleView(insOrder.getSample());
                 }
-                insOrder.setSampleViewEn(sampleViewEn);
+                if (StringUtils.isBlank(insOrder.getSampleViewEn())) {
+                    String sampleViewEn = insSampleMapper.getSampleEn(insOrder.getSample());
+                    if (StringUtils.isBlank(sampleViewEn)) {
+                        sampleViewEn = insSampleMapper.getSampleEnByObject(insOrder.getSample());
+                    }
+                    insOrder.setSampleViewEn(sampleViewEn);
+                }
             } else {
                 // 鑾峰緱鎵归噺妫�楠岀殑鎬绘暟
                 max = insOrderMapper.selectSampleMax(a.getId());
@@ -2694,11 +3147,20 @@
         tables.forEach(table -> {
             table.put("tableSize", tables.size() + 1);
         });
-        // 璁惧淇℃伅
-        List<Map<String, String>> deviceList = null;
-        if (CollectionUtils.isNotEmpty(deviceSet)) {
-            deviceList = insOrderMapper.selectDeviceList(deviceSet);
+        // 鏂拌鍗曟寜妯℃澘閫夋嫨璁惧锛涙棫璁㈠崟娌℃湁妯℃澘璁惧鍏崇郴鏃跺洖閫�鍒拌鍗曠骇璁惧璁板綍銆�
+        Set<String> reportDeviceNumbers = insOrderTemplateDeviceMapper
+                .selectManagementNumbersByOrderId(orderId).stream()
+                .filter(StringUtils::isNotBlank)
+                .collect(Collectors.toCollection(LinkedHashSet::new));
+        if (reportDeviceNumbers.isEmpty()) {
+            reportDeviceNumbers.addAll(insOrderDeviceRecordMapper.selectDeviceNumber(orderId).stream()
+                    .map(InsOrderDeviceRecordDto::getManagementNumber)
+                    .filter(StringUtils::isNotBlank)
+                    .collect(Collectors.toList()));
         }
+        reportDeviceNumbers.addAll(deviceSet);
+        List<Map<String, String>> deviceList = reportDeviceNumbers.isEmpty()
+                ? new ArrayList<>() : insOrderMapper.selectDeviceList(reportDeviceNumbers);
         if (CollectionUtils.isNotEmpty(deviceList)) {
             int count = 1;
             for (Map<String, String> stringMap : deviceList) {
@@ -2709,7 +3171,7 @@
 
         Map<String, String> codeStr = new HashMap<>();
         codeStr.put("鎶ュ憡缂栧彿", insReport.getCode());
-        codeStr.put("鏍峰搧鍚嶇О", insOrder.getSample());
+        codeStr.put("鏍峰搧鍚嶇О", defaultReportText(insOrder.getSampleView(), insOrder.getSample()));
         codeStr.put("瑙勬牸鍨嬪彿", samples.get(0).getModel());
         codeStr.put("鍙戞斁鏃ユ湡", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
 
@@ -2803,7 +3265,7 @@
             sendTime = reportInventory.getDeclareDate();
         }
 
-        // 妫�楠屾椂闂�  鎶芥牱鏃堕棿-鎻愪氦鏃堕棿
+        // 妫�娴嬫棩鏈燂細棣栨淇濆瓨妯℃澘鍊兼椂闂�-棣栨鎻愪氦鏃堕棿
         LocalDateTime now = LocalDateTime.now();
         // 鎻愪氦鏃堕棿
         LocalDateTime submitTime = LocalDateTime.now();
@@ -2812,15 +3274,20 @@
             submitTime = insOrder.getFirstSubmitDate();
         }
 
-        String insTime = insOrder.getSendTime().format(DateTimeFormatter.ofPattern("yyyy骞碝M鏈坉d鏃�")) + "-"
+        LocalDateTime firstInspectDate = insOrder.getFirstInspectDate();
+        // 瀛楁涓婄嚎鍓嶇殑鍘嗗彶璁㈠崟娌跨敤鍘熸潵鐨勪笅鍙戞椂闂达紝閬垮厤鏀瑰彉鏃㈡湁鎶ュ憡鍙e緞銆�
+        if (firstInspectDate == null) {
+            firstInspectDate = insOrder.getSendTime();
+        }
+
+        String insTime = firstInspectDate.format(DateTimeFormatter.ofPattern("yyyy骞碝M鏈坉d鏃�")) + "-"
                 + submitTime.format(DateTimeFormatter.ofPattern("yyyy骞碝M鏈坉d鏃�"));
 
-        String insTimeEn = monthNames[insOrder.getSendTime().getMonthValue() - 1] + " " + insOrder.getSendTime().format(DateTimeFormatter.ofPattern("dd, yyyy")) + "-"
+        String insTimeEn = monthNames[firstInspectDate.getMonthValue() - 1] + " " + firstInspectDate.format(DateTimeFormatter.ofPattern("dd, yyyy")) + "-"
                 + monthNames[submitTime.getMonthValue() - 1] + " " + submitTime.format(DateTimeFormatter.ofPattern("dd, yyyy"));
 
-        //妫�楠岄」鐩殑鐜
-        String environment = "";
-        environment = (ObjectUtils.isNotEmpty(insOrder.getTemperature()) ? insOrder.getTemperature() + "鈩� " : "") + (ObjectUtils.isNotEmpty(insOrder.getHumidity()) ? insOrder.getHumidity() + "%" : "");
+        // 妫�楠岀幆澧冿細浼樺厛姹囨�诲悇妯℃澘濉啓鐨勬俯婀垮害锛屾姤鍛婂睍绀哄叏閮ㄦā鏉跨殑鏈�灏忓�煎埌鏈�澶у�笺��
+        String environment = buildReportEnvironment(orderId, insOrder);
         String finalEnvironment = environment;
         LocalDateTime finalSendTime = sendTime;
         String finalResultCh = resultCh;
@@ -2829,7 +3296,7 @@
         List<Map<String, String>> finalDeviceList = deviceList;
         String finalModelStr = modelStr;
 
-        List<Map<String, String>> detectionResultRows = buildDetectionResultRows(samples);
+        List<Map<String, String>> detectionResultRows = buildDetectionResultRows(orderId, samples);
         String productName = defaultReportText(insOrder.getSampleView(), insOrder.getSample());
         String customerName = defaultReportText(custom == null ? null : custom.getCompany(), insOrder.getCompany());
         String detectionCategory = defaultReportText(finalOrderType, "濮旀墭妫�娴�");
@@ -2850,6 +3317,29 @@
                 productionDate = reportInventory.getProductDate().format(DateTimeFormatter.ofPattern("yyyy骞碝M鏈坉d鏃�"));
             }
             batchNo = defaultReportText(reportInventory.getUpdateBatchNo(), reportInventory.getLotBatchNo());
+        }
+        // 鏂拌仛鐩寸粦妯℃澘璁㈠崟锛氭姤鍛婁俊鎭彇濮旀墭淇℃伅鍜岃鍗曟ā鏉垮揩鐓э紝涓嶅啀鍙栬兘鍔涜寖鍥存爣鍑嗘柟娉曘��
+        List<InsOrderStandardTemplate> templateSnapshots = insOrderStandardTemplateService.list(
+                Wrappers.<InsOrderStandardTemplate>lambdaQuery()
+                        .eq(InsOrderStandardTemplate::getInsOrderId, orderId)
+                        .orderByAsc(InsOrderStandardTemplate::getSort, InsOrderStandardTemplate::getId));
+        boolean newJuTemplateOrder = !templateSnapshots.isEmpty() && isDirectTemplateOrder(orderId);
+        if (newJuTemplateOrder) {
+            productName = defaultReportText(insOrder.getSampleView(), insOrder.getSample());
+            finalModelStr = "/";
+            productAndModel = productName;
+            if (insOrder.getSampleDate() != null) {
+                finalSendTime = insOrder.getSampleDate().atStartOfDay();
+            }
+            if (insOrder.getProductionDate() != null) {
+                productionDate = insOrder.getProductionDate().format(DateTimeFormatter.ofPattern("yyyy骞碝M鏈坉d鏃�"));
+            }
+            batchNo = defaultReportText(insOrder.getProductionBatch(), "/");
+            standardMethodText = templateSnapshots.stream()
+                    .map(InsOrderStandardTemplate::getRemark)
+                    .filter(StringUtils::isNotBlank)
+                    .distinct()
+                    .collect(Collectors.joining("锛�"));
         }
         boolean containsOrganicChlorine = detectionResultRows.stream()
                 .map(row -> row.get("itemName"))
@@ -2883,6 +3373,8 @@
         String finalResultRemark = resultRemark;
         String equipmentText = buildEquipmentText(finalDeviceList);
         String issueDate = now.format(DateTimeFormatter.ofPattern("yyyy骞碝M鏈坉d鏃�"));
+        String finalReportModelStr = finalModelStr;
+        LocalDateTime finalReportSendTime = finalSendTime;
         XWPFTemplate template = XWPFTemplate.compile(inputStream, configure).render(
                 new HashMap<String, Object>() {{
                     put("order", insOrder);
@@ -2895,7 +3387,7 @@
                     put("standardMethod", finalStandardMethodText);
                     put("deviceList", finalDeviceList);
                     put("twoCode", null);
-                    put("models", finalModelStr);
+                    put("models", finalReportModelStr);
                     put("productSize", productSize.get());
                     put("createTime", now.format(DateTimeFormatter.ofPattern("yyyy骞碝M鏈坉d鏃�")));
                     put("createTimeEn", monthNames[now.getMonthValue() - 1] + " " + now.format(DateTimeFormatter.ofPattern("dd, yyyy")));
@@ -2907,8 +3399,8 @@
                     put("examineUrl", null);
                     put("ratifyUrl", null);
                     put("orderType", finalOrderType);
-                    put("getTime", finalSendTime.format(DateTimeFormatter.ofPattern("yyyy骞碝M鏈坉d鏃�")));
-                    put("getTimeEn", monthNames[finalSendTime.getMonthValue() - 1] + " " + finalSendTime.format(DateTimeFormatter.ofPattern("dd, yyyy")));
+                    put("getTime", finalReportSendTime.format(DateTimeFormatter.ofPattern("yyyy骞碝M鏈坉d鏃�")));
+                    put("getTimeEn", monthNames[finalReportSendTime.getMonthValue() - 1] + " " + finalReportSendTime.format(DateTimeFormatter.ofPattern("dd, yyyy")));
                     put("seal1", null);
                     put("seal2", null);
                     put("formTypeCh", formType);
@@ -2920,14 +3412,14 @@
                     put("no", insReport.getCode());
                     put("cover", finalProductAndModel);
                     put("product", finalProductName);
-                    put("model", defaultReportText(finalModelStr, "/"));
+                    put("model", defaultReportText(finalReportModelStr, "/"));
                     put("customer", finalCustomerName);
                     put("category", finalDetectionCategory);
                     put("date", issueDate);
                     put("maker", defaultReportText(insOrder.getProduction(), "/"));
                     put("env", finalEnvironment);
                     put("qty", defaultReportText(insOrder.getTestQuantity(), "/"));
-                    put("receive", finalSendTime.format(DateTimeFormatter.ofPattern("yyyy骞碝M鏈坉d鏃�")));
+                    put("receive", finalReportSendTime.format(DateTimeFormatter.ofPattern("yyyy骞碝M鏈坉d鏃�")));
                     put("sampleNo", finalSampleCode);
                     put("appearance", defaultReportText(sampleStatus, "/"));
                     put("sender", defaultReportText(insOrder.getPrepareUser(), "/"));
@@ -2949,22 +3441,34 @@
             Files.createDirectories(Paths.get(wordUrl));
             template.writeAndClose(Files.newOutputStream(Paths.get(wordUrl, name)));
             insReport.setUrl("/word/" + name);
-            insReport.setWriteUserId(SecurityUtils.getUserId().intValue());//鎻愪氦浜�
+            insReport.setWriteUserId(writeUserId);//缂栧埗浜猴紙妫�楠屼汉锛�
             insReport.setWriteTime(LocalDateTime.now());//鎻愪氦鏃堕棿
-            // 鏌ヨ鎶ュ憡, 鍒ゆ柇涔嬪墠鏄惁娣诲姞杩�, 娣诲姞杩囧垹闄�
-            insReportMapper.delete(Wrappers.<InsReport>lambdaQuery()
-                    .eq(InsReport::getInsOrderId, insOrder.getId()));
-            insReportMapper.insert(insReport);
+            saveGeneratedReport(insReport);
             inputStream.close();
         } catch (IOException e) {
             throw new RuntimeException(e);
         }
     }
 
+    private void saveGeneratedReport(InsReport report) {
+        InsReport existing = insReportMapper.selectOne(Wrappers.<InsReport>lambdaQuery()
+                .eq(InsReport::getInsOrderId, report.getInsOrderId())
+                .last("limit 1"));
+        if (existing == null) {
+            insReportMapper.insert(report);
+            return;
+        }
+        report.setId(existing.getId());
+        insReportMapper.updateById(report);
+    }
+
     /**
      * 鏂扮増妫�娴嬫姤鍛婄殑缁撴灉琛ㄥ浐瀹氫负鍥涘垪锛氬簭鍙枫�佹娴嬮」鐩�佸崟浣嶃�佹娴嬬粨鏋溿��
      */
-    private static List<Map<String, String>> buildDetectionResultRows(List<SampleProductDto> samples) {
+    private List<Map<String, String>> buildDetectionResultRows(Integer orderId, List<SampleProductDto> samples) {
+        if (isDirectTemplateOrder(orderId)) {
+            return buildTemplateDetectionResultRows(orderId);
+        }
         List<Map<String, String>> rows = new ArrayList<>();
         if (CollectionUtils.isEmpty(samples)) {
             return rows;
@@ -3000,6 +3504,120 @@
         return rows;
     }
 
+    /**
+     * 鏂拌仛鐩寸粦妯℃澘璁㈠崟锛氫竴涓ā鏉块〉绛惧湪鎶ュ憡涓彧瀵瑰簲涓�鏉℃娴嬮」鐩��
+     */
+    private List<Map<String, String>> buildTemplateDetectionResultRows(Integer orderId) {
+        List<InsOrderStandardTemplate> snapshots = insOrderStandardTemplateService.list(
+                Wrappers.<InsOrderStandardTemplate>lambdaQuery()
+                        .eq(InsOrderStandardTemplate::getInsOrderId, orderId)
+                        .orderByAsc(InsOrderStandardTemplate::getSort, InsOrderStandardTemplate::getId));
+        List<Map<String, String>> rows = new ArrayList<>();
+        int sequence = 1;
+        for (InsOrderStandardTemplate snapshot : snapshots) {
+            TemplateExportCell exportCell = getFirstTemplateExportCell(snapshot.getThing());
+            JSONObject recordValues = StrUtil.isBlank(snapshot.getRecordValues())
+                    ? new JSONObject() : JSON.parseObject(snapshot.getRecordValues());
+            String result = readTemplateCellText(recordValues, exportCell.resultCoordinate, exportCell.templateValues);
+            if (StringUtils.isBlank(result)) {
+                throw new ErrorException("妯℃澘鈥�" + defaultReportText(snapshot.getName(), "妯℃澘ID " + snapshot.getTemplateId())
+                        + "鈥濈殑瀵煎嚭鍊间笉鑳戒负绌�");
+            }
+            String unit = exportCell.unitCoordinate == null ? null
+                    : readTemplateCellText(recordValues, exportCell.unitCoordinate, exportCell.templateValues);
+            String itemName = exportCell.itemCoordinate == null ? null
+                    : readTemplateCellText(recordValues, exportCell.itemCoordinate, exportCell.templateValues);
+
+            Map<String, String> row = new LinkedHashMap<>();
+            row.put("seq", String.valueOf(sequence++));
+            row.put("itemName", defaultReportText(itemName,
+                    defaultReportText(snapshot.getName(), "妯℃澘ID " + snapshot.getTemplateId())));
+            row.put("unit", defaultReportText(unit, "/"));
+            row.put("result", result);
+            rows.add(row);
+        }
+        if (rows.isEmpty()) {
+            throw new ErrorException("褰撳墠璁㈠崟鏈壘鍒板彲鐢熸垚鎶ュ憡鐨勫師濮嬭褰曟ā鏉�");
+        }
+        return rows;
+    }
+
+    private TemplateExportCell getFirstTemplateExportCell(String compressedThing) {
+        if (StrUtil.isBlank(compressedThing)) {
+            throw new ErrorException("鍘熷璁板綍妯℃澘涓虹┖");
+        }
+        String thing;
+        try {
+            thing = GZipUtil.uncompress(compressedThing);
+        } catch (Exception ignored) {
+            thing = compressedThing;
+        }
+        JSONArray cellData = JSON.parseObject(thing).getJSONArray("data").getJSONObject(0).getJSONArray("celldata");
+        Integer resultColumn = null;
+        Integer itemColumn = null;
+        Integer unitColumn = null;
+        List<int[]> exportCells = new ArrayList<>();
+        Map<String, String> templateValues = new HashMap<>();
+        for (Object item : cellData) {
+            JSONObject cell = JSON.parseObject(JSON.toJSONString(item));
+            Integer row = cell.getInteger("r");
+            Integer column = cell.getInteger("c");
+            JSONObject value = cell.getJSONObject("v");
+            if (row == null || column == null || value == null) {
+                continue;
+            }
+            String displayValue = value.get("v") == null ? null : String.valueOf(value.get("v"));
+            templateValues.put(row + "-" + column, displayValue);
+            if (resultColumn == null && "妫�楠岀粨鏋�".equals(StringUtils.trim(displayValue))) {
+                resultColumn = column;
+            }
+            if (itemColumn == null && ("妫�娴嬮」鐩�".equals(StringUtils.trim(displayValue))
+                    || "妫�楠岄」鐩�".equals(StringUtils.trim(displayValue)))) {
+                itemColumn = column;
+            }
+            if (unitColumn == null && "鍗曚綅".equals(StringUtils.trim(displayValue))) {
+                unitColumn = column;
+            }
+            JSONObject ps = value.getJSONObject("ps");
+            if (ps != null && "瀵煎嚭鍊�".equals(ps.getString("value"))) {
+                exportCells.add(new int[]{row, column});
+            }
+        }
+        if (exportCells.isEmpty()) {
+            throw new ErrorException("妯℃澘蹇呴』鑷冲皯閰嶇疆涓�涓鍑哄�兼壒娉�");
+        }
+        exportCells.sort(Comparator.comparingInt((int[] cell) -> cell[0]).thenComparingInt(cell -> cell[1]));
+        int[] firstExport = exportCells.get(0);
+        int resultColumnForRow = resultColumn == null ? firstExport[1] : resultColumn;
+        // 閫氬父鈥滃鍑哄�尖�濇壒娉ㄩ厤缃湪椤圭洰鍚嶇О鍗曞厓鏍硷紱鑻ラ厤缃湪缁撴灉鍗曞厓鏍硷紝鍒欏洖鍒扳�滄娴嬮」鐩�濆垪鍙栧悕绉般��
+        Integer itemColumnForRow = !Objects.equals(firstExport[1], resultColumnForRow)
+                ? firstExport[1] : itemColumn;
+        TemplateExportCell result = new TemplateExportCell();
+        result.resultCoordinate = firstExport[0] + "-" + resultColumnForRow;
+        result.itemCoordinate = itemColumnForRow == null ? null : firstExport[0] + "-" + itemColumnForRow;
+        result.unitCoordinate = unitColumn == null ? null : firstExport[0] + "-" + unitColumn;
+        result.templateValues = templateValues;
+        return result;
+    }
+
+    private String readTemplateCellText(JSONObject recordValues, String coordinate, Map<String, String> templateValues) {
+        JSONObject recordCell = recordValues == null ? null : recordValues.getJSONObject(coordinate);
+        if (recordCell != null && recordCell.get("v") != null) {
+            String value = String.valueOf(recordCell.get("v"));
+            if (StringUtils.isNotBlank(value)) {
+                return value;
+            }
+        }
+        return templateValues.get(coordinate);
+    }
+
+    private static class TemplateExportCell {
+        private String itemCoordinate;
+        private String resultCoordinate;
+        private String unitCoordinate;
+        private Map<String, String> templateValues;
+    }
+
     private static String buildEquipmentText(List<Map<String, String>> deviceList) {
         if (CollectionUtils.isEmpty(deviceList)) {
             return "/";
@@ -3018,6 +3636,54 @@
         return String.join("\n", lines);
     }
 
+    private String buildReportEnvironment(Integer orderId, InsOrder order) {
+        List<InsOrderStandardTemplate> snapshots = insOrderStandardTemplateService.list(
+                Wrappers.<InsOrderStandardTemplate>lambdaQuery()
+                        .eq(InsOrderStandardTemplate::getInsOrderId, orderId)
+                        .select(InsOrderStandardTemplate::getTemperature,
+                                InsOrderStandardTemplate::getHumidity));
+        List<String> temperatures = snapshots.stream()
+                .map(InsOrderStandardTemplate::getTemperature)
+                .filter(StringUtils::isNotBlank)
+                .collect(Collectors.toList());
+        List<String> humidities = snapshots.stream()
+                .map(InsOrderStandardTemplate::getHumidity)
+                .filter(StringUtils::isNotBlank)
+                .collect(Collectors.toList());
+
+        String temperature = formatConditionRange(temperatures, order.getTemperature(), "鈩�");
+        String humidity = formatConditionRange(humidities, order.getHumidity(), "%");
+        return Stream.of(temperature, humidity)
+                .filter(StringUtils::isNotBlank)
+                .collect(Collectors.joining(" "));
+    }
+
+    private static String formatConditionRange(List<String> values, String fallback, String unit) {
+        List<BigDecimal> numbers = new ArrayList<>();
+        List<String> candidates = CollectionUtils.isEmpty(values)
+                ? Collections.singletonList(fallback) : values;
+        Pattern numberPattern = Pattern.compile("-?\\d+(?:\\.\\d+)?");
+        for (String value : candidates) {
+            if (StringUtils.isBlank(value)) {
+                continue;
+            }
+            java.util.regex.Matcher matcher = numberPattern.matcher(value);
+            while (matcher.find()) {
+                numbers.add(new BigDecimal(matcher.group()));
+            }
+        }
+        if (numbers.isEmpty()) {
+            return StringUtils.isBlank(fallback) ? "" : fallback + unit;
+        }
+        BigDecimal minimum = Collections.min(numbers);
+        BigDecimal maximum = Collections.max(numbers);
+        String minimumText = minimum.stripTrailingZeros().toPlainString();
+        if (minimum.compareTo(maximum) == 0) {
+            return minimumText + unit;
+        }
+        return minimumText + "~" + maximum.stripTrailingZeros().toPlainString() + unit;
+    }
+
     private static String getMapText(Map<String, String> map, String... keys) {
         if (map == null) {
             return null;
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsOrderServiceImpl.java b/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsOrderServiceImpl.java
index cb1f287..c62c2ce 100644
--- a/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsOrderServiceImpl.java
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsOrderServiceImpl.java
@@ -17,19 +17,27 @@
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.ruoyi.basic.dto.InventoryDetailDTO;
+import com.ruoyi.basic.dto.StandardTreeTemplateBindingVo;
+import com.ruoyi.basic.dto.TemplateInspectionItemDto;
 import com.ruoyi.basic.mapper.IfsInventoryQuantityMapper;
 import com.ruoyi.basic.mapper.StandardProductListMapper;
 import com.ruoyi.basic.mapper.StructureItemParameterMapper;
 import com.ruoyi.basic.pojo.IfsInventoryQuantity;
 import com.ruoyi.basic.pojo.StandardProductList;
+import com.ruoyi.basic.pojo.StandardMethod;
 import com.ruoyi.basic.pojo.StructureItemParameter;
 import com.ruoyi.basic.pojo.StructureTestObject;
+import com.ruoyi.basic.pojo.StandardTree;
+import com.ruoyi.basic.pojo.StandardTemplate;
+import com.ruoyi.basic.service.StandardTemplateService;
+import com.ruoyi.basic.service.StandardMethodService;
+import com.ruoyi.basic.service.StandardTreeService;
+import com.ruoyi.basic.utils.StandardTreePathParser;
 import com.ruoyi.common.constant.InsOrderTypeConstants;
 import com.ruoyi.common.core.domain.Result;
 import com.ruoyi.common.core.domain.entity.User;
 import com.ruoyi.common.enums.OrderType;
 import com.ruoyi.common.numgen.NumberGenerator;
-import com.ruoyi.common.utils.LimsDateUtil;
 import com.ruoyi.common.utils.QueryWrappers;
 import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.common.utils.api.IfsApiUtils;
@@ -43,6 +51,8 @@
 import com.ruoyi.inspect.service.InsSampleService;
 import com.ruoyi.inspect.vo.IfsOrderVO;
 import com.ruoyi.inspect.vo.InsOrderPrintingVo;
+import com.ruoyi.inspect.vo.OrderTemplateSummaryVo;
+import com.ruoyi.inspect.vo.TemplateConditionVo;
 import com.ruoyi.system.mapper.UserMapper;
 import lombok.AllArgsConstructor;
 import org.apache.commons.lang3.StringUtils;
@@ -90,6 +100,11 @@
     private SpotCheckQuarterItemMapper spotCheckQuarterItemMapper;
     private StandardProductListMapper standardProductListMapper;
     private StructureItemParameterMapper structureItemParameterMapper;
+    private StandardTreeService standardTreeService;
+    private StandardMethodService standardMethodService;
+    private StandardTemplateService standardTemplateService;
+    private InsOrderStandardTemplateMapper insOrderStandardTemplateMapper;
+    private InsOrderTemplateDeviceMapper insOrderTemplateDeviceMapper;
 
 
 
@@ -407,6 +422,142 @@
     }
 
     /**
+     * 鏂拌仛鏍囧噯搴撶洿缁戞ā鏉跨殑涓嬪崟娴佺▼锛氫竴涓鍗曘�佷竴涓牱鍝侊紝妫�楠岃褰曞畬鍏ㄧ敱妯℃澘鏍囪鐢熸垚銆�
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public int addNewJuInsOrder(InsPlaceOrderDto dto) {
+        if (dto == null || dto.getInsOrder() == null || dto.getStandardTreeId() == null) {
+            throw new ErrorException("涓嬪崟淇℃伅鎴栨爣鍑嗕笉鑳戒负绌�");
+        }
+        StandardTree standard = standardTreeService.getById(dto.getStandardTreeId());
+        if (standard == null || StringUtils.isBlank(standard.getSample())
+                || standard.getSampleType() != null || standard.getModel() != null) {
+            throw new ErrorException("璇烽�夋嫨鏍囧噯搴撲簩绾ф爣鍑�");
+        }
+        List<StandardTreeTemplateBindingVo> bindings = standardTreeService.selectTemplateBindings(dto.getStandardTreeId());
+        if (bindings.isEmpty()) {
+            throw new ErrorException("璇ユ爣鍑嗗皻鏈粦瀹氬彲鐢ㄧ殑鍘熷璁板綍妯℃澘");
+        }
+        Map<Integer, StandardTreeTemplateBindingVo> bindingMap = bindings.stream()
+                .collect(Collectors.toMap(StandardTreeTemplateBindingVo::getTemplateId,
+                        binding -> binding, (first, ignored) -> first));
+        List<OrderTemplateSelectionDto> selections = dto.getTemplateList();
+        if (selections == null) {
+            selections = bindings.stream().map(binding -> {
+                OrderTemplateSelectionDto selection = new OrderTemplateSelectionDto();
+                selection.setTemplateId(binding.getTemplateId());
+                selection.setSort(binding.getSort());
+                return selection;
+            }).collect(Collectors.toList());
+        }
+        if (selections.isEmpty()) {
+            throw new ErrorException("璇疯嚦灏戜繚鐣欎竴涓師濮嬭褰曟ā鏉�");
+        }
+        Set<Integer> selectedTemplateIds = new HashSet<>();
+        for (int index = 0; index < selections.size(); index++) {
+            OrderTemplateSelectionDto selection = selections.get(index);
+            if (selection == null || selection.getTemplateId() == null
+                    || !bindingMap.containsKey(selection.getTemplateId())) {
+                throw new ErrorException("鎵�閫夊師濮嬭褰曟ā鏉夸笉灞炰簬褰撳墠鏍囧噯");
+            }
+            if (!selectedTemplateIds.add(selection.getTemplateId())) {
+                throw new ErrorException("鍘熷璁板綍妯℃澘涓嶈兘閲嶅閫夋嫨");
+            }
+            if (selection.getSort() == null) {
+                selection.setSort(index);
+            }
+        }
+        selections.sort(Comparator.comparing(OrderTemplateSelectionDto::getSort)
+                .thenComparing(OrderTemplateSelectionDto::getTemplateId));
+        Map<Integer, Integer> templateSortMap = selections.stream()
+                .collect(Collectors.toMap(OrderTemplateSelectionDto::getTemplateId,
+                        OrderTemplateSelectionDto::getSort));
+        List<TemplateInspectionItemDto> templateItems = standardTreeService
+                .selectTemplateInspectionItems(dto.getStandardTreeId()).stream()
+                .filter(item -> selectedTemplateIds.contains(item.getTemplateId()))
+                .sorted(Comparator
+                        .comparing((TemplateInspectionItemDto item) -> templateSortMap.get(item.getTemplateId()))
+                        .thenComparing(item -> item.getTemplateRowIndex() == null
+                                ? Integer.MAX_VALUE : item.getTemplateRowIndex()))
+                .collect(Collectors.toList());
+
+        StandardMethod standardMethod = standardMethodService.getById(standard.getStandardMethodId());
+        if (standardMethod == null || StringUtils.isBlank(standardMethod.getCode())) {
+            throw new ErrorException("鏍囧噯缂栧彿涓嶅瓨鍦�");
+        }
+        String standardCode = standardMethod.getCode().trim();
+
+        InsOrder order = dto.getInsOrder();
+        order.setId(null);
+        order.setState(0);
+        order.setIsExemption(0);
+        order.setFactory(null);
+        order.setLaboratory(standard.getLaboratory());
+        order.setSampleType(null);
+        order.setSample(standardCode);
+        order.setPrepareUserEn(null);
+        order.setPrepareCode(null);
+        order.setRemarkEn(null);
+        // 鏂拌仛璁㈠崟鐨勭幆澧冩潯浠剁敱妫�楠屼换鍔″~鍐欙紝閬垮厤涓嬪崟妯℃澘鎴栨祻瑙堝櫒缂撳瓨甯﹀叆鏃у�笺��
+        order.setTemperature(null);
+        order.setHumidity(null);
+        insOrderMapper.insert(order);
+
+        InsSample sample = new InsSample();
+        sample.setInsOrderId(order.getId());
+        sample.setSampleCode(dto.getSampleCode());
+        sample.setFactory(null);
+        sample.setLaboratory(standard.getLaboratory());
+        sample.setSampleType(null);
+        sample.setSample(standardCode);
+        sample.setModel(null);
+        sample.setQuantity(1);
+        sample.setStandardMethodListId(null);
+        insSampleMapper.insert(sample);
+
+        int productSort = 0;
+        for (TemplateInspectionItemDto item : templateItems) {
+            InsProduct product = new InsProduct();
+            product.setInsSampleId(sample.getId());
+            product.setInspectionItem(item.getInspectionItem());
+            product.setInspectionItemClass("");
+            product.setInspectionItemSubclass("");
+            product.setFactory(null);
+            product.setLaboratory(standard.getLaboratory());
+            product.setSampleType(null);
+            product.setSample(standardCode);
+            product.setModel(null);
+            product.setTemplateId(item.getTemplateId());
+            product.setTemplateRowIndex(item.getTemplateRowIndex());
+            product.setSonLaboratory("鏂拌仛");
+            product.setState(1);
+            product.setSort(productSort++);
+            insProductMapper.insert(product);
+        }
+        saveTemplateSnapshots(order.getId(), selections);
+        return order.getId();
+    }
+
+    private void saveTemplateSnapshots(Integer orderId, List<OrderTemplateSelectionDto> selections) {
+        for (OrderTemplateSelectionDto selection : selections) {
+            StandardTemplate template = standardTemplateService.getById(selection.getTemplateId());
+            if (template == null) {
+                throw new ErrorException("鍘熷璁板綍妯℃澘涓嶅瓨鍦�");
+            }
+            InsOrderStandardTemplate snapshot = new InsOrderStandardTemplate();
+            snapshot.setInsOrderId(orderId);
+            snapshot.setTemplateId(template.getId());
+            snapshot.setName(template.getName());
+            snapshot.setNumber(template.getNumber());
+            snapshot.setRemark(template.getRemark());
+            snapshot.setThing(template.getThing());
+            snapshot.setSort(selection.getSort());
+            insOrderStandardTemplateMapper.insert(snapshot);
+        }
+    }
+
+    /**
      * 娣诲姞妫�楠岄」
      * @param sampleId
      * @param productList
@@ -447,9 +598,33 @@
             list = insSampleMapper.selectSampleProductListByOrderId2(id);
         }
         Map<String, Object> map1 = insSampleMapper.selectInsOrder(id);
+        List<InsSample> orderSamples = insSampleMapper.selectList(
+                Wrappers.<InsSample>lambdaQuery()
+                        .eq(InsSample::getInsOrderId, id)
+                        .select(InsSample::getSampleCode)
+                        .orderByAsc(InsSample::getId));
+        List<OrderTemplateSummaryVo> templateList = insOrderStandardTemplateMapper.selectList(
+                        Wrappers.<InsOrderStandardTemplate>lambdaQuery()
+                                .eq(InsOrderStandardTemplate::getInsOrderId, id)
+                                .orderByAsc(InsOrderStandardTemplate::getSort,
+                                        InsOrderStandardTemplate::getId))
+                .stream()
+                .map(template -> {
+                    OrderTemplateSummaryVo summary = new OrderTemplateSummaryVo();
+                    summary.setTemplateId(template.getTemplateId());
+                    summary.setName(template.getName());
+                    summary.setNumber(template.getNumber());
+                    summary.setRemark(template.getRemark());
+                    summary.setSort(template.getSort());
+                    return summary;
+                })
+                .collect(Collectors.toList());
         map.put("insOrder", insOrder);
         map.put("sampleProduct", list);
+        // 鏍峰搧缂栧彿鐩存帴浠庢牱鍝佽〃璇诲彇锛屼笉渚濊禆妫�楠岄」鐩叧鑱旀煡璇㈡槸鍚︽湁缁撴灉銆�
+        map.put("sampleCode", orderSamples.isEmpty() ? null : orderSamples.get(0).getSampleCode());
         map.put("insOrderTemplate", map1);
+        map.put("templateList", templateList);
         return map;
     }
 
@@ -463,24 +638,15 @@
         insOrder.setExamineTime(LocalDateTime.now());
         if (insOrder.getState() == 1) {
             //瀹℃牳閫氳繃鎵嶄細鐢熸垚濮旀墭缂栧彿
-            // todo: 妫�楠岀被鍨嬬紪鍙�
             InsOrder order = this.getById(insOrder.getId());
-            String code = "";
-            switch (order.getOrderType()) {
-                case InsOrderTypeConstants.SPOT_CHECK:
-                    code = "C";
-                    break;
-                case InsOrderTypeConstants.CUSTOMER_ORDERED:
-                    code = "W";
-                    break;
-                case InsOrderTypeConstants.ENTER_THE_FACTORY:
-                case InsOrderTypeConstants.QUARTERLY_TEST:
-                    code = "Y";
-                    break;
-            }
-            // 鐢熸垚缂栧彿
-            String no = numberGenerator.generateNumberWithPrefix(3,
-                    "JCZX/ZB-" + code + LimsDateUtil.resetDate(LocalDateTime.now()),
+            // 濮旀墭缂栧彿鎸夐�佹牱鏃ユ湡鐙珛娴佹按锛屼緥濡傦細LMSY-20260909-01銆�
+            // 鍘嗗彶/澶栭儴璁㈠崟鏈~鍐欓�佹牱鏃ユ湡鏃跺洖閫�鍒板綋澶╋紝閬垮厤褰卞搷鍘熸湁娴佺▼銆�
+            LocalDate entrustDate = order.getSampleDate() == null ? LocalDate.now() : order.getSampleDate();
+            String entrustCodePrefix = "LMSY-"
+                    + entrustDate.format(DateTimeFormatter.BASIC_ISO_DATE)
+                    + "-";
+            String no = numberGenerator.generateNumberWithPrefix(2,
+                    entrustCodePrefix,
                     InsOrder::getEntrustCode);
             // 鍒ゆ柇鏄惁鏄搴︽楠�, 鏄搴︽楠屽彇娑堝師鏉愭枡瀛e害妫�楠屼笅鍗�
             if (InsOrderTypeConstants.QUARTERLY_TEST.equals(order.getOrderType())) {
@@ -525,6 +691,39 @@
         Map<String, Object> map = new HashMap<>();
 //        InsOrder insOrder = insOrderMapper.selectById(id);
         IfsOrderVO ifsOrderVO = insOrderMapper.selectOrderInfoById(id);
+        Map<Integer, List<Integer>> templateDeviceIds = insOrderTemplateDeviceMapper.selectList(
+                        Wrappers.<InsOrderTemplateDevice>lambdaQuery()
+                                .eq(InsOrderTemplateDevice::getInsOrderId, id)
+                                .orderByAsc(InsOrderTemplateDevice::getSort,
+                                        InsOrderTemplateDevice::getId))
+                .stream().collect(Collectors.groupingBy(
+                        InsOrderTemplateDevice::getTemplateId,
+                        LinkedHashMap::new,
+                        Collectors.mapping(InsOrderTemplateDevice::getDeviceId, Collectors.toList())));
+        List<TemplateConditionVo> templateConditions = insOrderStandardTemplateMapper.selectList(
+                        Wrappers.<InsOrderStandardTemplate>lambdaQuery()
+                                .eq(InsOrderStandardTemplate::getInsOrderId, id)
+                                .select(InsOrderStandardTemplate::getTemplateId,
+                                        InsOrderStandardTemplate::getTemperature,
+                                        InsOrderStandardTemplate::getHumidity,
+                                        InsOrderStandardTemplate::getDetectionTime,
+                                        InsOrderStandardTemplate::getDetectionPlace,
+                                        InsOrderStandardTemplate::getInsResult)
+                                .orderByAsc(InsOrderStandardTemplate::getSort,
+                                        InsOrderStandardTemplate::getId))
+                .stream().map(snapshot -> {
+                    TemplateConditionVo condition = new TemplateConditionVo();
+                    condition.setTemplateId(snapshot.getTemplateId());
+                    condition.setTemperature(snapshot.getTemperature());
+                    condition.setHumidity(snapshot.getHumidity());
+                    condition.setDetectionTime(snapshot.getDetectionTime());
+                    condition.setDetectionPlace(snapshot.getDetectionPlace());
+                    condition.setInsResult(snapshot.getInsResult());
+                    condition.setDeviceIds(templateDeviceIds.getOrDefault(
+                            snapshot.getTemplateId(), Collections.emptyList()));
+                    return condition;
+                }).collect(Collectors.toList());
+        ifsOrderVO.setTemplateConditions(templateConditions);
         List<SampleProductDto> list = insSampleMapper.getInsOrderAndSample(id, laboratory);
         map.put("insOrder", ifsOrderVO);
         map.put("sampleProduct", list);
@@ -969,11 +1168,18 @@
         InsProduct insProduct = insProductMapper.selectOne(Wrappers.<InsProduct>lambdaQuery()
                 .eq(InsProduct::getInsSampleId, insSampleId)
                 .last("limit 1"));
-        String tree = insProduct.getFactory() + " - " +
-                insProduct.getLaboratory() + " - " +
-                insProduct.getSampleType() + " - " +
-                insProduct.getSample() + " - " +
-                insProduct.getModel();
+        String tree;
+        if (insProduct.getSampleType() == null && insProduct.getModel() == null
+                && StringUtils.isNotBlank(insProduct.getLaboratory())
+                && StringUtils.isNotBlank(insProduct.getSample())) {
+            tree = StandardTreePathParser.canonical(insProduct.getLaboratory(), insProduct.getSample());
+        } else {
+            tree = insProduct.getFactory() + " - " +
+                    insProduct.getLaboratory() + " - " +
+                    insProduct.getSampleType() + " - " +
+                    insProduct.getSample() + " - " +
+                    insProduct.getModel();
+        }
         // 鏌ヨ鏍囧噯鏍�
         List<StandardProductList> standardProductLists = standardProductListMapper.selectList(Wrappers.<StandardProductList>lambdaQuery()
                 .eq(StandardProductList::getStandardMethodListId, insProduct.getStandardMethodListId())
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsProductServiceImpl.java b/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsProductServiceImpl.java
index 397c0c5..b5d0e0f 100644
--- a/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsProductServiceImpl.java
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsProductServiceImpl.java
@@ -7,12 +7,18 @@
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.ruoyi.basic.mapper.StandardProductListMapper;
 import com.ruoyi.basic.pojo.StandardProductList;
+import com.ruoyi.basic.utils.StandardTreePathParser;
 import com.ruoyi.framework.exception.ErrorException;
 import com.ruoyi.inspect.dto.InsProductBindingDto;
+import com.ruoyi.inspect.dto.WriteInspectionConditionDto;
 import com.ruoyi.inspect.mapper.InsOrderMapper;
+import com.ruoyi.inspect.mapper.InsOrderStandardTemplateMapper;
+import com.ruoyi.inspect.mapper.InsOrderTemplateDeviceMapper;
 import com.ruoyi.inspect.mapper.InsProductMapper;
 import com.ruoyi.inspect.mapper.InsSampleMapper;
 import com.ruoyi.inspect.pojo.InsOrder;
+import com.ruoyi.inspect.pojo.InsOrderStandardTemplate;
+import com.ruoyi.inspect.pojo.InsOrderTemplateDevice;
 import com.ruoyi.inspect.pojo.InsProduct;
 import com.ruoyi.inspect.pojo.InsSample;
 import com.ruoyi.inspect.service.InsProductService;
@@ -39,6 +45,10 @@
 
     private StandardProductListMapper standardProductListMapper;
 
+    private InsOrderStandardTemplateMapper insOrderStandardTemplateMapper;
+
+    private InsOrderTemplateDeviceMapper insOrderTemplateDeviceMapper;
+
     @Override
     public int selectOrderManDay(Integer orderId) {
         return insProductMapper.selectOrderManDay(orderId);
@@ -54,7 +64,69 @@
     }
 
     @Override
-    public boolean write(InsOrder insOrder) {
+    public boolean write(WriteInspectionConditionDto dto) {
+        if (dto == null || dto.getId() == null) {
+            throw new ErrorException("璁㈠崟ID涓嶈兘涓虹┖");
+        }
+        if (dto.getTemplateId() != null) {
+            InsOrderStandardTemplate snapshot = insOrderStandardTemplateMapper.selectOne(
+                    Wrappers.<InsOrderStandardTemplate>lambdaQuery()
+                            .eq(InsOrderStandardTemplate::getInsOrderId, dto.getId())
+                            .eq(InsOrderStandardTemplate::getTemplateId, dto.getTemplateId())
+                            .last("limit 1"));
+            if (snapshot == null) {
+                throw new ErrorException("鏈壘鍒板綋鍓嶆ā鏉跨殑鍘熷璁板綍蹇収");
+            }
+            boolean hasSnapshotField = dto.isTemperaturePresent() || dto.isHumidityPresent()
+                    || dto.isDetectionTimePresent() || dto.isDetectionPlacePresent()
+                    || dto.isInsResultPresent();
+            if (hasSnapshotField) {
+                insOrderStandardTemplateMapper.update(null,
+                        Wrappers.<InsOrderStandardTemplate>lambdaUpdate()
+                                .eq(InsOrderStandardTemplate::getId, snapshot.getId())
+                                .set(dto.isTemperaturePresent(),
+                                        InsOrderStandardTemplate::getTemperature, dto.getTemperature())
+                                .set(dto.isHumidityPresent(),
+                                        InsOrderStandardTemplate::getHumidity, dto.getHumidity())
+                                .set(dto.isDetectionTimePresent(),
+                                        InsOrderStandardTemplate::getDetectionTime, dto.getDetectionTime())
+                                .set(dto.isDetectionPlacePresent(),
+                                        InsOrderStandardTemplate::getDetectionPlace, dto.getDetectionPlace())
+                                .set(dto.isInsResultPresent(),
+                                        InsOrderStandardTemplate::getInsResult, dto.getInsResult()));
+            }
+            if (dto.isDeviceIdsPresent()) {
+                insOrderTemplateDeviceMapper.delete(
+                        Wrappers.<InsOrderTemplateDevice>lambdaQuery()
+                                .eq(InsOrderTemplateDevice::getInsOrderId, dto.getId())
+                                .eq(InsOrderTemplateDevice::getTemplateId, dto.getTemplateId()));
+                List<Integer> deviceIds = dto.getDeviceIds() == null
+                        ? Collections.emptyList()
+                        : dto.getDeviceIds().stream()
+                                .filter(Objects::nonNull)
+                                .distinct()
+                                .collect(Collectors.toList());
+                if (!deviceIds.isEmpty()
+                        && insOrderTemplateDeviceMapper.selectExistingDeviceIds(deviceIds).size() != deviceIds.size()) {
+                    throw new ErrorException("鎵�閫夎澶囦笉瀛樺湪锛岃鍒锋柊璁惧鍒楄〃鍚庨噸璇�");
+                }
+                for (int index = 0; index < deviceIds.size(); index++) {
+                    InsOrderTemplateDevice relation = new InsOrderTemplateDevice();
+                    relation.setInsOrderId(dto.getId());
+                    relation.setTemplateId(dto.getTemplateId());
+                    relation.setDeviceId(deviceIds.get(index));
+                    relation.setSort(index);
+                    insOrderTemplateDeviceMapper.insert(relation);
+                }
+            }
+            if (!hasSnapshotField && !dto.isDeviceIdsPresent()) {
+                throw new ErrorException("娌℃湁闇�瑕佷繚瀛樼殑妯℃澘妫�楠屾潯浠�");
+            }
+            return true;
+        }
+        InsOrder insOrder = new InsOrder();
+        insOrder.setId(dto.getId());
+        insOrder.setRemark(dto.getRemark());
         insOrderMapper.updateById(insOrder);
         return true;
     }
@@ -118,11 +190,18 @@
     public List<InsProduct> getProductTreeByProductId(Integer productId) {
         // 鏌ヨ妫�楠岄」淇℃伅
         InsProduct insProduct = insProductMapper.selectById(productId);
-        String tree = insProduct.getFactory() + " - " +
-                insProduct.getLaboratory() + " - " +
-                insProduct.getSampleType() + " - " +
-                insProduct.getSample() + " - " +
-                insProduct.getModel();
+        String tree;
+        if (insProduct.getSampleType() == null && insProduct.getModel() == null
+                && StringUtils.isNotBlank(insProduct.getLaboratory())
+                && StringUtils.isNotBlank(insProduct.getSample())) {
+            tree = StandardTreePathParser.canonical(insProduct.getLaboratory(), insProduct.getSample());
+        } else {
+            tree = insProduct.getFactory() + " - " +
+                    insProduct.getLaboratory() + " - " +
+                    insProduct.getSampleType() + " - " +
+                    insProduct.getSample() + " - " +
+                    insProduct.getModel();
+        }
         // 鏌ヨ鏍囧噯鏍�
         List<StandardProductList> standardProductLists = standardProductListMapper.selectList(Wrappers.<StandardProductList>lambdaQuery()
                 .eq(StandardProductList::getStandardMethodListId, insProduct.getStandardMethodListId())
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsReportServiceImpl.java b/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsReportServiceImpl.java
index e27d5ff..614153a 100644
--- a/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsReportServiceImpl.java
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsReportServiceImpl.java
@@ -576,6 +576,47 @@
         return "/word/zip/output.zip";
     }
 
+    @Override
+    public void download(Integer id, Integer createOrderUser, HttpServletResponse response) {
+        if (id == null) {
+            throw new ErrorException("鎶ュ憡涓嶈兘涓虹┖");
+        }
+        InsReport report = insReportMapper.selectDownloadableReport(id, createOrderUser);
+        if (report == null) {
+            throw new ErrorException("鎶ュ憡涓嶅瓨鍦ㄣ�佸皻鏈敓鎴愭垨鏃犳潈涓嬭浇");
+        }
+        String reportUrl = StringUtils.isNotBlank(report.getUrlS()) ? report.getUrlS() : report.getUrl();
+        if (StringUtils.isBlank(reportUrl) || !reportUrl.replace('\\', '/').startsWith("/word/")) {
+            throw new ErrorException("鎶ュ憡鏂囦欢涓嶅瓨鍦紝璇疯仈绯荤鐞嗗憳");
+        }
+        Path root = Paths.get(wordUrl).toAbsolutePath().normalize();
+        Path file = root.resolve(reportUrl.replace('\\', '/').substring("/word/".length())).normalize();
+        if (!file.startsWith(root) || !Files.isRegularFile(file)) {
+            throw new ErrorException("鎶ュ憡鏂囦欢涓嶅瓨鍦紝璇疯仈绯荤鐞嗗憳");
+        }
+        try {
+            String filename = file.getFileName().toString();
+            String encodedName = URLEncoder.encode(filename, "UTF-8").replace("+", "%20");
+            response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
+            response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodedName);
+            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename");
+            response.setHeader("download-filename", encodedName);
+            response.setContentLengthLong(Files.size(file));
+            try (InputStream inputStream = Files.newInputStream(file);
+                 OutputStream outputStream = response.getOutputStream()) {
+                byte[] buffer = new byte[8192];
+                int length;
+                while ((length = inputStream.read(buffer)) != -1) {
+                    outputStream.write(buffer, 0, length);
+                }
+                outputStream.flush();
+            }
+        } catch (IOException e) {
+            log.error("涓嬭浇鎶ュ憡澶辫触锛宺eportId={}", id, e);
+            throw new ErrorException("鎶ュ憡涓嬭浇澶辫触");
+        }
+    }
+
     //鎵归噺涓婁紶
     @Override
     @Transactional(rollbackFor = Exception.class)
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/vo/IfsOrderVO.java b/inspect-server/src/main/java/com/ruoyi/inspect/vo/IfsOrderVO.java
index 38ae11a..df3b97b 100644
--- a/inspect-server/src/main/java/com/ruoyi/inspect/vo/IfsOrderVO.java
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/vo/IfsOrderVO.java
@@ -6,6 +6,7 @@
 
 import java.io.Serializable;
 import java.math.BigDecimal;
+import java.util.List;
 
 /**
  * ifs璁㈠崟vo
@@ -73,4 +74,7 @@
 
     private String lotBatchNo;
 
+    @ApiModelProperty("鍚勫師濮嬭褰曟ā鏉跨殑鐜鏉′欢")
+    private List<TemplateConditionVo> templateConditions;
+
 }
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/vo/OrderTemplateSummaryVo.java b/inspect-server/src/main/java/com/ruoyi/inspect/vo/OrderTemplateSummaryVo.java
new file mode 100644
index 0000000..83742fd
--- /dev/null
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/vo/OrderTemplateSummaryVo.java
@@ -0,0 +1,13 @@
+package com.ruoyi.inspect.vo;
+
+import lombok.Data;
+
+/** 璁㈠崟鍘熷璁板綍妯℃澘鐨勮交閲忓睍绀轰俊鎭�� */
+@Data
+public class OrderTemplateSummaryVo {
+    private Integer templateId;
+    private String name;
+    private String number;
+    private String remark;
+    private Integer sort;
+}
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/vo/TemplateConditionVo.java b/inspect-server/src/main/java/com/ruoyi/inspect/vo/TemplateConditionVo.java
new file mode 100644
index 0000000..5ced399
--- /dev/null
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/vo/TemplateConditionVo.java
@@ -0,0 +1,36 @@
+package com.ruoyi.inspect.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.List;
+
+/** 鍘熷璁板綍妯℃澘瀵瑰簲鐨勬楠岀幆澧冩潯浠躲�� */
+@Data
+public class TemplateConditionVo {
+
+    @ApiModelProperty("妯℃澘ID")
+    private Integer templateId;
+
+    @ApiModelProperty("娓╁害")
+    private String temperature;
+
+    @ApiModelProperty("婀垮害")
+    private String humidity;
+
+    @ApiModelProperty("妫�娴嬫棩鏈�")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private LocalDate detectionTime;
+
+    @ApiModelProperty("妫�娴嬪湴鐐�")
+    private String detectionPlace;
+
+    @ApiModelProperty("璁惧ID鍒楄〃")
+    private List<Integer> deviceIds = new ArrayList<>();
+
+    @ApiModelProperty("妯℃澘妫�楠岀粨璁猴細0涓嶅悎鏍硷紝1鍚堟牸")
+    private Integer insResult;
+}
diff --git a/inspect-server/src/main/resources/mapper/InsOrderMapper.xml b/inspect-server/src/main/resources/mapper/InsOrderMapper.xml
index 6380858..e5af839 100644
--- a/inspect-server/src/main/resources/mapper/InsOrderMapper.xml
+++ b/inspect-server/src/main/resources/mapper/InsOrderMapper.xml
@@ -181,6 +181,8 @@
         type,
         entrust_code,
         company,
+        sample,
+        sample_view,
         remark,
         create_time,
         state
diff --git a/inspect-server/src/main/resources/mapper/InsReportMapper.xml b/inspect-server/src/main/resources/mapper/InsReportMapper.xml
index e424fda..bd95b66 100644
--- a/inspect-server/src/main/resources/mapper/InsReportMapper.xml
+++ b/inspect-server/src/main/resources/mapper/InsReportMapper.xml
@@ -66,6 +66,19 @@
         limit 1
     </select>
 
+    <select id="selectDownloadableReport" resultType="com.ruoyi.inspect.pojo.InsReport">
+        select ir.*
+        from ins_report ir
+        where ir.id = #{id}
+          and ir.is_pass = 1
+        <if test="createOrderUser != null">
+          and (ir.write_user_id = #{createOrderUser}
+               or ir.examine_user_id = #{createOrderUser}
+               or ir.ratify_user_id = #{createOrderUser})
+        </if>
+        limit 1
+    </select>
+
     <!-- 鎶ュ憡鎶ヨ〃瀵煎嚭 -->
     <select id="reportAllExport" resultType="com.ruoyi.inspect.dto.InsReportExport">
         select code,
diff --git a/inspect-server/src/main/resources/mapper/InsSampleMapper.xml b/inspect-server/src/main/resources/mapper/InsSampleMapper.xml
index 59e0cfd..f433304 100644
--- a/inspect-server/src/main/resources/mapper/InsSampleMapper.xml
+++ b/inspect-server/src/main/resources/mapper/InsSampleMapper.xml
@@ -70,6 +70,7 @@
         <result property="createTime" column="ip_create_time" jdbcType="TIMESTAMP"/>
         <result property="updateTime" column="ip_update_time" jdbcType="TIMESTAMP"/>
         <result property="templateId" column="template_id" jdbcType="INTEGER"/>
+        <result property="templateRowIndex" column="template_row_index" jdbcType="INTEGER"/>
         <result property="methodS" column="method_s"/>
         <result property="tell" column="tell"/>
         <result property="dic" column="dic"/>
@@ -152,6 +153,7 @@
                ip.create_time ip_create_time,
                ip.update_time ip_update_time,
                template_id,
+               template_row_index,
                ipr.ins_value,
                ipr.com_value,
                ipr.equip_value,
@@ -529,7 +531,7 @@
                  left join ins_product ip on isa.id = ip.ins_sample_id
                  left join ins_product_result ipr on ip.id = ipr.ins_product_id
         where ins_order_id = #{id}
-          and ip.standard_method_list_id is not null
+          and (ip.standard_method_list_id is not null or ip.template_row_index is not null)
           and state = 1
           and ip.is_binding != 1
         order by ip.sort asc,
@@ -550,7 +552,7 @@
           and ip.son_laboratory = #{laboratory}
           and ins_fiber_id is null
           and ins_fibers_id is null
-          and standard_method_list_id is not null
+          and (standard_method_list_id is not null or template_row_index is not null)
         /* 鐢电紗閰嶇疆鏍囪瘑 */
         <if test="cableTag == null or cableTag == ''">
             and ip.cable_tag is null
@@ -577,7 +579,7 @@
         and ip.son_laboratory = #{laboratory}
         and ins_fiber_id is null
         and ins_fibers_id is null
-        and standard_method_list_id is not null
+        and (standard_method_list_id is not null or template_row_index is not null)
         and ip.raw_material_tag = #{rawMaterialTag}
         order by sort asc
     </select>

--
Gitblit v1.9.3