xzc 1 час назад
Родитель
Сommit
0ba52f21e7
29 измененных файлов с 1443 добавлено и 38 удалено
  1. 90 0
      build-prod/updata.sql
  2. 6 0
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/boilerconnectrecordreport/vo/BoilerConnectRecordReportPageReqVO.java
  3. 11 0
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/boilerconnectrecordreport/vo/BoilerConnectRecordReportRespVO.java
  4. 6 0
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/boilerconnectrecordreport/vo/BoilerConnectRecordReportSaveReqVO.java
  5. 6 0
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/boilertaskorder/vo/BoilerOrderItemCostInfoRepsVO.java
  6. 95 0
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/boilertaskorderitemfee/BoilerTaskOrderItemFeeController.java
  7. 42 0
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/boilertaskorderitemfee/vo/BoilerTaskOrderItemFeeRespVO.java
  8. 32 0
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/boilertaskorderitemfee/vo/BoilerTaskOrderItemFeeSaveReqVO.java
  9. 96 0
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/connectrecordreportcategory/ConnectRecordReportCategoryController.java
  10. 25 0
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/connectrecordreportcategory/vo/ConnectRecordReportCategoryPageReqVO.java
  11. 39 0
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/connectrecordreportcategory/vo/ConnectRecordReportCategoryRespVO.java
  12. 28 0
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/connectrecordreportcategory/vo/ConnectRecordReportCategorySaveReqVO.java
  13. 104 0
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/pipetaskorderitemfee/PipeTaskOrderItemFeeController.java
  14. 42 0
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/pipetaskorderitemfee/vo/PipeTaskOrderItemFeeRespVO.java
  15. 32 0
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/pipetaskorderitemfee/vo/PipeTaskOrderItemFeeSaveReqVO.java
  16. 11 0
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/dal/dataobject/boilerconnectrecordreport/BoilerConnectRecordReportDO.java
  17. 75 0
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/dal/dataobject/boilertaskorderitemfee/BoilerTaskOrderItemFeeDO.java
  18. 47 0
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/dal/dataobject/connectrecordreportcategory/ConnectRecordReportCategoryDO.java
  19. 75 0
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/dal/dataobject/pipetaskorderitemfee/PipeTaskOrderItemFeeDO.java
  20. 18 7
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/dal/mysql/boilerconnectrecordreport/BoilerConnectRecordReportMapper.java
  21. 35 0
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/dal/mysql/boilertaskorderitemfee/BoilerTaskOrderItemFeeMapper.java
  22. 24 0
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/dal/mysql/connectrecordreportcategory/ConnectRecordReportCategoryMapper.java
  23. 35 0
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/dal/mysql/pipetaskorderitemfee/PipeTaskOrderItemFeeMapper.java
  24. 3 0
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/service/boilerconnectrecordreport/BoilerConnectRecordReportServiceImpl.java
  25. 21 3
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/service/boilerlegalreport/BoilerLegalReportServiceImpl.java
  26. 156 28
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/service/boilertaskorder/BoilerTaskOrderServiceImpl.java
  27. 64 0
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/service/connectrecordreportcategory/ConnectRecordReportCategoryService.java
  28. 102 0
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/service/connectrecordreportcategory/ConnectRecordReportCategoryServiceImpl.java
  29. 123 0
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/service/pipetaskorder/PipeTaskOrderServiceImpl.java

+ 90 - 0
build-prod/updata.sql

@@ -245,3 +245,93 @@ COMMENT ON COLUMN "PRESSURE2_BOILER_TASK_ORDER_ITEM_REPORT_VERSION_ARCHIVING"."O
 COMMENT ON COLUMN "PRESSURE2_BOILER_TASK_ORDER_ITEM_REPORT_VERSION_ARCHIVING"."DYNAMIC_TB_CODE" IS '模板编号CODE';
 
 --已加
+
+
+ALTER TABLE "PRESSURE2_EQUIP_PIPE_SCHEDULING" ADD COLUMN "MANAGER_ID" VARCHAR(64);
+COMMENT ON COLUMN "PRESSURE2_EQUIP_PIPE_SCHEDULING"."MANAGER_ID" IS '负责人';
+
+ALTER TABLE "PRESSURE2_EQUIP_BOILER_SCHEDULING" ADD COLUMN "MANAGER_ID" VARCHAR(64);
+COMMENT ON COLUMN "PRESSURE2_EQUIP_BOILER_SCHEDULING"."MANAGER_ID" IS '负责人';
+
+ALTER TABLE "PRESSURE_APPOINTMENT_CONFIRM_ORDER" ADD COLUMN "MANAGER_ID" VARCHAR(64);
+COMMENT ON COLUMN "PRESSURE_APPOINTMENT_CONFIRM_ORDER"."MANAGER_ID" IS '负责人';
+
+ALTER TABLE "PRESSURE_ACCEPT_ORDER" ADD COLUMN "MANAGER_ID" VARCHAR(64);
+COMMENT ON COLUMN "PRESSURE_ACCEPT_ORDER"."MANAGER_ID" IS '负责人';
+
+-- 以上已加
+-- ============================================================
+-- 检验项目新增 itemType + projectCategory 字段 DDL
+-- 执行前请备份数据库
+-- ============================================================
+
+-- 1. 项目类别表(锅炉+管道通用)
+CREATE TABLE PRESSURE2_CONNECT_RECORD_REPORT_CATEGORY (
+                                                          ID VARCHAR(64) NOT NULL,
+                                                          NAME VARCHAR(100) NOT NULL,
+                                                          SORT INT DEFAULT 0,
+                                                          STATUS VARCHAR(1) DEFAULT '0',
+                                                          CREATOR VARCHAR(64),
+                                                          CREATE_TIME DATETIME,
+                                                          UPDATER VARCHAR(64),
+                                                          UPDATE_TIME DATETIME,
+                                                          DELETED BIT DEFAULT 0,
+                                                          PRIMARY KEY (ID)
+);
+COMMENT ON TABLE PRESSURE2_CONNECT_RECORD_REPORT_CATEGORY IS '检验项目类别(锅炉+管道通用)';
+COMMENT ON COLUMN PRESSURE2_CONNECT_RECORD_REPORT_CATEGORY.NAME IS '类别名称';
+COMMENT ON COLUMN PRESSURE2_CONNECT_RECORD_REPORT_CATEGORY.SORT IS '显示顺序';
+COMMENT ON COLUMN PRESSURE2_CONNECT_RECORD_REPORT_CATEGORY.STATUS IS '状态 0正常 1停用';
+
+-- 2. 检验项目配置表加字段
+ALTER TABLE PRESSURE2_BOILER_CONNECT_RECORD_REPORT
+    ADD COLUMN ITEM_TYPE VARCHAR(20) DEFAULT 'INSPECTION';
+ALTER TABLE PRESSURE2_BOILER_CONNECT_RECORD_REPORT
+    ADD COLUMN PROJECT_CATEGORY VARCHAR(64);
+
+-- 数据迁移:现有记录默认为 INSPECTION
+UPDATE PRESSURE2_BOILER_CONNECT_RECORD_REPORT SET ITEM_TYPE = 'INSPECTION' WHERE ITEM_TYPE IS NULL;
+
+-- 3. 锅炉任务单 FEE 实例表
+CREATE TABLE PRESSURE2_BOILER_TASK_ORDER_ITEM_FEE (
+                                                      ID VARCHAR(64) NOT NULL,
+                                                      ORDER_ID VARCHAR(64) NOT NULL,
+                                                      ORDER_ITEM_ID VARCHAR(64) NOT NULL,
+                                                      PROJECT_ID VARCHAR(64) NOT NULL,
+                                                      PROJECT_NAME VARCHAR(200),
+                                                      FEE DECIMAL(18,2),
+                                                      FEE_TYPE VARCHAR(2),
+                                                      FEE_CALCULATE_JSON TEXT,
+                                                      SORT INT DEFAULT 0,
+                                                      CREATOR VARCHAR(64),
+                                                      CREATE_TIME DATETIME,
+                                                      UPDATER VARCHAR(64),
+                                                      UPDATE_TIME DATETIME,
+                                                      DELETED BIT DEFAULT 0,
+                                                      PRIMARY KEY (ID)
+);
+CREATE INDEX IDX_BOILER_TASK_ORDER_ITEM_FEE_ORDER_ID ON PRESSURE2_BOILER_TASK_ORDER_ITEM_FEE(ORDER_ID);
+CREATE INDEX IDX_BOILER_TASK_ORDER_ITEM_FEE_ORDER_ITEM_ID ON PRESSURE2_BOILER_TASK_ORDER_ITEM_FEE(ORDER_ITEM_ID);
+
+-- 4. 管道任务单 FEE 实例表
+CREATE TABLE PRESSURE2_PIPE_TASK_ORDER_ITEM_FEE (
+                                                    ID VARCHAR(64) NOT NULL,
+                                                    ORDER_ID VARCHAR(64) NOT NULL,
+                                                    ORDER_ITEM_ID VARCHAR(64),
+                                                    PROJECT_ID VARCHAR(64) NOT NULL,
+                                                    PROJECT_NAME VARCHAR(200),
+                                                    FEE DECIMAL(18,2),
+                                                    FEE_TYPE VARCHAR(2),
+                                                    FEE_CALCULATE_JSON TEXT,
+                                                    SORT INT DEFAULT 0,
+                                                    CREATOR VARCHAR(64),
+                                                    CREATE_TIME DATETIME,
+                                                    UPDATER VARCHAR(64),
+                                                    UPDATE_TIME DATETIME,
+                                                    DELETED BIT DEFAULT 0,
+                                                    PRIMARY KEY (ID)
+);
+CREATE INDEX IDX_PIPE_TASK_ORDER_ITEM_FEE_ORDER_ID ON PRESSURE2_PIPE_TASK_ORDER_ITEM_FEE(ORDER_ID);
+CREATE INDEX IDX_PIPE_TASK_ORDER_ITEM_FEE_ORDER_ITEM_ID ON PRESSURE2_PIPE_TASK_ORDER_ITEM_FEE(ORDER_ITEM_ID);
+
+-- 已加

+ 6 - 0
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/boilerconnectrecordreport/vo/BoilerConnectRecordReportPageReqVO.java

@@ -88,4 +88,10 @@ public class BoilerConnectRecordReportPageReqVO extends PageParam {
 
     @Schema(description = "收费性质(1=法定收费, 2=服务收费,多个用逗号分隔)")
     private String chargeNature;
+
+    @Schema(description = "项目类型 INSPECTION 检验项目 / FEE 收费项目")
+    private String itemType;
+
+    @Schema(description = "项目类别ID")
+    private String projectCategory;
 }

+ 11 - 0
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/boilerconnectrecordreport/vo/BoilerConnectRecordReportRespVO.java

@@ -101,4 +101,15 @@ public class BoilerConnectRecordReportRespVO {
     @Schema(description = "收费性质(1=法定收费, 2=服务收费,多个用逗号分隔)")
     @ExcelProperty("收费性质")
     private String chargeNature;
+
+    @Schema(description = "项目类型 INSPECTION 检验项目 / FEE 收费项目")
+    @ExcelProperty("项目类型")
+    private String itemType;
+
+    @Schema(description = "项目类别ID")
+    private String projectCategory;
+
+    @Schema(description = "项目类别名称")
+    @ExcelProperty("项目类别名称")
+    private String projectCategoryName;
 }

+ 6 - 0
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/boilerconnectrecordreport/vo/BoilerConnectRecordReportSaveReqVO.java

@@ -76,4 +76,10 @@ public class BoilerConnectRecordReportSaveReqVO {
 
     @Schema(description = "收费性质(1=法定收费, 2=服务收费,多个用逗号分隔)")
     private String chargeNature;
+
+    @Schema(description = "项目类型 INSPECTION 检验项目 / FEE 收费项目")
+    private String itemType;
+
+    @Schema(description = "项目类别ID")
+    private String projectCategory;
 }

+ 6 - 0
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/boilertaskorder/vo/BoilerOrderItemCostInfoRepsVO.java

@@ -84,4 +84,10 @@ public class BoilerOrderItemCostInfoRepsVO {
      */
     @Schema(description = "序号")
     private Integer sort;
+
+    /**
+     * 项目类型:INSPECTION-检验项目, FEE-收费项目
+     * 检验项目走原有报告流程;收费项目不生成报告,仅在任务单下记录费用实例
+     */
+    private String itemType;
 }

+ 95 - 0
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/boilertaskorderitemfee/BoilerTaskOrderItemFeeController.java

@@ -0,0 +1,95 @@
+package cn.start.tz.module.pressure2.controller.admin.boilertaskorderitemfee;
+
+import cn.start.tz.framework.common.pojo.CommonResult;
+import cn.start.tz.framework.common.util.object.BeanUtils;
+import cn.start.tz.module.pressure2.controller.admin.boilertaskorderitemfee.vo.BoilerTaskOrderItemFeeRespVO;
+import cn.start.tz.module.pressure2.controller.admin.boilertaskorderitemfee.vo.BoilerTaskOrderItemFeeSaveReqVO;
+import cn.start.tz.module.pressure2.dal.dataobject.boilertaskorderitem.BoilerTaskOrderItemDO;
+import cn.start.tz.module.pressure2.dal.dataobject.boilertaskorderitemfee.BoilerTaskOrderItemFeeDO;
+import cn.start.tz.module.pressure2.dal.mysql.boilertaskorderitem.BoilerTaskOrderItemMapper;
+import cn.start.tz.module.pressure2.dal.mysql.boilertaskorderitemfee.BoilerTaskOrderItemFeeMapper;
+import cn.start.tz.module.pressure2.service.boilertaskorder.BoilerTaskOrderService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+import static cn.start.tz.framework.common.pojo.CommonResult.success;
+
+/**
+ * 锅炉任务单设备收费项目实例 Controller
+ *
+ * 仅支持增删改查费用信息,不涉及报告流转
+ *
+ * @author 特种管理员
+ */
+@Tag(name = "管理后台 - 锅炉任务单收费项目")
+@RestController
+@RequestMapping("/pressure2/boiler-task-order/order-item/fee")
+@Validated
+public class BoilerTaskOrderItemFeeController {
+
+    @Resource
+    private BoilerTaskOrderItemFeeMapper boilerTaskOrderItemFeeMapper;
+    @Resource
+    private BoilerTaskOrderItemMapper boilerTaskOrderItemMapper;
+    @Resource
+    private BoilerTaskOrderService boilerTaskOrderService;
+
+    @GetMapping("/list")
+    @Operation(summary = "按设备项查询收费项目列表")
+    @Parameter(name = "orderItemId", description = "设备项ID", required = true)
+    public CommonResult<List<BoilerTaskOrderItemFeeRespVO>> getListByOrderItemId(
+            @RequestParam("orderItemId") String orderItemId) {
+        List<BoilerTaskOrderItemFeeDO> list = boilerTaskOrderItemFeeMapper.selectListByOrderItemId(orderItemId);
+        return success(BeanUtils.toBean(list, BoilerTaskOrderItemFeeRespVO.class));
+    }
+
+    @PostMapping("/create")
+    @Operation(summary = "新增收费项目")
+    public CommonResult<String> createFee(@Valid @RequestBody BoilerTaskOrderItemFeeSaveReqVO createReqVO) {
+        BoilerTaskOrderItemFeeDO feeDO = BeanUtils.toBean(createReqVO, BoilerTaskOrderItemFeeDO.class);
+        // 通过设备项补全任务单ID
+        BoilerTaskOrderItemDO orderItem = boilerTaskOrderItemMapper.selectById(createReqVO.getOrderItemId());
+        if (orderItem != null) {
+            feeDO.setOrderId(orderItem.getOrderId());
+        }
+        boilerTaskOrderItemFeeMapper.insert(feeDO);
+        // 刷新任务单费用汇总(收费项目费用参与免征/减免计算)
+        if (feeDO.getOrderId() != null) {
+            boilerTaskOrderService.updateOrderFee(feeDO.getOrderId());
+        }
+        return success(feeDO.getId());
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "修改收费项目")
+    public CommonResult<Boolean> updateFee(@Valid @RequestBody BoilerTaskOrderItemFeeSaveReqVO updateReqVO) {
+        BoilerTaskOrderItemFeeDO updateObj = BeanUtils.toBean(updateReqVO, BoilerTaskOrderItemFeeDO.class);
+        boilerTaskOrderItemFeeMapper.updateById(updateObj);
+        // 修改费用后刷新任务单费用汇总
+        BoilerTaskOrderItemFeeDO feeDO = boilerTaskOrderItemFeeMapper.selectById(updateReqVO.getId());
+        if (feeDO != null && feeDO.getOrderId() != null) {
+            boilerTaskOrderService.updateOrderFee(feeDO.getOrderId());
+        }
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除收费项目")
+    @Parameter(name = "id", description = "编号", required = true)
+    public CommonResult<Boolean> deleteFee(@RequestParam("id") String id) {
+        // 删除前先记录任务单ID,删除后刷新费用汇总
+        BoilerTaskOrderItemFeeDO feeDO = boilerTaskOrderItemFeeMapper.selectById(id);
+        boilerTaskOrderItemFeeMapper.deleteById(id);
+        if (feeDO != null && feeDO.getOrderId() != null) {
+            boilerTaskOrderService.updateOrderFee(feeDO.getOrderId());
+        }
+        return success(true);
+    }
+}

+ 42 - 0
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/boilertaskorderitemfee/vo/BoilerTaskOrderItemFeeRespVO.java

@@ -0,0 +1,42 @@
+package cn.start.tz.module.pressure2.controller.admin.boilertaskorderitemfee.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 锅炉任务单收费项目 Response VO")
+@Data
+public class BoilerTaskOrderItemFeeRespVO {
+
+    @Schema(description = "主键", example = "1024")
+    private String id;
+
+    @Schema(description = "任务单ID", example = "1024")
+    private String orderId;
+
+    @Schema(description = "设备项ID", example = "1024")
+    private String orderItemId;
+
+    @Schema(description = "检验项目配置ID", example = "1024")
+    private String projectId;
+
+    @Schema(description = "项目名称", example = "委托检验费")
+    private String projectName;
+
+    @Schema(description = "费用", example = "100.00")
+    private BigDecimal fee;
+
+    @Schema(description = "费用类型:1 法定 2 服务", example = "1")
+    private String feeType;
+
+    @Schema(description = "费用计算配置JSON")
+    private String feeCalculateJson;
+
+    @Schema(description = "排序", example = "1")
+    private Integer sort;
+
+    @Schema(description = "创建时间")
+    private LocalDateTime createTime;
+}

+ 32 - 0
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/boilertaskorderitemfee/vo/BoilerTaskOrderItemFeeSaveReqVO.java

@@ -0,0 +1,32 @@
+package cn.start.tz.module.pressure2.controller.admin.boilertaskorderitemfee.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Schema(description = "管理后台 - 锅炉任务单收费项目保存 Request VO")
+@Data
+public class BoilerTaskOrderItemFeeSaveReqVO {
+
+    @Schema(description = "主键(更新时必填)", example = "1024")
+    private String id;
+
+    @Schema(description = "设备项ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    private String orderItemId;
+
+    @Schema(description = "检验项目配置ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    private String projectId;
+
+    @Schema(description = "费用", example = "100.00")
+    private BigDecimal fee;
+
+    @Schema(description = "费用类型:1 法定 2 服务", example = "1")
+    private String feeType;
+
+    @Schema(description = "费用计算配置JSON")
+    private String feeCalculateJson;
+
+    @Schema(description = "排序", example = "1")
+    private Integer sort;
+}

+ 96 - 0
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/connectrecordreportcategory/ConnectRecordReportCategoryController.java

@@ -0,0 +1,96 @@
+package cn.start.tz.module.pressure2.controller.admin.connectrecordreportcategory;
+
+import cn.start.tz.framework.common.pojo.CommonResult;
+import cn.start.tz.framework.common.pojo.PageResult;
+import cn.start.tz.framework.common.util.object.BeanUtils;
+import cn.start.tz.framework.excel.core.util.ExcelUtils;
+import cn.start.tz.framework.apilog.core.annotation.ApiAccessLog;
+import cn.start.tz.module.pressure2.controller.admin.connectrecordreportcategory.vo.ConnectRecordReportCategoryPageReqVO;
+import cn.start.tz.module.pressure2.controller.admin.connectrecordreportcategory.vo.ConnectRecordReportCategoryRespVO;
+import cn.start.tz.module.pressure2.controller.admin.connectrecordreportcategory.vo.ConnectRecordReportCategorySaveReqVO;
+import cn.start.tz.module.pressure2.dal.dataobject.connectrecordreportcategory.ConnectRecordReportCategoryDO;
+import cn.start.tz.module.pressure2.service.connectrecordreportcategory.ConnectRecordReportCategoryService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.Valid;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.io.IOException;
+import java.util.List;
+
+import static cn.start.tz.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
+import static cn.start.tz.framework.common.pojo.CommonResult.success;
+
+/**
+ * 检验项目类别 Controller
+ *
+ * @author 特种管理员
+ */
+@Tag(name = "管理后台 - 检验项目类别")
+@RestController
+@RequestMapping("/pressure2/connect-record-report-category")
+@Validated
+public class ConnectRecordReportCategoryController {
+
+    @Resource
+    private ConnectRecordReportCategoryService categoryService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建检验项目类别")
+    public CommonResult<String> createCategory(@Valid @RequestBody ConnectRecordReportCategorySaveReqVO createReqVO) {
+        return success(categoryService.createCategory(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新检验项目类别")
+    public CommonResult<Boolean> updateCategory(@Valid @RequestBody ConnectRecordReportCategorySaveReqVO updateReqVO) {
+        categoryService.updateCategory(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除检验项目类别")
+    @Parameter(name = "id", description = "编号", required = true)
+    public CommonResult<Boolean> deleteCategory(@RequestParam("id") String id) {
+        categoryService.deleteCategory(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得检验项目类别")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    public CommonResult<ConnectRecordReportCategoryRespVO> getCategory(@RequestParam("id") String id) {
+        ConnectRecordReportCategoryDO category = categoryService.getCategory(id);
+        return success(BeanUtils.toBean(category, ConnectRecordReportCategoryRespVO.class));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得检验项目类别分页")
+    public CommonResult<PageResult<ConnectRecordReportCategoryRespVO>> getCategoryPage(
+            @Valid ConnectRecordReportCategoryPageReqVO pageReqVO) {
+        PageResult<ConnectRecordReportCategoryDO> pageResult = categoryService.getCategoryPage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, ConnectRecordReportCategoryRespVO.class));
+    }
+
+    @GetMapping("/list-all-simple")
+    @Operation(summary = "获取全部启用的检验项目类别(用于下拉选择)")
+    public CommonResult<List<ConnectRecordReportCategoryRespVO>> getSimpleList() {
+        return success(categoryService.getSimpleList());
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "导出检验项目类别 Excel")
+    @ApiAccessLog(operateType = EXPORT)
+    public void exportCategoryExcel(@Valid ConnectRecordReportCategoryPageReqVO pageReqVO,
+                                   HttpServletResponse response) throws IOException {
+        pageReqVO.setPageSize(-1);
+        List<ConnectRecordReportCategoryDO> list = categoryService.getCategoryPage(pageReqVO).getList();
+        ExcelUtils.write(response, "检验项目类别.xls", "数据",
+                ConnectRecordReportCategoryRespVO.class,
+                BeanUtils.toBean(list, ConnectRecordReportCategoryRespVO.class));
+    }
+}

+ 25 - 0
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/connectrecordreportcategory/vo/ConnectRecordReportCategoryPageReqVO.java

@@ -0,0 +1,25 @@
+package cn.start.tz.module.pressure2.controller.admin.connectrecordreportcategory.vo;
+
+import cn.start.tz.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+/**
+ * 检验项目类别 分页 Request VO
+ *
+ * @author 特种管理员
+ */
+@Schema(description = "管理后台 - 检验项目类别分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class ConnectRecordReportCategoryPageReqVO extends PageParam {
+
+    @Schema(description = "类别名称")
+    private String name;
+
+    @Schema(description = "状态 0 正常 1 停用")
+    private String status;
+}

+ 39 - 0
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/connectrecordreportcategory/vo/ConnectRecordReportCategoryRespVO.java

@@ -0,0 +1,39 @@
+package cn.start.tz.module.pressure2.controller.admin.connectrecordreportcategory.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * 检验项目类别 Response VO
+ *
+ * @author 特种管理员
+ */
+@Schema(description = "管理后台 - 检验项目类别 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class ConnectRecordReportCategoryRespVO {
+
+    @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    @ExcelProperty("主键")
+    private String id;
+
+    @Schema(description = "类别名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "定期检验")
+    @ExcelProperty("类别名称")
+    private String name;
+
+    @Schema(description = "显示顺序", example = "1")
+    @ExcelProperty("显示顺序")
+    private Integer sort;
+
+    @Schema(description = "状态 0 正常 1 停用", example = "0")
+    @ExcelProperty("状态")
+    private String status;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    @ExcelProperty("创建时间")
+    private LocalDateTime createTime;
+}

+ 28 - 0
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/connectrecordreportcategory/vo/ConnectRecordReportCategorySaveReqVO.java

@@ -0,0 +1,28 @@
+package cn.start.tz.module.pressure2.controller.admin.connectrecordreportcategory.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+/**
+ * 检验项目类别 新增/修改 Request VO
+ *
+ * @author 特种管理员
+ */
+@Schema(description = "管理后台 - 检验项目类别新增/修改 Request VO")
+@Data
+public class ConnectRecordReportCategorySaveReqVO {
+
+    @Schema(description = "主键", example = "1024")
+    private String id;
+
+    @Schema(description = "类别名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "定期检验")
+    @NotBlank(message = "类别名称不能为空")
+    private String name;
+
+    @Schema(description = "显示顺序", example = "1")
+    private Integer sort;
+
+    @Schema(description = "状态 0 正常 1 停用", example = "0")
+    private String status;
+}

+ 104 - 0
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/pipetaskorderitemfee/PipeTaskOrderItemFeeController.java

@@ -0,0 +1,104 @@
+package cn.start.tz.module.pressure2.controller.admin.pipetaskorderitemfee;
+
+import cn.start.tz.framework.common.pojo.CommonResult;
+import cn.start.tz.framework.common.util.object.BeanUtils;
+import cn.start.tz.module.pressure2.controller.admin.pipetaskorderitemfee.vo.PipeTaskOrderItemFeeRespVO;
+import cn.start.tz.module.pressure2.controller.admin.pipetaskorderitemfee.vo.PipeTaskOrderItemFeeSaveReqVO;
+import cn.start.tz.module.pressure2.dal.dataobject.pipetaskorderitem.PipeTaskOrderItemDO;
+import cn.start.tz.module.pressure2.dal.dataobject.pipetaskorderitemfee.PipeTaskOrderItemFeeDO;
+import cn.start.tz.module.pressure2.dal.mysql.pipetaskorderitem.PipeTaskOrderItemMapper;
+import cn.start.tz.module.pressure2.dal.mysql.pipetaskorderitemfee.PipeTaskOrderItemFeeMapper;
+import cn.start.tz.module.pressure2.service.pipetaskorder.PipeTaskOrderService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+import static cn.start.tz.framework.common.pojo.CommonResult.success;
+
+/**
+ * 管道任务单设备收费项目实例 Controller
+ *
+ * 仅支持增删改查费用信息,不涉及报告流转
+ *
+ * @author 特种管理员
+ */
+@Tag(name = "管理后台 - 管道任务单收费项目")
+@RestController
+@RequestMapping("/pressure2/pipe-task-order/order-item/fee")
+@Validated
+public class PipeTaskOrderItemFeeController {
+
+    @Resource
+    private PipeTaskOrderItemFeeMapper pipeTaskOrderItemFeeMapper;
+    @Resource
+    private PipeTaskOrderItemMapper pipeTaskOrderItemMapper;
+    @Resource
+    private PipeTaskOrderService pipeTaskOrderService;
+
+    @GetMapping("/list")
+    @Operation(summary = "按设备项查询收费项目列表")
+    @Parameter(name = "orderItemId", description = "设备项ID", required = true)
+    public CommonResult<List<PipeTaskOrderItemFeeRespVO>> getListByOrderItemId(
+            @RequestParam("orderItemId") String orderItemId) {
+        List<PipeTaskOrderItemFeeDO> list = pipeTaskOrderItemFeeMapper.selectListByOrderItemId(orderItemId);
+        return success(BeanUtils.toBean(list, PipeTaskOrderItemFeeRespVO.class));
+    }
+
+    @GetMapping("/list-by-order")
+    @Operation(summary = "按任务单查询收费项目列表")
+    @Parameter(name = "orderId", description = "任务单ID", required = true)
+    public CommonResult<List<PipeTaskOrderItemFeeRespVO>> getListByOrderId(
+            @RequestParam("orderId") String orderId) {
+        List<PipeTaskOrderItemFeeDO> list = pipeTaskOrderItemFeeMapper.selectListByOrderId(orderId);
+        return success(BeanUtils.toBean(list, PipeTaskOrderItemFeeRespVO.class));
+    }
+
+    @PostMapping("/create")
+    @Operation(summary = "新增收费项目")
+    public CommonResult<String> createFee(@Valid @RequestBody PipeTaskOrderItemFeeSaveReqVO createReqVO) {
+        PipeTaskOrderItemFeeDO feeDO = BeanUtils.toBean(createReqVO, PipeTaskOrderItemFeeDO.class);
+        // 通过设备项补全任务单ID
+        PipeTaskOrderItemDO orderItem = pipeTaskOrderItemMapper.selectById(createReqVO.getOrderItemId());
+        if (orderItem != null) {
+            feeDO.setOrderId(orderItem.getOrderId());
+        }
+        pipeTaskOrderItemFeeMapper.insert(feeDO);
+        // 刷新任务单费用汇总(收费项目费用参与免征/减免计算)
+        if (feeDO.getOrderId() != null) {
+            pipeTaskOrderService.updateOrderFee(feeDO.getOrderId());
+        }
+        return success(feeDO.getId());
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "修改收费项目")
+    public CommonResult<Boolean> updateFee(@Valid @RequestBody PipeTaskOrderItemFeeSaveReqVO updateReqVO) {
+        PipeTaskOrderItemFeeDO updateObj = BeanUtils.toBean(updateReqVO, PipeTaskOrderItemFeeDO.class);
+        pipeTaskOrderItemFeeMapper.updateById(updateObj);
+        // 修改费用后刷新任务单费用汇总
+        PipeTaskOrderItemFeeDO feeDO = pipeTaskOrderItemFeeMapper.selectById(updateReqVO.getId());
+        if (feeDO != null && feeDO.getOrderId() != null) {
+            pipeTaskOrderService.updateOrderFee(feeDO.getOrderId());
+        }
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除收费项目")
+    @Parameter(name = "id", description = "编号", required = true)
+    public CommonResult<Boolean> deleteFee(@RequestParam("id") String id) {
+        // 删除前先记录任务单ID,删除后刷新费用汇总
+        PipeTaskOrderItemFeeDO feeDO = pipeTaskOrderItemFeeMapper.selectById(id);
+        pipeTaskOrderItemFeeMapper.deleteById(id);
+        if (feeDO != null && feeDO.getOrderId() != null) {
+            pipeTaskOrderService.updateOrderFee(feeDO.getOrderId());
+        }
+        return success(true);
+    }
+}

+ 42 - 0
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/pipetaskorderitemfee/vo/PipeTaskOrderItemFeeRespVO.java

@@ -0,0 +1,42 @@
+package cn.start.tz.module.pressure2.controller.admin.pipetaskorderitemfee.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 管道任务单收费项目 Response VO")
+@Data
+public class PipeTaskOrderItemFeeRespVO {
+
+    @Schema(description = "主键", example = "1024")
+    private String id;
+
+    @Schema(description = "任务单ID", example = "1024")
+    private String orderId;
+
+    @Schema(description = "设备项ID", example = "1024")
+    private String orderItemId;
+
+    @Schema(description = "检验项目配置ID", example = "1024")
+    private String projectId;
+
+    @Schema(description = "项目名称", example = "委托检验费")
+    private String projectName;
+
+    @Schema(description = "费用", example = "100.00")
+    private BigDecimal fee;
+
+    @Schema(description = "费用类型:1 法定 2 服务", example = "1")
+    private String feeType;
+
+    @Schema(description = "费用计算配置JSON")
+    private String feeCalculateJson;
+
+    @Schema(description = "排序", example = "1")
+    private Integer sort;
+
+    @Schema(description = "创建时间")
+    private LocalDateTime createTime;
+}

+ 32 - 0
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/pipetaskorderitemfee/vo/PipeTaskOrderItemFeeSaveReqVO.java

@@ -0,0 +1,32 @@
+package cn.start.tz.module.pressure2.controller.admin.pipetaskorderitemfee.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Schema(description = "管理后台 - 管道任务单收费项目保存 Request VO")
+@Data
+public class PipeTaskOrderItemFeeSaveReqVO {
+
+    @Schema(description = "主键(更新时必填)", example = "1024")
+    private String id;
+
+    @Schema(description = "设备项ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    private String orderItemId;
+
+    @Schema(description = "检验项目配置ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    private String projectId;
+
+    @Schema(description = "费用", example = "100.00")
+    private BigDecimal fee;
+
+    @Schema(description = "费用类型:1 法定 2 服务", example = "1")
+    private String feeType;
+
+    @Schema(description = "费用计算配置JSON")
+    private String feeCalculateJson;
+
+    @Schema(description = "排序", example = "1")
+    private Integer sort;
+}

+ 11 - 0
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/dal/dataobject/boilerconnectrecordreport/BoilerConnectRecordReportDO.java

@@ -100,6 +100,17 @@ public class BoilerConnectRecordReportDO extends BaseDO {
      */
     private String chargeNature;
 
+    /**
+     * 项目类型:INSPECTION-检验项目, FEE-收费项目
+     * 检验项目走原有报告流程;收费项目不生成报告,仅在任务单下记录费用实例
+     */
+    private String itemType;
+
+    /**
+     * 项目类别ID(关联 pressure2_connect_record_report_category.id)
+     */
+    private String projectCategory;
+
     public String isMain(){
         return String.valueOf("MAIN".equals(this.projectType));
     }

+ 75 - 0
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/dal/dataobject/boilertaskorderitemfee/BoilerTaskOrderItemFeeDO.java

@@ -0,0 +1,75 @@
+package cn.start.tz.module.pressure2.dal.dataobject.boilertaskorderitemfee;
+
+import cn.start.tz.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.math.BigDecimal;
+
+/**
+ * 锅炉任务单设备收费项目实例 DO
+ *
+ * 由配置表中 itemType=FEE 的检验项目在任务单生成时产生,仅用于费用汇总与展示,
+ * 不走报告流转流程(无 taskStatus/reportNo/reportUrl 等字段)
+ *
+ * @author 特种管理员
+ */
+@TableName("pressure2_boiler_task_order_item_fee")
+@KeySequence("pressure2_boiler_task_order_item_fee_seq")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class BoilerTaskOrderItemFeeDO extends BaseDO {
+
+    /**
+     * 主键
+     */
+    @TableId(value = "id", type = IdType.ASSIGN_UUID)
+    private String id;
+
+    /**
+     * 任务单ID
+     */
+    private String orderId;
+
+    /**
+     * 设备项ID
+     */
+    private String orderItemId;
+
+    /**
+     * 检验项目配置ID(关联 pressure2_boiler_connect_record_report.id)
+     */
+    private String projectId;
+
+    /**
+     * 项目名称(冗余,便于展示)
+     */
+    private String projectName;
+
+    /**
+     * 费用
+     */
+    private BigDecimal fee;
+
+    /**
+     * 费用类型:1 法定 2 服务
+     */
+    private String feeType;
+
+    /**
+     * 费用计算配置JSON
+     */
+    private String feeCalculateJson;
+
+    /**
+     * 排序
+     */
+    private Integer sort;
+}

+ 47 - 0
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/dal/dataobject/connectrecordreportcategory/ConnectRecordReportCategoryDO.java

@@ -0,0 +1,47 @@
+package cn.start.tz.module.pressure2.dal.dataobject.connectrecordreportcategory;
+
+import cn.start.tz.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+/**
+ * 检验项目类别 DO
+ *
+ * 锅炉+管道通用,仅维护 id + name + sort + status 最小字段集
+ *
+ * @author 特种管理员
+ */
+@TableName("pressure2_connect_record_report_category")
+@KeySequence("pressure2_connect_record_report_category_seq")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class ConnectRecordReportCategoryDO extends BaseDO {
+
+    /**
+     * 主键
+     */
+    @TableId(value = "id", type = IdType.ASSIGN_UUID)
+    private String id;
+
+    /**
+     * 类别名称
+     */
+    private String name;
+
+    /**
+     * 显示顺序
+     */
+    private Integer sort;
+
+    /**
+     * 状态 0 正常 1 停用
+     */
+    private String status;
+}

+ 75 - 0
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/dal/dataobject/pipetaskorderitemfee/PipeTaskOrderItemFeeDO.java

@@ -0,0 +1,75 @@
+package cn.start.tz.module.pressure2.dal.dataobject.pipetaskorderitemfee;
+
+import cn.start.tz.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.math.BigDecimal;
+
+/**
+ * 管道任务单设备收费项目实例 DO
+ *
+ * 由配置表中 itemType=FEE 的检验项目在任务单生成时产生,仅用于费用汇总与展示,
+ * 不走报告流转流程(无 taskStatus/reportNo/reportUrl 等字段)
+ *
+ * @author 特种管理员
+ */
+@TableName("pressure2_pipe_task_order_item_fee")
+@KeySequence("pressure2_pipe_task_order_item_fee_seq")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class PipeTaskOrderItemFeeDO extends BaseDO {
+
+    /**
+     * 主键
+     */
+    @TableId(value = "id", type = IdType.ASSIGN_UUID)
+    private String id;
+
+    /**
+     * 任务单ID
+     */
+    private String orderId;
+
+    /**
+     * 设备项ID
+     */
+    private String orderItemId;
+
+    /**
+     * 检验项目配置ID(关联 pressure2_boiler_connect_record_report.id)
+     */
+    private String projectId;
+
+    /**
+     * 项目名称(冗余,便于展示)
+     */
+    private String projectName;
+
+    /**
+     * 费用
+     */
+    private BigDecimal fee;
+
+    /**
+     * 费用类型:1 法定 2 服务
+     */
+    private String feeType;
+
+    /**
+     * 费用计算配置JSON
+     */
+    private String feeCalculateJson;
+
+    /**
+     * 排序
+     */
+    private Integer sort;
+}

+ 18 - 7
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/dal/mysql/boilerconnectrecordreport/BoilerConnectRecordReportMapper.java

@@ -9,6 +9,7 @@ import cn.start.tz.framework.mybatis.core.mapper.BaseMapperX;
 import cn.start.tz.framework.mybatis.core.query.MPJLambdaWrapperX;
 import cn.start.tz.module.pressure2.controller.admin.pipetaskorder.vo.PipeOrderItemPageRespVO;
 import cn.start.tz.module.pressure2.dal.dataobject.boilerconnectrecordreport.BoilerConnectRecordReportDO;
+import cn.start.tz.module.pressure2.dal.dataobject.connectrecordreportcategory.ConnectRecordReportCategoryDO;
 import cn.start.tz.module.pressure2.dal.dataobject.dynamictb.DynamicTbDO;
 import cn.start.tz.module.pressure2.dal.dataobject.pipetaskorderitem.PipeTaskOrderItemDO;
 import org.apache.ibatis.annotations.Mapper;
@@ -29,6 +30,8 @@ public interface BoilerConnectRecordReportMapper extends BaseMapperX<BoilerConne
                 .likeIfPresent(BoilerConnectRecordReportDO::getProjectCode, reqVO.getProjectCode())
                 .eqIfPresent(BoilerConnectRecordReportDO::getStatus, reqVO.getStatus())
                 .eqIfPresent(BoilerConnectRecordReportDO::getProjectType, reqVO.getProjectType())
+                .eqIfPresent(BoilerConnectRecordReportDO::getItemType, reqVO.getItemType())
+                .eqIfPresent(BoilerConnectRecordReportDO::getProjectCategory, reqVO.getProjectCategory())
                 .orderByDesc(BoilerConnectRecordReportDO::getCreateTime));
     }
 
@@ -46,6 +49,8 @@ public interface BoilerConnectRecordReportMapper extends BaseMapperX<BoilerConne
                 .selectAs("DynamicTb3",DynamicTbDO::getTbName, BoilerConnectRecordReportRespVO::getResultTemplateName)
                 .selectAs("DynamicTb3",DynamicTbDO::getTbCode, BoilerConnectRecordReportRespVO::getResultTemplateCode)
                 .leftJoin(DynamicTbDO.class,"DynamicTb3", DynamicTbDO::getId, BoilerConnectRecordReportDO::getResultTemplateId)
+                .selectAs("Category", ConnectRecordReportCategoryDO::getName, BoilerConnectRecordReportRespVO::getProjectCategoryName)
+                .leftJoin(ConnectRecordReportCategoryDO.class, "Category", ConnectRecordReportCategoryDO::getId, BoilerConnectRecordReportDO::getProjectCategory)
                 .orderByDesc(BoilerConnectRecordReportDO::getCreateTime);
 
         if (StrUtil.isNotEmpty(reqVO.getProjectName())) {
@@ -64,20 +69,26 @@ public interface BoilerConnectRecordReportMapper extends BaseMapperX<BoilerConne
             recordDOMPJLambdaWrapper.eq(BoilerConnectRecordReportDO::getProjectType, reqVO.getProjectType());
         }
 
-        if (StrUtil.isNotEmpty(reqVO.getRecordTemplateCode())) {
-            recordDOMPJLambdaWrapper.like("DynamicTb1",DynamicTbDO::getTbCode, reqVO.getRecordTemplateCode());
+        if (StrUtil.isNotEmpty(reqVO.getItemType())) {
+            recordDOMPJLambdaWrapper.eq(BoilerConnectRecordReportDO::getItemType, reqVO.getItemType());
         }
 
-        if (StrUtil.isNotEmpty(reqVO.getRecordTemplateName())) {
-            recordDOMPJLambdaWrapper.like("DynamicTb1",DynamicTbDO::getTbName, reqVO.getRecordTemplateName());
+        if (StrUtil.isNotEmpty(reqVO.getProjectCategory())) {
+            recordDOMPJLambdaWrapper.eq(BoilerConnectRecordReportDO::getProjectCategory, reqVO.getProjectCategory());
         }
 
-        if (StrUtil.isNotEmpty(reqVO.getReportTemplateCode())) {
-            recordDOMPJLambdaWrapper.like("DynamicTb2",DynamicTbDO::getTbCode, reqVO.getReportTemplateCode());
+        if (StrUtil.isNotEmpty(reqVO.getRecordTemplateName())) {
+            recordDOMPJLambdaWrapper.and(wrapper -> wrapper
+                    .like("DynamicTb1", DynamicTbDO::getTbCode, reqVO.getRecordTemplateName())
+                    .or()
+                    .like("DynamicTb1", DynamicTbDO::getTbName, reqVO.getRecordTemplateName()));
         }
 
         if (StrUtil.isNotEmpty(reqVO.getReportTemplateName())) {
-            recordDOMPJLambdaWrapper.like("DynamicTb2",DynamicTbDO::getTbName, reqVO.getReportTemplateName());
+            recordDOMPJLambdaWrapper.and(wrapper -> wrapper
+                    .like("DynamicTb2", DynamicTbDO::getTbCode, reqVO.getReportTemplateName())
+                    .or()
+                    .like("DynamicTb2", DynamicTbDO::getTbName, reqVO.getReportTemplateName()));
         }
 
         if (reqVO.getCreateTime() != null && reqVO.getCreateTime().length == 2) {

+ 35 - 0
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/dal/mysql/boilertaskorderitemfee/BoilerTaskOrderItemFeeMapper.java

@@ -0,0 +1,35 @@
+package cn.start.tz.module.pressure2.dal.mysql.boilertaskorderitemfee;
+
+import cn.start.tz.framework.mybatis.core.mapper.BaseMapperX;
+import cn.start.tz.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.start.tz.module.pressure2.dal.dataobject.boilertaskorderitemfee.BoilerTaskOrderItemFeeDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 锅炉任务单设备收费项目实例 Mapper
+ *
+ * @author 特种管理员
+ */
+@Mapper
+public interface BoilerTaskOrderItemFeeMapper extends BaseMapperX<BoilerTaskOrderItemFeeDO> {
+
+    /**
+     * 按任务单ID查询所有 FEE 实例
+     */
+    default List<BoilerTaskOrderItemFeeDO> selectListByOrderId(String orderId) {
+        return selectList(new LambdaQueryWrapperX<BoilerTaskOrderItemFeeDO>()
+                .eq(BoilerTaskOrderItemFeeDO::getOrderId, orderId)
+                .orderByAsc(BoilerTaskOrderItemFeeDO::getSort));
+    }
+
+    /**
+     * 按设备项ID查询所有 FEE 实例
+     */
+    default List<BoilerTaskOrderItemFeeDO> selectListByOrderItemId(String orderItemId) {
+        return selectList(new LambdaQueryWrapperX<BoilerTaskOrderItemFeeDO>()
+                .eq(BoilerTaskOrderItemFeeDO::getOrderItemId, orderItemId)
+                .orderByAsc(BoilerTaskOrderItemFeeDO::getSort));
+    }
+}

+ 24 - 0
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/dal/mysql/connectrecordreportcategory/ConnectRecordReportCategoryMapper.java

@@ -0,0 +1,24 @@
+package cn.start.tz.module.pressure2.dal.mysql.connectrecordreportcategory;
+
+import cn.start.tz.framework.mybatis.core.mapper.BaseMapperX;
+import cn.start.tz.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.start.tz.module.pressure2.controller.admin.connectrecordreportcategory.vo.ConnectRecordReportCategoryPageReqVO;
+import cn.start.tz.module.pressure2.dal.dataobject.connectrecordreportcategory.ConnectRecordReportCategoryDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 检验项目类别 Mapper
+ *
+ * @author 特种管理员
+ */
+@Mapper
+public interface ConnectRecordReportCategoryMapper extends BaseMapperX<ConnectRecordReportCategoryDO> {
+
+    default List<ConnectRecordReportCategoryDO> selectListAll() {
+        return selectList(new LambdaQueryWrapperX<ConnectRecordReportCategoryDO>()
+                .eq(ConnectRecordReportCategoryDO::getStatus, "0")
+                .orderByAsc(ConnectRecordReportCategoryDO::getSort));
+    }
+}

+ 35 - 0
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/dal/mysql/pipetaskorderitemfee/PipeTaskOrderItemFeeMapper.java

@@ -0,0 +1,35 @@
+package cn.start.tz.module.pressure2.dal.mysql.pipetaskorderitemfee;
+
+import cn.start.tz.framework.mybatis.core.mapper.BaseMapperX;
+import cn.start.tz.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.start.tz.module.pressure2.dal.dataobject.pipetaskorderitemfee.PipeTaskOrderItemFeeDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 管道任务单设备收费项目实例 Mapper
+ *
+ * @author 特种管理员
+ */
+@Mapper
+public interface PipeTaskOrderItemFeeMapper extends BaseMapperX<PipeTaskOrderItemFeeDO> {
+
+    /**
+     * 按任务单ID查询所有 FEE 实例
+     */
+    default List<PipeTaskOrderItemFeeDO> selectListByOrderId(String orderId) {
+        return selectList(new LambdaQueryWrapperX<PipeTaskOrderItemFeeDO>()
+                .eq(PipeTaskOrderItemFeeDO::getOrderId, orderId)
+                .orderByAsc(PipeTaskOrderItemFeeDO::getSort));
+    }
+
+    /**
+     * 按设备项ID查询所有 FEE 实例
+     */
+    default List<PipeTaskOrderItemFeeDO> selectListByOrderItemId(String orderItemId) {
+        return selectList(new LambdaQueryWrapperX<PipeTaskOrderItemFeeDO>()
+                .eq(PipeTaskOrderItemFeeDO::getOrderItemId, orderItemId)
+                .orderByAsc(PipeTaskOrderItemFeeDO::getSort));
+    }
+}

+ 3 - 0
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/service/boilerconnectrecordreport/BoilerConnectRecordReportServiceImpl.java

@@ -92,6 +92,9 @@ public class BoilerConnectRecordReportServiceImpl extends ServiceImpl<BoilerConn
                 .set(BoilerConnectRecordReportDO::getIsSign, updateObj.getIsSign())
                 .set(BoilerConnectRecordReportDO::getIsFee, updateObj.getIsFee())
                 .set(BoilerConnectRecordReportDO::getStatus, updateObj.getStatus())
+                .set(BoilerConnectRecordReportDO::getItemType, updateObj.getItemType())
+                .set(BoilerConnectRecordReportDO::getProjectCategory, updateObj.getProjectCategory())
+                .set(BoilerConnectRecordReportDO::getChargeNature, updateObj.getChargeNature())
                         .eq(BoilerConnectRecordReportDO::getId, updateObj.getId()
         )
         );

+ 21 - 3
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/service/boilerlegalreport/BoilerLegalReportServiceImpl.java

@@ -8,11 +8,13 @@ import cn.start.tz.module.pressure2.controller.admin.boilertaskorder.vo.BoilerOr
 import cn.start.tz.module.pressure2.controller.admin.boilertaskorder.vo.BoilerTaskOrderSyncReportVO;
 import cn.start.tz.module.pressure2.dal.dataobject.boilerconnectrecordreport.BoilerConnectRecordReportDO;
 import cn.start.tz.module.pressure2.dal.dataobject.boilertaskorderitem.BoilerTaskOrderItemDO;
+import cn.start.tz.module.pressure2.dal.dataobject.boilertaskorderitemfee.BoilerTaskOrderItemFeeDO;
 import cn.start.tz.module.pressure2.dal.dataobject.boilertaskorderitemreport.BoilerTaskOrderItemReportDO;
 import cn.start.tz.module.pressure2.dal.dataobject.boilertaskorderitemreportuser.BoilerTaskOrderItemReportUserDO;
 import cn.start.tz.module.pressure2.dal.dataobject.dynamictb.DynamicTbDO;
 import cn.start.tz.module.pressure2.dal.mysql.boilerconnectrecordreport.BoilerConnectRecordReportMapper;
 import cn.start.tz.module.pressure2.dal.mysql.boilertaskorderitem.BoilerTaskOrderItemMapper;
+import cn.start.tz.module.pressure2.dal.mysql.boilertaskorderitemfee.BoilerTaskOrderItemFeeMapper;
 import cn.start.tz.module.pressure2.dal.mysql.boilertaskorderitemreport.BoilerTaskOrderItemReportMapper;
 import cn.start.tz.module.pressure2.dal.mysql.boilertaskorderitemreportuser.BoilerTaskOrderItemReportUserMapper;
 import cn.start.tz.module.pressure2.dal.mysql.dynamictb.DynamicTbMapper;
@@ -62,6 +64,9 @@ public class BoilerLegalReportServiceImpl implements BoilerLegalReportService {
     @Resource
     private BoilerTaskOrderItemReportUserMapper taskOrderItemReportUserMapper;
 
+    @Resource
+    private BoilerTaskOrderItemFeeMapper boilerTaskOrderItemFeeMapper;
+
     /**
      * 反向调用任务单Service同步报表数据,加 @Lazy 避免循环依赖
      */
@@ -98,9 +103,22 @@ public class BoilerLegalReportServiceImpl implements BoilerLegalReportService {
                         .in(BoilerTaskOrderItemReportDO::getOrderItemId, orderItemIds)
                         .ne(BoilerTaskOrderItemReportDO::getTaskStatus, TaskOrderStatusEnum.CANCEL.getStatus())
         );
-        boolean hasLegalFeeItem = allItemReports.stream()
-                .anyMatch(r -> "2".equals(r.getFeeType()) && r.getFee() != null
-                        && r.getFee().compareTo(BigDecimal.ZERO) > 0);
+        boolean hasLegalFeeItem = false;
+        // 2.0 检查报告表中是否有委托收费项目
+        if (allItemReports != null) {
+            hasLegalFeeItem = allItemReports.stream()
+                    .anyMatch(r -> "2".equals(r.getFeeType()) && r.getFee() != null
+                            && r.getFee().compareTo(BigDecimal.ZERO) > 0);
+        }
+        // 2.0.1 同时检查 FEE 实例表中是否有委托收费项目
+        if (!hasLegalFeeItem) {
+            List<BoilerTaskOrderItemFeeDO> feeDOList = boilerTaskOrderItemFeeMapper.selectListByOrderId(orderId);
+            if (feeDOList != null) {
+                hasLegalFeeItem = feeDOList.stream()
+                        .anyMatch(f -> "2".equals(f.getFeeType()) && f.getFee() != null
+                                && f.getFee().compareTo(BigDecimal.ZERO) > 0);
+            }
+        }
         // 2.1 同时检查本次添加的项目中是否有委托收费项目
         if (!hasLegalFeeItem && orderItemEnterVO != null) {
             hasLegalFeeItem = orderItemEnterVO.getItemList().stream()

+ 156 - 28
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/service/boilertaskorder/BoilerTaskOrderServiceImpl.java

@@ -83,6 +83,7 @@ import cn.start.tz.module.pressure2.dal.dataobject.boilertaskorderitem.BoilerTas
 import cn.start.tz.module.pressure2.dal.dataobject.boilertaskorderitempart.BoilerTaskOrderItemPartDO;
 import cn.start.tz.module.pressure2.dal.dataobject.boilertaskorderitemrecheck.BoilerTaskOrderItemRecheckDO;
 import cn.start.tz.module.pressure2.dal.dataobject.boilertaskorderitemreport.BoilerTaskOrderItemReportDO;
+import cn.start.tz.module.pressure2.dal.dataobject.boilertaskorderitemfee.BoilerTaskOrderItemFeeDO;
 import cn.start.tz.module.pressure2.dal.dataobject.boilertaskorderitemreportequip.BoilerTaskOrderItemReportEquipDO;
 import cn.start.tz.module.pressure2.dal.dataobject.boilertaskorderitemreportrecord.BoilerTaskOrderItemReportRecordDO;
 import cn.start.tz.module.pressure2.dal.dataobject.boilertaskorderitemreportuser.BoilerTaskOrderItemReportUserDO;
@@ -387,6 +388,9 @@ public class BoilerTaskOrderServiceImpl extends ServiceImpl<BoilerTaskOrderMappe
     @Resource
     private BoilerConnectRecordReportMapper boilerConnectRecordReportMapper;
 
+    @Resource
+    private cn.start.tz.module.pressure2.dal.mysql.boilertaskorderitemfee.BoilerTaskOrderItemFeeMapper boilerTaskOrderItemFeeMapper;
+
     @Resource
     private BoilerTaskOrderItemReportVersionService versionService;
 
@@ -910,17 +914,23 @@ public class BoilerTaskOrderServiceImpl extends ServiceImpl<BoilerTaskOrderMappe
 
     private void checkAddReport(String id, BoilerTaskOrderDO taskOrderDO) {
         //TODO 新增项目
-        // 判断是否新增项目
+        // 判断是否新增项目:报告表或 FEE 实例表任一非空则跳过,避免重复生成
         List<BoilerTaskOrderItemReportDO> itemReportDOS = taskOrderItemReportMapper.selectList(new LambdaQueryWrapperX<BoilerTaskOrderItemReportDO>().
                 eq(BoilerTaskOrderItemReportDO::getOrderItemId, id).orderByAsc(BoilerTaskOrderItemReportDO::getSort));
         if (itemReportDOS != null && !itemReportDOS.isEmpty()) {
             return;
         }
+        List<BoilerTaskOrderItemFeeDO> existingFeeDOS = boilerTaskOrderItemFeeMapper.selectListByOrderItemId(id);
+        if (existingFeeDOS != null && !existingFeeDOS.isEmpty()) {
+            return;
+        }
 
         //根据前台约检勾选的检验项目生成数据
         List<EquipBoilerSchedulingCheckDO> checkDOList = equipBoilerSchedulingCheckService.lambdaQuery().eq(EquipBoilerSchedulingCheckDO::getTaskOrderId, taskOrderDO.getId()).orderByAsc(EquipBoilerSchedulingCheckDO::getSort).list();
         AtomicReference<Integer> index = new AtomicReference<>(1);
         List<BoilerTaskOrderItemReportDO> insertList = new ArrayList<>();
+        // FEE 项目实例列表(不走报告流程)
+        List<BoilerTaskOrderItemFeeDO> feeInsertList = new ArrayList<>();
         if (!checkDOList.isEmpty()){
 
             String projectCode = getProjectCodeByTaskOrderId(taskOrderDO.getId());
@@ -936,6 +946,21 @@ public class BoilerTaskOrderServiceImpl extends ServiceImpl<BoilerTaskOrderMappe
                     BoilerConnectRecordReportDO recordReportDO = boilerConnectRecordReportMapper.selectById(item.getCheckId());
 
                     if (recordReportDO != null){
+                        // 收费项目(FEE):仅记录费用实例,不创建报告
+                        if ("FEE".equals(recordReportDO.getItemType())) {
+                            BoilerTaskOrderItemFeeDO feeDO = BoilerTaskOrderItemFeeDO.builder()
+                                    .orderId(taskOrderDO.getId())
+                                    .orderItemId(id)
+                                    .projectId(recordReportDO.getId())
+                                    .projectName(recordReportDO.getProjectName())
+                                    .fee(item.getFee())
+                                    .feeType(item.getType())
+                                    .feeCalculateJson(item.getFeeCalculateJson())
+                                    .sort(item.getSort())
+                                    .build();
+                            feeInsertList.add(feeDO);
+                            return;
+                        }
                         if (recordReportDO.getRecordTemplateId()!=null){
 
                         DynamicTbDO dynamicTbDO = dynamicTbMapper.selectById(recordReportDO.getRecordTemplateId());
@@ -1090,6 +1115,10 @@ public class BoilerTaskOrderServiceImpl extends ServiceImpl<BoilerTaskOrderMappe
 
                 });
             }
+            // 新增 FEE 项目实例(不走报告流程)
+            if (!feeInsertList.isEmpty()) {
+                boilerTaskOrderItemFeeMapper.insert(feeInsertList);
+            }
         }
 
     }
@@ -1422,7 +1451,7 @@ public class BoilerTaskOrderServiceImpl extends ServiceImpl<BoilerTaskOrderMappe
         List<BoilerOrderItemCostInfoRepsVO> list = new ArrayList<>();
         // 获取检验性质和所属设备的模版列表
         List<String> natureIds = inspectionNatureService.inspectionNatureIdList(reqVO.getInspectionNature(),
-                reqVO.getEquipmentCategory(),reqVO.getEquipType());
+                reqVO.getEquipmentCategory(), reqVO.getEquipType());
         if (CollUtil.isEmpty(natureIds)) {
             return list;
         }
@@ -1435,32 +1464,34 @@ public class BoilerTaskOrderServiceImpl extends ServiceImpl<BoilerTaskOrderMappe
                 .map(natureTemplateDO -> {
 
                     BoilerConnectRecordReportDO connectRecordReportDO = boilerConnectRecordReportMapper.selectById(natureTemplateDO.getTemplateId());
-                    if (connectRecordReportDO != null){
+                    if (connectRecordReportDO != null) {
 
-                        if (connectRecordReportDO.getRecordTemplateId() != null){
-                        DynamicTbDO dynamicTbDO = dynamicTbMapper.selectById(connectRecordReportDO.getRecordTemplateId());
-                        if (dynamicTbDO != null) {
+                        if (connectRecordReportDO.getRecordTemplateId() != null) {
+                            DynamicTbDO dynamicTbDO = dynamicTbMapper.selectById(connectRecordReportDO.getRecordTemplateId());
+                            if (dynamicTbDO != null) {
 
-                            BoilerOrderItemCostInfoRepsVO repsVO = new BoilerOrderItemCostInfoRepsVO();
-                            repsVO.setOrderId(reqVO.getOrderId());
-                            //repsVO.setName(dynamicTbDO.getTbName());
-                            repsVO.setConnectId(connectRecordReportDO.getId());
-                            repsVO.setName(connectRecordReportDO.getProjectName());
-                            repsVO.setIsFee(connectRecordReportDO.getIsFee());
-                            repsVO.setFee("1".equals(connectRecordReportDO.getIsFee()) ? ConversionUtil.convert(connectRecordReportDO.getFee()) : BigDecimal.ZERO);
-                            repsVO.setTemplateId(dynamicTbDO.getId());
-                            repsVO.setIsAutoAmount("0");
-                            repsVO.setReportType(dynamicTbDO.getTbType());
-                            repsVO.setUse("1".equals(natureTemplateDO.getIsDefault())); // 默认值
-                            repsVO.setProjectType(connectRecordReportDO.getProjectType());
-                            repsVO.setIsMainProject("MAIN".equals(connectRecordReportDO.getProjectType()) ? "1" : "0");
-                            repsVO.setDefaultCount(natureTemplateDO.getDefaultCount() != null ? natureTemplateDO.getDefaultCount() : 1);
-                            repsVO.setSort(natureTemplateDO.getSort() != null ? natureTemplateDO.getSort() : 1);
-                            repsVO.setChargeNature(connectRecordReportDO.getChargeNature());
-                            return repsVO;
-                        }else {
-                            return null;
-                        }}else if (connectRecordReportDO.getReportTemplateId() != null){
+                                BoilerOrderItemCostInfoRepsVO repsVO = new BoilerOrderItemCostInfoRepsVO();
+                                repsVO.setOrderId(reqVO.getOrderId());
+                                //repsVO.setName(dynamicTbDO.getTbName());
+                                repsVO.setConnectId(connectRecordReportDO.getId());
+                                repsVO.setName(connectRecordReportDO.getProjectName());
+                                repsVO.setIsFee(connectRecordReportDO.getIsFee());
+                                repsVO.setFee("1".equals(connectRecordReportDO.getIsFee()) ? ConversionUtil.convert(connectRecordReportDO.getFee()) : BigDecimal.ZERO);
+                                repsVO.setTemplateId(dynamicTbDO.getId());
+                                repsVO.setIsAutoAmount("0");
+                                repsVO.setReportType(dynamicTbDO.getTbType());
+                                repsVO.setUse("1".equals(natureTemplateDO.getIsDefault())); // 默认值
+                                repsVO.setProjectType(connectRecordReportDO.getProjectType());
+                                repsVO.setIsMainProject("MAIN".equals(connectRecordReportDO.getProjectType()) ? "1" : "0");
+                                repsVO.setDefaultCount(natureTemplateDO.getDefaultCount() != null ? natureTemplateDO.getDefaultCount() : 1);
+                                repsVO.setSort(natureTemplateDO.getSort() != null ? natureTemplateDO.getSort() : 1);
+                                repsVO.setChargeNature(connectRecordReportDO.getChargeNature());
+                                repsVO.setItemType(connectRecordReportDO.getItemType());
+                                return repsVO;
+                            } else {
+                                return null;
+                            }
+                        } else if (connectRecordReportDO.getReportTemplateId() != null) {
                             DynamicTbDO dynamicTbDO = dynamicTbMapper.selectById(connectRecordReportDO.getReportTemplateId());
                             if (dynamicTbDO != null) {
                                 BoilerOrderItemCostInfoRepsVO repsVO = new BoilerOrderItemCostInfoRepsVO();
@@ -1479,17 +1510,38 @@ public class BoilerTaskOrderServiceImpl extends ServiceImpl<BoilerTaskOrderMappe
                                 repsVO.setDefaultCount(natureTemplateDO.getDefaultCount() != null ? natureTemplateDO.getDefaultCount() : 1);
                                 repsVO.setSort(natureTemplateDO.getSort() != null ? natureTemplateDO.getSort() : 1);
                                 repsVO.setChargeNature(connectRecordReportDO.getChargeNature());
+                                repsVO.setItemType(connectRecordReportDO.getItemType());
                                 return repsVO;
-                            }else {
+                            } else {
                                 return null;
                             }
                         }
+//                        return null;
+                        // 判断是否收费项目
+                        if ("FEE".equals(connectRecordReportDO.getItemType())) {
+                            BoilerOrderItemCostInfoRepsVO repsVO = new BoilerOrderItemCostInfoRepsVO();
+                            repsVO.setOrderId(reqVO.getOrderId());
+                            //repsVO.setName(dynamicTbDO.getTbName());
+                            repsVO.setConnectId(connectRecordReportDO.getId());
+                            repsVO.setName(connectRecordReportDO.getProjectName());
+                            repsVO.setIsFee(connectRecordReportDO.getIsFee());
+                            repsVO.setFee("1".equals(connectRecordReportDO.getIsFee()) ? ConversionUtil.convert(connectRecordReportDO.getFee()) : BigDecimal.ZERO);
+                            repsVO.setIsAutoAmount("0");
+                            repsVO.setUse("1".equals(natureTemplateDO.getIsDefault())); // 默认值
+                            repsVO.setProjectType(connectRecordReportDO.getProjectType());
+                            repsVO.setDefaultCount(natureTemplateDO.getDefaultCount() != null ? natureTemplateDO.getDefaultCount() : 1);
+                            repsVO.setSort(natureTemplateDO.getSort() != null ? natureTemplateDO.getSort() : 1);
+                            repsVO.setChargeNature(connectRecordReportDO.getChargeNature());
+                            repsVO.setItemType(connectRecordReportDO.getItemType());
+                            return repsVO;
+                        }
                         return null;
 
-                    }else{
+                    } else {
                         return null;
                     }
                 })
+                .filter(Objects::nonNull)
                 .sorted(Comparator.comparing(BoilerOrderItemCostInfoRepsVO::getSort))
                 .collect(Collectors.toCollection(ArrayList::new)); // 预分配空间
 
@@ -2779,6 +2831,24 @@ public class BoilerTaskOrderServiceImpl extends ServiceImpl<BoilerTaskOrderMappe
             );
             int count = 0;
 
+            // FEE 项目分流:跳过模板校验和报告构造,直接记录费用实例
+            BoilerConnectRecordReportDO connectCheckDO = boilerConnectRecordReportMapper.selectById(vo.getConnectId());
+            if (connectCheckDO != null && "FEE".equals(connectCheckDO.getItemType())) {
+                BoilerTaskOrderItemFeeDO feeDO = BoilerTaskOrderItemFeeDO.builder()
+                        .orderId(orderItemDOS.get(0).getOrderId())
+                        .orderItemId(vo.getOrderItemId())
+                        .projectId(connectCheckDO.getId())
+                        .projectName(connectCheckDO.getProjectName())
+                        .fee(vo.getFee() != null && vo.getFee().compareTo(BigDecimal.ZERO) > 0
+                                ? vo.getFee() : ConversionUtil.convert(connectCheckDO.getFee()))
+                        .feeType(vo.getType())
+                        .feeCalculateJson(vo.getFeeCalculateJson())
+                        .sort(vo.getSort())
+                        .build();
+                boilerTaskOrderItemFeeMapper.insert(feeDO);
+                continue;
+            }
+
             DynamicTbDO reportTemplateDO = null;
             if (StringUtils.isBlank(vo.getManualUrl())) {
                 reportTemplateDO = dynamicTbMapper.selectById(vo.getTemplateId());
@@ -3292,6 +3362,50 @@ public class BoilerTaskOrderServiceImpl extends ServiceImpl<BoilerTaskOrderMappe
                     }
                 }
             }
+            // 2. 追加 FEE 项目实例费用(收费项目不进报告表,但参与费用汇总)
+            List<BoilerTaskOrderItemFeeDO> feeDOList = boilerTaskOrderItemFeeMapper.selectListByOrderId(id);
+            if (feeDOList != null && !feeDOList.isEmpty()) {
+                // 2.1 设备项 fee 聚合:FEE 费用合并到 BoilerTaskOrderItemDO.fee
+                Map<String, BigDecimal> itemFeeMap = new HashMap<>();
+                for (BoilerTaskOrderItemFeeDO feeDO : feeDOList) {
+                    if (feeDO.getOrderItemId() != null && feeDO.getFee() != null) {
+                        itemFeeMap.merge(feeDO.getOrderItemId(), feeDO.getFee(), BigDecimal::add);
+                    }
+                }
+                for (Map.Entry<String, BigDecimal> entry : itemFeeMap.entrySet()) {
+                    BoilerTaskOrderItemDO currentItem = boilerTaskOrderItemMapper.selectById(entry.getKey());
+                    if (currentItem != null) {
+                        BoilerTaskOrderItemDO updateItem = new BoilerTaskOrderItemDO();
+                        updateItem.setId(entry.getKey());
+                        BigDecimal existingFee = currentItem.getFee() != null ? currentItem.getFee() : BigDecimal.ZERO;
+                        updateItem.setFee(existingFee.add(entry.getValue()));
+                        boilerTaskOrderItemMapper.updateById(updateItem);
+                    }
+                }
+                // 2.2 任务单费用汇总:FEE 项目参与免征/减免逻辑
+                for (BoilerTaskOrderItemFeeDO feeDO : feeDOList) {
+                    if (feeDO.getFee() != null) {
+                        if (isExempt && "1".equals(feeDO.getFeeType())) {
+                            reduceFee = reduceFee.add(feeDO.getFee());
+                            continue;
+                        }
+                        if ("2".equals(feeDO.getFeeType())) {
+                            serviceAmount = serviceAmount.add(feeDO.getFee());
+                            orderFee = orderFee.add(feeDO.getFee());
+                            continue;
+                        }
+                        if ("1".equals(feeDO.getFeeType())) {
+                            shouldAmount = shouldAmount.add(feeDO.getFee());
+                            BigDecimal reduceRadioFee = feeDO.getFee();
+                            if (boilerTaskOrderDO.getReductionRadio() != null) {
+                                double v = boilerTaskOrderDO.getReductionRadio().doubleValue();
+                                reduceRadioFee = reduceRadioFee.multiply(new BigDecimal(v)).setScale(2, RoundingMode.HALF_UP);
+                            }
+                            orderFee = orderFee.add(reduceRadioFee);
+                        }
+                    }
+                }
+            }
             // 金额发生变动,将受理单签名置空,判断全部金额都要和之前不一致
             if (!"1".equals(boilerTaskOrderDO.getIsSync()) && ObjectUtil.notEqual(shouldAmount, boilerTaskOrderDO.getShouldAmount()) || ObjectUtil.notEqual(serviceAmount, boilerTaskOrderDO.getServiceAmount()) || ObjectUtil.notEqual(reduceFee, boilerTaskOrderDO.getReduceFee()) || ObjectUtil.notEqual(orderFee, boilerTaskOrderDO.getActualAmount())) {
                 orderReportService.updateFeeBoiler(id);
@@ -3889,6 +4003,7 @@ public class BoilerTaskOrderServiceImpl extends ServiceImpl<BoilerTaskOrderMappe
                 vo.setFee(BigDecimal.ZERO);
             }
             vo.setIsFee(templateDO.getIsFee());
+            vo.setItemType(templateDO.getItemType());
             vo.setFee("1".equals(templateDO.getIsFee()) ? ConversionUtil.convert(templateDO.getFee()) : BigDecimal.ZERO);
             //vo.setRecordTemplateUrl(templateDO.getRecordTemplateUrl());
             //vo.setReportTemplateUrl(templateDO.getReportTemplateUrl());
@@ -3908,7 +4023,20 @@ public class BoilerTaskOrderServiceImpl extends ServiceImpl<BoilerTaskOrderMappe
                 vo.setFee(BigDecimal.ZERO);
             }
             vo.setIsFee(templateDO.getIsFee());
+            vo.setItemType(templateDO.getItemType());
+            vo.setFee("1".equals(templateDO.getIsFee()) ? ConversionUtil.convert(templateDO.getFee()) : BigDecimal.ZERO);
+        } else if ("FEE".equals(templateDO.getItemType())) {
+            vo.setOrderId(reqVO.getOrderId());
+            //repsVO.setName(dynamicTbDO.getTbName());
+            vo.setConnectId(templateDO.getId());
+            vo.setName(templateDO.getProjectName());
+            vo.setIsFee(templateDO.getIsFee());
             vo.setFee("1".equals(templateDO.getIsFee()) ? ConversionUtil.convert(templateDO.getFee()) : BigDecimal.ZERO);
+            vo.setIsAutoAmount("0");
+            vo.setProjectType(templateDO.getProjectType());
+            vo.setChargeNature(templateDO.getChargeNature());
+            vo.setItemType(templateDO.getItemType());
+            return vo;
         }
         vo.setFeeCalcType(templateDO.getFeeCalcType());
         if ("1".equals(templateDO.getFeeCalcType())){

+ 64 - 0
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/service/connectrecordreportcategory/ConnectRecordReportCategoryService.java

@@ -0,0 +1,64 @@
+package cn.start.tz.module.pressure2.service.connectrecordreportcategory;
+
+import cn.start.tz.framework.common.pojo.PageResult;
+import cn.start.tz.module.pressure2.controller.admin.connectrecordreportcategory.vo.ConnectRecordReportCategoryPageReqVO;
+import cn.start.tz.module.pressure2.controller.admin.connectrecordreportcategory.vo.ConnectRecordReportCategoryRespVO;
+import cn.start.tz.module.pressure2.controller.admin.connectrecordreportcategory.vo.ConnectRecordReportCategorySaveReqVO;
+import cn.start.tz.module.pressure2.dal.dataobject.connectrecordreportcategory.ConnectRecordReportCategoryDO;
+import com.baomidou.mybatisplus.extension.service.IService;
+import jakarta.validation.Valid;
+
+import java.util.List;
+
+/**
+ * 检验项目类别 Service 接口
+ *
+ * @author 特种管理员
+ */
+public interface ConnectRecordReportCategoryService extends IService<ConnectRecordReportCategoryDO> {
+
+    /**
+     * 创建检验项目类别
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    String createCategory(@Valid ConnectRecordReportCategorySaveReqVO createReqVO);
+
+    /**
+     * 更新检验项目类别
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateCategory(@Valid ConnectRecordReportCategorySaveReqVO updateReqVO);
+
+    /**
+     * 删除检验项目类别
+     *
+     * @param id 编号
+     */
+    void deleteCategory(String id);
+
+    /**
+     * 获取检验项目类别
+     *
+     * @param id 编号
+     * @return 检验项目类别
+     */
+    ConnectRecordReportCategoryDO getCategory(String id);
+
+    /**
+     * 获取检验项目类别分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 分页结果
+     */
+    PageResult<ConnectRecordReportCategoryDO> getCategoryPage(ConnectRecordReportCategoryPageReqVO pageReqVO);
+
+    /**
+     * 获取全部启用的检验项目类别(用于下拉)
+     *
+     * @return 类别列表
+     */
+    List<ConnectRecordReportCategoryRespVO> getSimpleList();
+}

+ 102 - 0
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/service/connectrecordreportcategory/ConnectRecordReportCategoryServiceImpl.java

@@ -0,0 +1,102 @@
+package cn.start.tz.module.pressure2.service.connectrecordreportcategory;
+
+import cn.start.tz.framework.common.pojo.PageResult;
+import cn.start.tz.framework.common.util.object.BeanUtils;
+import cn.start.tz.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.start.tz.module.pressure2.controller.admin.connectrecordreportcategory.vo.ConnectRecordReportCategoryPageReqVO;
+import cn.start.tz.module.pressure2.controller.admin.connectrecordreportcategory.vo.ConnectRecordReportCategoryRespVO;
+import cn.start.tz.module.pressure2.controller.admin.connectrecordreportcategory.vo.ConnectRecordReportCategorySaveReqVO;
+import cn.start.tz.module.pressure2.dal.dataobject.connectrecordreportcategory.ConnectRecordReportCategoryDO;
+import cn.start.tz.module.pressure2.dal.mysql.connectrecordreportcategory.ConnectRecordReportCategoryMapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import java.util.List;
+
+import static cn.start.tz.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.start.tz.module.pressure2.enums.ErrorCodeConstants.CONNECT_RECORD_REPORT_CATEGORY_NAME_EXISTS;
+import static cn.start.tz.module.pressure2.enums.ErrorCodeConstants.CONNECT_RECORD_REPORT_CATEGORY_NOT_EXISTS;
+
+/**
+ * 检验项目类别 Service 实现类
+ *
+ * @author 特种管理员
+ */
+@Service
+@Validated
+public class ConnectRecordReportCategoryServiceImpl extends ServiceImpl<ConnectRecordReportCategoryMapper, ConnectRecordReportCategoryDO> implements ConnectRecordReportCategoryService {
+
+    @Resource
+    private ConnectRecordReportCategoryMapper categoryMapper;
+
+    @Override
+    public String createCategory(ConnectRecordReportCategorySaveReqVO createReqVO) {
+        // 1. 校验名称唯一
+        validateNameUnique(createReqVO.getName(), null);
+        // 2. 插入
+        ConnectRecordReportCategoryDO category = BeanUtils.toBean(createReqVO, ConnectRecordReportCategoryDO.class);
+        if (category.getStatus() == null) {
+            category.setStatus("0");
+        }
+        categoryMapper.insert(category);
+        return category.getId();
+    }
+
+    @Override
+    public void updateCategory(ConnectRecordReportCategorySaveReqVO updateReqVO) {
+        // 1. 校验存在
+        validateExists(updateReqVO.getId());
+        // 2. 校验名称唯一
+        validateNameUnique(updateReqVO.getName(), updateReqVO.getId());
+        // 3. 更新
+        ConnectRecordReportCategoryDO updateObj = BeanUtils.toBean(updateReqVO, ConnectRecordReportCategoryDO.class);
+        categoryMapper.updateById(updateObj);
+    }
+
+    @Override
+    public void deleteCategory(String id) {
+        // 1. 校验存在
+        validateExists(id);
+        // 2. 删除
+        categoryMapper.deleteById(id);
+    }
+
+    @Override
+    public ConnectRecordReportCategoryDO getCategory(String id) {
+        return categoryMapper.selectById(id);
+    }
+
+    @Override
+    public PageResult<ConnectRecordReportCategoryDO> getCategoryPage(ConnectRecordReportCategoryPageReqVO pageReqVO) {
+        return categoryMapper.selectPage(pageReqVO, new LambdaQueryWrapperX<ConnectRecordReportCategoryDO>()
+                .likeIfPresent(ConnectRecordReportCategoryDO::getName, pageReqVO.getName())
+                .eqIfPresent(ConnectRecordReportCategoryDO::getStatus, pageReqVO.getStatus())
+                .orderByAsc(ConnectRecordReportCategoryDO::getSort));
+    }
+
+    @Override
+    public List<ConnectRecordReportCategoryRespVO> getSimpleList() {
+        List<ConnectRecordReportCategoryDO> list = categoryMapper.selectListAll();
+        return BeanUtils.toBean(list, ConnectRecordReportCategoryRespVO.class);
+    }
+
+    private void validateExists(String id) {
+        if (categoryMapper.selectById(id) == null) {
+            throw exception(CONNECT_RECORD_REPORT_CATEGORY_NOT_EXISTS);
+        }
+    }
+
+    private void validateNameUnique(String name, String id) {
+        if (name == null) {
+            return;
+        }
+        ConnectRecordReportCategoryDO existing = categoryMapper.selectOne(
+                new LambdaQueryWrapperX<ConnectRecordReportCategoryDO>()
+                        .eq(ConnectRecordReportCategoryDO::getName, name));
+        if (existing != null && !existing.getId().equals(id)) {
+            throw exception(CONNECT_RECORD_REPORT_CATEGORY_NAME_EXISTS);
+        }
+    }
+}

+ 123 - 0
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/service/pipetaskorder/PipeTaskOrderServiceImpl.java

@@ -106,6 +106,8 @@ import cn.start.tz.module.pressure2.dal.dataobject.pipetaskorderitem.PipeTaskOrd
 import cn.start.tz.module.pressure2.dal.dataobject.pipetaskorderitemdetail.PipeTaskOrderItemDetailDO;
 import cn.start.tz.module.pressure2.dal.dataobject.pipetaskorderitemrecheck.PipeTaskOrderItemRecheckDO;
 import cn.start.tz.module.pressure2.dal.dataobject.pipetaskorderitemreport.PipeTaskOrderItemReportDO;
+import cn.start.tz.module.pressure2.dal.dataobject.pipetaskorderitemfee.PipeTaskOrderItemFeeDO;
+import cn.start.tz.module.pressure2.dal.mysql.pipetaskorderitemfee.PipeTaskOrderItemFeeMapper;
 import cn.start.tz.module.pressure2.dal.dataobject.pipetaskorderitemreportrecord.PipeTaskOrderItemReportRecordDO;
 import cn.start.tz.module.pressure2.dal.dataobject.pipetaskorderitemreportuser.PipeTaskOrderItemReportUserDO;
 import cn.start.tz.module.pressure2.dal.dataobject.pipetaskorderitemuser.PipeTaskOrderItemUserDO;
@@ -396,6 +398,9 @@ public class PipeTaskOrderServiceImpl extends ServiceImpl<PipeTaskOrderMapper, P
     @Resource
     private BoilerConnectRecordReportMapper boilerConnectRecordReportMapper;
 
+    @Resource
+    private PipeTaskOrderItemFeeMapper pipeTaskOrderItemFeeMapper;
+
     @Resource
     private NotificationformReportMapper notificationformReportMapper;
 
@@ -887,6 +892,8 @@ public class PipeTaskOrderServiceImpl extends ServiceImpl<PipeTaskOrderMapper, P
                         .eq(PipeTaskOrderItemDO::getOrderId, taskOrderDO.getId())
         );
         List<PipeTaskOrderItemReportDO> insertList = new ArrayList<>();
+        // FEE 项目实例列表(不走报告流程)
+        List<PipeTaskOrderItemFeeDO> feeInsertList = new ArrayList<>();
         if (!checkDOList.isEmpty()){
 
             String projectCode = getProjectCodeByTaskOrderId(taskOrderDO.getId());
@@ -901,6 +908,21 @@ public class PipeTaskOrderServiceImpl extends ServiceImpl<PipeTaskOrderMapper, P
                     //checkId是记录和报告关联的主键ID
                     BoilerConnectRecordReportDO recordReportDO = boilerConnectRecordReportMapper.selectById(item.getCheckId());
                     if (recordReportDO != null){
+                        // 收费项目(FEE):仅记录费用实例,不创建报告
+                        if ("FEE".equals(recordReportDO.getItemType())) {
+                            PipeTaskOrderItemFeeDO feeDO = PipeTaskOrderItemFeeDO.builder()
+                                    .orderId(taskOrderDO.getId())
+                                    .orderItemId(null)
+                                    .projectId(recordReportDO.getId())
+                                    .projectName(recordReportDO.getProjectName())
+                                    .fee(item.getFee())
+                                    .feeType(item.getType())
+                                    .feeCalculateJson(item.getFeeCalculateJson())
+                                    .sort(item.getSort())
+                                    .build();
+                            feeInsertList.add(feeDO);
+                            return;
+                        }
                         if (recordReportDO.getRecordTemplateId() != null){
                         DynamicTbDO dynamicTbDO = dynamicTbMapper.selectById(recordReportDO.getRecordTemplateId());
                         if (dynamicTbDO != null && dynamicTbDO.getTbCode().contains("QC12116")) {
@@ -1060,6 +1082,10 @@ public class PipeTaskOrderServiceImpl extends ServiceImpl<PipeTaskOrderMapper, P
 
                 });
             }
+            // 新增 FEE 项目实例(不走报告流程)
+            if (!feeInsertList.isEmpty()) {
+                pipeTaskOrderItemFeeMapper.insert(feeInsertList);
+            }
 
         }
     }
@@ -1457,6 +1483,7 @@ public class PipeTaskOrderServiceImpl extends ServiceImpl<PipeTaskOrderMapper, P
                             repsVO.setProjectType(connectRecordReportDO.getProjectType());
                             repsVO.setIsMainProject("MAIN".equals(connectRecordReportDO.getProjectType()) ? "1" : "0");
                             repsVO.setDefaultCount(natureTemplateDO.getDefaultCount() != null ? natureTemplateDO.getDefaultCount() : 1);
+                            repsVO.setItemType(connectRecordReportDO.getItemType());
                             return repsVO;
                         }else {
                             return null;
@@ -1478,11 +1505,31 @@ public class PipeTaskOrderServiceImpl extends ServiceImpl<PipeTaskOrderMapper, P
                                 repsVO.setProjectType(connectRecordReportDO.getProjectType());
                                 repsVO.setIsMainProject("MAIN".equals(connectRecordReportDO.getProjectType()) ? "1" : "0");
                                 repsVO.setDefaultCount(natureTemplateDO.getDefaultCount() != null ? natureTemplateDO.getDefaultCount() : 1);
+                                repsVO.setItemType(connectRecordReportDO.getItemType());
                                 return repsVO;
                             }else {
                                 return null;
                             }
                         }
+//                        return null;
+                        // 判断是否收费项目
+                        if ("FEE".equals(connectRecordReportDO.getItemType())) {
+                            BoilerOrderItemCostInfoRepsVO repsVO = new BoilerOrderItemCostInfoRepsVO();
+                            repsVO.setOrderId(reqVO.getOrderId());
+                            //repsVO.setName(dynamicTbDO.getTbName());
+                            repsVO.setConnectId(connectRecordReportDO.getId());
+                            repsVO.setName(connectRecordReportDO.getProjectName());
+                            repsVO.setIsFee(connectRecordReportDO.getIsFee());
+                            repsVO.setFee("1".equals(connectRecordReportDO.getIsFee()) ? ConversionUtil.convert(connectRecordReportDO.getFee()) : BigDecimal.ZERO);
+                            repsVO.setIsAutoAmount("0");
+                            repsVO.setUse("1".equals(natureTemplateDO.getIsDefault())); // 默认值
+                            repsVO.setProjectType(connectRecordReportDO.getProjectType());
+                            repsVO.setDefaultCount(natureTemplateDO.getDefaultCount() != null ? natureTemplateDO.getDefaultCount() : 1);
+                            repsVO.setSort(natureTemplateDO.getSort() != null ? natureTemplateDO.getSort() : 1);
+                            repsVO.setChargeNature(connectRecordReportDO.getChargeNature());
+                            repsVO.setItemType(connectRecordReportDO.getItemType());
+                            return repsVO;
+                        }
                         return null;
                     }else{
                         return null;
@@ -1519,6 +1566,7 @@ public class PipeTaskOrderServiceImpl extends ServiceImpl<PipeTaskOrderMapper, P
 //            } else {
 //                vo.setFee(BigDecimal.ZERO);
 //            }
+            vo.setItemType(templateDO.getItemType());
             vo.setIsFee(templateDO.getIsFee());
             vo.setFee("1".equals(templateDO.getIsFee()) ? ConversionUtil.convert(templateDO.getFee()) : BigDecimal.ZERO);
         }else if (templateDO.getReportTemplateId() != null){
@@ -1530,6 +1578,7 @@ public class PipeTaskOrderServiceImpl extends ServiceImpl<PipeTaskOrderMapper, P
             vo.setIsAutoAmount("0");
             vo.setReportType(recordDO.getTbType());
             vo.setProjectType(templateDO.getProjectType());
+            vo.setItemType(templateDO.getItemType());
             // 将 String 类型的 fee 转换为 BigDecimal
 //            if (StringUtils.isNotBlank(templateDO.getFee())) {
 //                vo.setFee(new BigDecimal(templateDO.getFee()));
@@ -1538,7 +1587,20 @@ public class PipeTaskOrderServiceImpl extends ServiceImpl<PipeTaskOrderMapper, P
 //            }
             vo.setIsFee(templateDO.getIsFee());
             vo.setFee("1".equals(templateDO.getIsFee()) ? ConversionUtil.convert(templateDO.getFee()) : BigDecimal.ZERO);
+        }else if ("FEE".equals(templateDO.getItemType())) {
+            vo.setOrderId(reqVO.getOrderId());
+            //repsVO.setName(dynamicTbDO.getTbName());
+            vo.setConnectId(templateDO.getId());
+            vo.setName(templateDO.getProjectName());
+            vo.setIsFee(templateDO.getIsFee());
+            vo.setFee("1".equals(templateDO.getIsFee()) ? ConversionUtil.convert(templateDO.getFee()) : BigDecimal.ZERO);
+            vo.setIsAutoAmount("0");
+            vo.setProjectType(templateDO.getProjectType());
+            vo.setChargeNature(templateDO.getChargeNature());
+            vo.setItemType(templateDO.getItemType());
+            return vo;
         }
+
         return vo;
     }
 
@@ -1581,6 +1643,23 @@ public class PipeTaskOrderServiceImpl extends ServiceImpl<PipeTaskOrderMapper, P
             );
             int count = 0;
 
+            // FEE 项目分流:跳过模板校验和报告构造,直接记录费用实例
+            BoilerConnectRecordReportDO connectCheckDO = boilerConnectRecordReportMapper.selectById(vo.getConnectId());
+            if (connectCheckDO != null && "FEE".equals(connectCheckDO.getItemType())) {
+                PipeTaskOrderItemFeeDO feeDO = PipeTaskOrderItemFeeDO.builder()
+                        .orderId(orderId)
+                        .projectId(connectCheckDO.getId())
+                        .projectName(connectCheckDO.getProjectName())
+                        .fee(vo.getFee() != null && vo.getFee().compareTo(BigDecimal.ZERO) > 0
+                                ? vo.getFee() : ConversionUtil.convert(connectCheckDO.getFee()))
+                        .feeType(vo.getType())
+                        .feeCalculateJson(vo.getFeeCalculateJson())
+                        .sort(vo.getSort())
+                        .build();
+                pipeTaskOrderItemFeeMapper.insert(feeDO);
+                continue;
+            }
+
             DynamicTbDO reportTemplateDO = null;
             if (StringUtils.isBlank(vo.getManualUrl())) {
                 reportTemplateDO = dynamicTbMapper.selectById(vo.getTemplateId());
@@ -2365,6 +2444,50 @@ public class PipeTaskOrderServiceImpl extends ServiceImpl<PipeTaskOrderMapper, P
                     }
                 }
             }
+            // 2. 追加 FEE 项目实例费用(收费项目不进报告表,但参与费用汇总)
+            List<PipeTaskOrderItemFeeDO> feeDOList = pipeTaskOrderItemFeeMapper.selectListByOrderId(id);
+            if (feeDOList != null && !feeDOList.isEmpty()) {
+                // 2.1 设备项 fee 聚合:FEE 费用合并到 PipeTaskOrderItemDO.fee
+                Map<String, BigDecimal> itemFeeMap = new HashMap<>();
+                for (PipeTaskOrderItemFeeDO feeDO : feeDOList) {
+                    if (feeDO.getOrderItemId() != null && feeDO.getFee() != null) {
+                        itemFeeMap.merge(feeDO.getOrderItemId(), feeDO.getFee(), BigDecimal::add);
+                    }
+                }
+                for (Map.Entry<String, BigDecimal> entry : itemFeeMap.entrySet()) {
+                    PipeTaskOrderItemDO currentItem = pipeTaskOrderItemMapper.selectById(entry.getKey());
+                    if (currentItem != null) {
+                        PipeTaskOrderItemDO updateItem = new PipeTaskOrderItemDO();
+                        updateItem.setId(entry.getKey());
+                        BigDecimal existingFee = currentItem.getFee() != null ? currentItem.getFee() : BigDecimal.ZERO;
+                        updateItem.setFee(existingFee.add(entry.getValue()));
+                        pipeTaskOrderItemMapper.updateById(updateItem);
+                    }
+                }
+                // 2.2 任务单费用汇总:FEE 项目参与免征/减免逻辑
+                for (PipeTaskOrderItemFeeDO feeDO : feeDOList) {
+                    if (feeDO.getFee() != null) {
+                        if (isExempt && "1".equals(feeDO.getFeeType())) {
+                            reduceFee = reduceFee.add(feeDO.getFee());
+                            continue;
+                        }
+                        if ("2".equals(feeDO.getFeeType())) {
+                            serviceAmount = serviceAmount.add(feeDO.getFee());
+                            orderFee = orderFee.add(feeDO.getFee());
+                            continue;
+                        }
+                        if ("1".equals(feeDO.getFeeType())) {
+                            shouldAmount = shouldAmount.add(feeDO.getFee());
+                            BigDecimal reduceRadioFee = feeDO.getFee();
+                            if (pipeTaskOrderDO.getReductionRadio() != null) {
+                                double v = pipeTaskOrderDO.getReductionRadio().doubleValue();
+                                reduceRadioFee = reduceRadioFee.multiply(new BigDecimal(v)).setScale(2, RoundingMode.HALF_UP);
+                            }
+                            orderFee = orderFee.add(reduceRadioFee);
+                        }
+                    }
+                }
+            }
             // 金额发生变动,将受理单签名置空,判断全部金额都要和之前不一致
             if (!"1".equals(pipeTaskOrderDO.getIsSync()) && ObjectUtil.notEqual(shouldAmount, pipeTaskOrderDO.getShouldAmount()) || ObjectUtil.notEqual(serviceAmount, pipeTaskOrderDO.getServiceAmount()) || ObjectUtil.notEqual(reduceFee, pipeTaskOrderDO.getReduceFee()) || ObjectUtil.notEqual(orderFee, pipeTaskOrderDO.getActualAmount())) {
                 orderReportService.updateFeePipe(id);