diff --git a/src/main/java/com/xujie/sys/modules/erf/controller/ErfApprovalCycleReportController.java b/src/main/java/com/xujie/sys/modules/erf/controller/ErfApprovalCycleReportController.java
new file mode 100644
index 00000000..0bcd35e1
--- /dev/null
+++ b/src/main/java/com/xujie/sys/modules/erf/controller/ErfApprovalCycleReportController.java
@@ -0,0 +1,52 @@
+package com.xujie.sys.modules.erf.controller;
+
+import com.xujie.sys.common.utils.PageUtils;
+import com.xujie.sys.common.utils.R;
+import com.xujie.sys.modules.erf.data.ErfApprovalCycleQueryData;
+import com.xujie.sys.modules.erf.service.ErfApprovalCycleReportService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 审批周期报表控制器
+ *
+ *
功能说明:
+ *
+ * - 查询从申请单发起日到三位经理全部审批完成的周期报表
+ * - 统计技术/生产/质量三类经理的审批时间及审批人
+ * - 计算全部审批完成时间和审批周期天数
+ *
+ *
+ * @author System
+ * @since 2026-02-26
+ */
+@RestController
+@RequestMapping("/erf/approvalCycleReport")
+@Slf4j
+public class ErfApprovalCycleReportController {
+
+ @Autowired
+ private ErfApprovalCycleReportService erfApprovalCycleReportService;
+
+ /**
+ * @Author System
+ * @Description 查询审批周期报表列表(分页)
+ * @Date 2026/2/26
+ * @Param [data]
+ * @return com.xujie.sys.common.utils.R
+ **/
+ @PostMapping("list")
+ public R list(@RequestBody ErfApprovalCycleQueryData data) {
+ try {
+ log.info("=== 查询审批周期报表列表 === 参数: {}", data);
+
+ PageUtils page = erfApprovalCycleReportService.queryPage(data);
+
+ return R.ok().put("page", page);
+ } catch (Exception e) {
+ log.error("查询审批周期报表列表失败: " + e.getMessage(), e);
+ return R.error("查询失败: " + e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/com/xujie/sys/modules/erf/controller/ErfSampleCycleReportController.java b/src/main/java/com/xujie/sys/modules/erf/controller/ErfSampleCycleReportController.java
new file mode 100644
index 00000000..276448db
--- /dev/null
+++ b/src/main/java/com/xujie/sys/modules/erf/controller/ErfSampleCycleReportController.java
@@ -0,0 +1,53 @@
+package com.xujie.sys.modules.erf.controller;
+
+import com.xujie.sys.common.utils.PageUtils;
+import com.xujie.sys.common.utils.R;
+import com.xujie.sys.modules.erf.data.ErfExpApplyData;
+import com.xujie.sys.modules.erf.service.ErfSampleCycleReportService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 样品完成周期报表控制器
+ *
+ * 功能说明:
+ *
+ * - 查询样品完成周期报表列表(分页)
+ * - 对比期望完成时间和实际完成时间
+ * - 统计周期差异(提前/准时/延期)
+ * - 显示样品状态和入库数量
+ *
+ *
+ * @author System
+ * @since 2026-02-12
+ */
+@RestController
+@RequestMapping("/erf/sampleCycleReport")
+@Slf4j
+public class ErfSampleCycleReportController {
+
+ @Autowired
+ private ErfSampleCycleReportService erfSampleCycleReportService;
+
+ /**
+ * @Author System
+ * @Description 查询样品完成周期报表列表(分页)
+ * @Date 2026/2/12
+ * @Param [data]
+ * @return com.xujie.sys.common.utils.R
+ **/
+ @PostMapping("list")
+ public R list(@RequestBody ErfExpApplyData data) {
+ try {
+ log.info("=== 查询样品完成周期报表列表 === 参数: {}", data);
+
+ PageUtils page = erfSampleCycleReportService.queryPage(data);
+
+ return R.ok().put("page", page);
+ } catch (Exception e) {
+ log.error("查询样品完成周期报表列表失败: " + e.getMessage(), e);
+ return R.error("查询失败: " + e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/com/xujie/sys/modules/erf/data/ErfApprovalCycleQueryData.java b/src/main/java/com/xujie/sys/modules/erf/data/ErfApprovalCycleQueryData.java
new file mode 100644
index 00000000..c9aeb11a
--- /dev/null
+++ b/src/main/java/com/xujie/sys/modules/erf/data/ErfApprovalCycleQueryData.java
@@ -0,0 +1,92 @@
+package com.xujie.sys.modules.erf.data;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 审批周期报表查询条件
+ *
+ * @author System
+ * @since 2026-02-26
+ */
+@Data
+public class ErfApprovalCycleQueryData implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 申请单号(模糊查询)
+ */
+ private String applyNo;
+
+ /**
+ * 事业部代码
+ */
+ private String buNo;
+
+ /**
+ * 试验类型
+ */
+ private String experimentType;
+
+ /**
+ * 创建人姓名(模糊查询)
+ */
+ private String creatorName;
+
+ /**
+ * 发起日开始日期
+ */
+ @DateTimeFormat(pattern = "yyyy-MM-dd")
+ @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+ private Date submitStartDate;
+
+ /**
+ * 发起日结束日期
+ */
+ @DateTimeFormat(pattern = "yyyy-MM-dd")
+ @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+ private Date submitEndDate;
+
+ /**
+ * 全部审批完成开始日期
+ */
+ @DateTimeFormat(pattern = "yyyy-MM-dd")
+ @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+ private Date allApproveStartDate;
+
+ /**
+ * 全部审批完成结束日期
+ */
+ @DateTimeFormat(pattern = "yyyy-MM-dd")
+ @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+ private Date allApproveEndDate;
+
+ /**
+ * 审批周期最小天数(含)
+ */
+ private Integer cycleDaysMin;
+
+ /**
+ * 审批周期最大天数(含)
+ */
+ private Integer cycleDaysMax;
+
+ /**
+ * 申请单状态过滤
+ */
+ private String status;
+
+ /**
+ * 页码
+ */
+ private Integer page;
+
+ /**
+ * 每页大小
+ */
+ private Integer limit;
+}
diff --git a/src/main/java/com/xujie/sys/modules/erf/data/ErfExpApplyData.java b/src/main/java/com/xujie/sys/modules/erf/data/ErfExpApplyData.java
index e6dae69a..d93d1418 100644
--- a/src/main/java/com/xujie/sys/modules/erf/data/ErfExpApplyData.java
+++ b/src/main/java/com/xujie/sys/modules/erf/data/ErfExpApplyData.java
@@ -236,6 +236,30 @@ public class ErfExpApplyData implements Serializable {
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date expectedFinishEndDate;
+ /**
+ * 实际开始日期
+ */
+ @DateTimeFormat(pattern = "yyyy-MM-dd")
+ @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+ private Date actualFinishStartDate;
+
+ /**
+ * 实际结束日期
+ */
+ @DateTimeFormat(pattern = "yyyy-MM-dd")
+ @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+ private Date actualFinishEndDate;
+
+ /**
+ * 周期差异最小值(用于筛选,单位:天)
+ */
+ private Integer cycleDifferenceMin;
+
+ /**
+ * 周期差异最大值(用于筛选,单位:天)
+ */
+ private Integer cycleDifferenceMax;
+
private String pendingStatus;
/**
diff --git a/src/main/java/com/xujie/sys/modules/erf/dto/ErfApprovalCycleReportDto.java b/src/main/java/com/xujie/sys/modules/erf/dto/ErfApprovalCycleReportDto.java
new file mode 100644
index 00000000..ed783533
--- /dev/null
+++ b/src/main/java/com/xujie/sys/modules/erf/dto/ErfApprovalCycleReportDto.java
@@ -0,0 +1,135 @@
+package com.xujie.sys.modules.erf.dto;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 审批周期报表DTO
+ *
+ * 用途:统计从申请单发起日到三位经理(技术/生产/质量)全部审批完成的周期
+ *
+ * 核心字段说明:
+ *
+ * - submitTime:申请单发起日(下达时间)
+ * - techApproveTime:技术经理审批完成时间
+ * - prodApproveTime:生产经理全部审批完成时间(最晚的那位)
+ * - qualApproveTime:质量经理全部审批完成时间(最晚的那位)
+ * - allApproveTime:三类经理全部审批完成时间
+ * - approvalCycleDays:审批周期(从发起日到全部审批完成的天数)
+ *
+ *
+ * @author System
+ * @since 2026-02-26
+ */
+@Data
+public class ErfApprovalCycleReportDto implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 申请单号
+ */
+ private String applyNo;
+
+ /**
+ * 事业部编码
+ */
+ private String buNo;
+
+ /**
+ * 事业部名称
+ */
+ private String buName;
+
+ /**
+ * 试验类型(High Risk/Low Risk)
+ */
+ private String experimentType;
+
+ /**
+ * 试验名称
+ */
+ private String title;
+
+ /**
+ * 创建人姓名
+ */
+ private String creatorName;
+
+ /**
+ * 当前状态
+ */
+ private String status;
+
+ /**
+ * 创建时间
+ */
+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ private Date createTime;
+
+ /**
+ * 申请单发起日(下达时间)
+ */
+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ private Date submitTime;
+
+ /**
+ * 技术经理姓名
+ */
+ private String techManagerName;
+
+ /**
+ * 技术经理审批完成时间
+ */
+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ private Date techApproveTime;
+
+ /**
+ * 生产经理姓名(多人时用顿号分隔)
+ */
+ private String prodManagerName;
+
+ /**
+ * 生产经理全部审批完成时间(即最晚完成的那位审批时间)
+ */
+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ private Date prodApproveTime;
+
+ /**
+ * 质量经理姓名(多人时用顿号分隔)
+ */
+ private String qualManagerName;
+
+ /**
+ * 质量经理全部审批完成时间(即最晚完成的那位审批时间)
+ */
+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ private Date qualApproveTime;
+
+ /**
+ * 三类经理全部审批完成时间(取三者中最晚的时间)
+ */
+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ private Date allApproveTime;
+
+ /**
+ * 审批周期(天数)
+ * 从申请单发起日(submit_time)到全部审批完成时间(all_approve_time)的自然天数
+ * 正数=审批用时天数,null=尚未全部审批完成
+ */
+ private Integer approvalCycleDays;
+
+ /**
+ * 是否全部审批完成(技术/生产/质量三类均已批准)
+ */
+ private Boolean isAllApproved;
+}
diff --git a/src/main/java/com/xujie/sys/modules/erf/dto/ErfSampleCycleReportDto.java b/src/main/java/com/xujie/sys/modules/erf/dto/ErfSampleCycleReportDto.java
new file mode 100644
index 00000000..7d606d6b
--- /dev/null
+++ b/src/main/java/com/xujie/sys/modules/erf/dto/ErfSampleCycleReportDto.java
@@ -0,0 +1,106 @@
+package com.xujie.sys.modules.erf.dto;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 样品完成周期报表DTO
+ *
+ * 用途:对比期望完成时间和实际完成时间,统计样品生产周期
+ *
+ * @author System
+ * @since 2026-02-12
+ */
+@Data
+public class ErfSampleCycleReportDto implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 申请单号
+ */
+ private String applyNo;
+
+ /**
+ * 试验名称
+ */
+ private String title;
+
+ /**
+ * 试验类型(High Risk/Low Risk)
+ */
+ private String experimentType;
+
+ /**
+ * 事业部编码
+ */
+ private String buNo;
+
+ /**
+ * 事业部名称
+ */
+ private String buName;
+
+ /**
+ * 创建人姓名
+ */
+ private String creatorName;
+
+ /**
+ * 期望完成日期(第一步工程师填写)
+ */
+ @DateTimeFormat(pattern = "yyyy-MM-dd")
+ @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+ private Date expectedFinishDate;
+
+ /**
+ * 实际完成日期(步骤七工程师填写)
+ */
+ @DateTimeFormat(pattern = "yyyy-MM-dd")
+ @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+ private Date actualFinishDate;
+
+ /**
+ * 周期差异(天数)
+ * 正数:延期天数
+ * 负数:提前天数
+ * 0:准时完成
+ */
+ private Integer cycleDifference;
+
+ /**
+ * 是否延期(true=延期, false=准时或提前)
+ */
+ private Boolean isDelayed;
+
+ /**
+ * 样品最终状态(NORMAL=正常入库, SCRAPPED=报废)
+ */
+ private String finalStatus;
+
+ /**
+ * 样品最终状态描述
+ */
+ private String finalStatusDesc;
+
+ /**
+ * 正常入库数量
+ */
+ private BigDecimal finalQuantity;
+
+ /**
+ * 当前状态
+ */
+ private String status;
+
+ /**
+ * 创建时间
+ */
+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ private Date createTime;
+}
diff --git a/src/main/java/com/xujie/sys/modules/erf/mapper/ErfExpApplyMapper.java b/src/main/java/com/xujie/sys/modules/erf/mapper/ErfExpApplyMapper.java
index e12c44aa..5a6e96c1 100644
--- a/src/main/java/com/xujie/sys/modules/erf/mapper/ErfExpApplyMapper.java
+++ b/src/main/java/com/xujie/sys/modules/erf/mapper/ErfExpApplyMapper.java
@@ -1,7 +1,9 @@
package com.xujie.sys.modules.erf.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.xujie.sys.modules.erf.data.ErfApprovalCycleQueryData;
import com.xujie.sys.modules.erf.data.ErfExpApplyData;
+import com.xujie.sys.modules.erf.dto.ErfApprovalCycleReportDto;
import com.xujie.sys.modules.erf.entity.ErfExpApply;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@@ -44,6 +46,14 @@ public interface ErfExpApplyMapper extends BaseMapper {
String getSiteByBu(String buNo);
+ /**
+ * 查询审批周期报表列表
+ *
+ * @param data 查询条件
+ * @return 审批周期报表列表
+ */
+ List getApprovalCycleReportList(@Param("data") ErfApprovalCycleQueryData data);
+
/**
* 删除三方确认明细
*
diff --git a/src/main/java/com/xujie/sys/modules/erf/service/ErfApprovalCycleReportService.java b/src/main/java/com/xujie/sys/modules/erf/service/ErfApprovalCycleReportService.java
new file mode 100644
index 00000000..9ae127c2
--- /dev/null
+++ b/src/main/java/com/xujie/sys/modules/erf/service/ErfApprovalCycleReportService.java
@@ -0,0 +1,21 @@
+package com.xujie.sys.modules.erf.service;
+
+import com.xujie.sys.common.utils.PageUtils;
+import com.xujie.sys.modules.erf.data.ErfApprovalCycleQueryData;
+
+/**
+ * 审批周期报表服务接口
+ *
+ * @author System
+ * @since 2026-02-26
+ */
+public interface ErfApprovalCycleReportService {
+
+ /**
+ * 查询审批周期报表列表(分页)
+ *
+ * @param data 查询条件
+ * @return 分页结果
+ */
+ PageUtils queryPage(ErfApprovalCycleQueryData data);
+}
diff --git a/src/main/java/com/xujie/sys/modules/erf/service/ErfSampleCycleReportService.java b/src/main/java/com/xujie/sys/modules/erf/service/ErfSampleCycleReportService.java
new file mode 100644
index 00000000..e4bb2f2e
--- /dev/null
+++ b/src/main/java/com/xujie/sys/modules/erf/service/ErfSampleCycleReportService.java
@@ -0,0 +1,21 @@
+package com.xujie.sys.modules.erf.service;
+
+import com.xujie.sys.common.utils.PageUtils;
+import com.xujie.sys.modules.erf.data.ErfExpApplyData;
+
+/**
+ * 样品完成周期报表服务接口
+ *
+ * @author System
+ * @since 2026-02-12
+ */
+public interface ErfSampleCycleReportService {
+
+ /**
+ * 查询样品完成周期报表列表(分页)
+ *
+ * @param data 查询参数
+ * @return 分页数据
+ */
+ PageUtils queryPage(ErfExpApplyData data);
+}
diff --git a/src/main/java/com/xujie/sys/modules/erf/service/impl/ErfApprovalCycleReportServiceImpl.java b/src/main/java/com/xujie/sys/modules/erf/service/impl/ErfApprovalCycleReportServiceImpl.java
new file mode 100644
index 00000000..93dccb6d
--- /dev/null
+++ b/src/main/java/com/xujie/sys/modules/erf/service/impl/ErfApprovalCycleReportServiceImpl.java
@@ -0,0 +1,159 @@
+package com.xujie.sys.modules.erf.service.impl;
+
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import com.xujie.sys.common.utils.PageUtils;
+import com.xujie.sys.modules.erf.data.ErfApprovalCycleQueryData;
+import com.xujie.sys.modules.erf.dto.ErfApprovalCycleReportDto;
+import com.xujie.sys.modules.erf.mapper.ErfExpApplyMapper;
+import com.xujie.sys.modules.erf.service.ErfApprovalCycleReportService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.temporal.ChronoUnit;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 审批周期报表服务实现类
+ *
+ * 核心功能:
+ *
+ * - 查询从申请单发起日到三类经理(技术/生产/质量)全部审批完成的记录
+ * - 计算全部审批完成时间(取三类经理中最晚的审批时间)
+ * - 计算审批周期天数(从submit_time到allApproveTime)
+ * - 支持按审批周期天数范围过滤
+ *
+ *
+ * @author System
+ * @since 2026-02-26
+ */
+@Slf4j
+@Service
+public class ErfApprovalCycleReportServiceImpl implements ErfApprovalCycleReportService {
+
+ @Autowired
+ private ErfExpApplyMapper erfExpApplyMapper;
+
+ @Override
+ public PageUtils queryPage(ErfApprovalCycleQueryData data) {
+ log.info("=== 查询审批周期报表列表 ===");
+
+ // 分页查询
+ PageHelper.startPage(data.getPage(), data.getLimit());
+ List reportList = erfExpApplyMapper.getApprovalCycleReportList(data);
+ PageInfo pageInfo = new PageInfo<>(reportList);
+
+ // 计算审批周期及完成状态
+ reportList.forEach(this::calculateApprovalCycle);
+
+ // 按审批完成时间范围过滤(post-filter,因为allApproveTime在Java中计算)
+ if (data.getAllApproveStartDate() != null || data.getAllApproveEndDate() != null) {
+ reportList = reportList.stream()
+ .filter(dto -> {
+ Date allApproveTime = dto.getAllApproveTime();
+ if (allApproveTime == null) {
+ return false;
+ }
+ LocalDate approveDate = convertToLocalDate(allApproveTime);
+ if (data.getAllApproveStartDate() != null) {
+ LocalDate startDate = convertToLocalDate(data.getAllApproveStartDate());
+ if (approveDate.isBefore(startDate)) {
+ return false;
+ }
+ }
+ if (data.getAllApproveEndDate() != null) {
+ LocalDate endDate = convertToLocalDate(data.getAllApproveEndDate());
+ if (approveDate.isAfter(endDate)) {
+ return false;
+ }
+ }
+ return true;
+ })
+ .collect(Collectors.toList());
+ }
+
+ // 按审批周期天数范围过滤
+ if (data.getCycleDaysMin() != null || data.getCycleDaysMax() != null) {
+ reportList = reportList.stream()
+ .filter(dto -> {
+ Integer days = dto.getApprovalCycleDays();
+ if (days == null) {
+ return false;
+ }
+ boolean matchMin = data.getCycleDaysMin() == null || days >= data.getCycleDaysMin();
+ boolean matchMax = data.getCycleDaysMax() == null || days <= data.getCycleDaysMax();
+ return matchMin && matchMax;
+ })
+ .collect(Collectors.toList());
+ }
+
+ log.info("查询完成,共{}条记录", reportList.size());
+ return new PageUtils(reportList, (int) pageInfo.getTotal(), data.getLimit(), data.getPage());
+ }
+
+ /**
+ * 计算审批周期相关字段
+ *
+ * 逻辑说明:
+ *
+ * - allApproveTime = MAX(techApproveTime, prodApproveTime, qualApproveTime),
+ * 要求三者都不为null才视为全部完成
+ * - approvalCycleDays = DATEDIFF(day, submitTime, allApproveTime)
+ *
+ *
+ * @param dto 报表DTO
+ */
+ private void calculateApprovalCycle(ErfApprovalCycleReportDto dto) {
+ Date techTime = dto.getTechApproveTime();
+ Date prodTime = dto.getProdApproveTime();
+ Date qualTime = dto.getQualApproveTime();
+
+ // 三类经理均已审批完成,才认为全部完成
+ boolean allApproved = (techTime != null && prodTime != null && qualTime != null);
+ dto.setIsAllApproved(allApproved);
+
+ if (allApproved) {
+ // 取三者中最晚的时间作为全部审批完成时间
+ Date allApproveTime = List.of(techTime, prodTime, qualTime)
+ .stream()
+ .max(Comparator.naturalOrder())
+ .orElse(null);
+ dto.setAllApproveTime(allApproveTime);
+
+ // 计算审批周期天数
+ if (dto.getSubmitTime() != null && allApproveTime != null) {
+ LocalDate submitDate = convertToLocalDate(dto.getSubmitTime());
+ LocalDate approveDate = convertToLocalDate(allApproveTime);
+ long days = ChronoUnit.DAYS.between(submitDate, approveDate);
+ if (days < 1) {
+ days = 1;
+ }
+ dto.setApprovalCycleDays((int) days);
+ }
+ } else {
+ dto.setAllApproveTime(null);
+ dto.setApprovalCycleDays(null);
+ }
+ }
+
+ /**
+ * 将Date转换为LocalDate
+ *
+ * @param date Date对象
+ * @return LocalDate对象
+ */
+ private LocalDate convertToLocalDate(Date date) {
+ if (date instanceof java.sql.Date) {
+ return ((java.sql.Date) date).toLocalDate();
+ }
+ return date.toInstant()
+ .atZone(ZoneId.systemDefault())
+ .toLocalDate();
+ }
+}
diff --git a/src/main/java/com/xujie/sys/modules/erf/service/impl/ErfSampleCycleReportServiceImpl.java b/src/main/java/com/xujie/sys/modules/erf/service/impl/ErfSampleCycleReportServiceImpl.java
new file mode 100644
index 00000000..403d92bd
--- /dev/null
+++ b/src/main/java/com/xujie/sys/modules/erf/service/impl/ErfSampleCycleReportServiceImpl.java
@@ -0,0 +1,139 @@
+package com.xujie.sys.modules.erf.service.impl;
+
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import com.xujie.sys.common.utils.PageUtils;
+import com.xujie.sys.modules.erf.data.ErfExpApplyData;
+import com.xujie.sys.modules.erf.dto.ErfSampleCycleReportDto;
+import com.xujie.sys.modules.erf.mapper.ErfExpApplyMapper;
+import com.xujie.sys.modules.erf.service.ErfSampleCycleReportService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.temporal.ChronoUnit;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 样品完成周期报表服务实现类
+ *
+ * 核心功能:
+ *
+ * - 对比期望完成时间和实际完成时间
+ * - 计算周期差异(提前/准时/延期)
+ * - 统计样品状态和数量
+ *
+ *
+ * @author System
+ * @since 2026-02-12
+ */
+@Slf4j
+@Service
+public class ErfSampleCycleReportServiceImpl implements ErfSampleCycleReportService {
+
+ @Autowired
+ private ErfExpApplyMapper erfExpApplyMapper;
+
+ @Override
+ public PageUtils queryPage(ErfExpApplyData data) {
+ log.info("=== 查询样品完成周期报表列表 ===");
+
+ // 分页查询
+ PageHelper.startPage(data.getPage(), data.getLimit());
+ List applyList = erfExpApplyMapper.searchExpApplyList(data);
+ PageInfo pageInfo = new PageInfo<>(applyList);
+
+ // 转换为报表DTO
+ List reportList = applyList.stream()
+ .map(this::convertToReportDto)
+ .collect(Collectors.toList());
+
+ // 过滤周期差异范围(如果有设置)
+ if (data.getCycleDifferenceMin() != null || data.getCycleDifferenceMax() != null) {
+ reportList = reportList.stream()
+ .filter(dto -> {
+ Integer diff = dto.getCycleDifference();
+ if (diff == null) {
+ return false;
+ }
+ boolean matchMin = data.getCycleDifferenceMin() == null || diff >= data.getCycleDifferenceMin();
+ boolean matchMax = data.getCycleDifferenceMax() == null || diff <= data.getCycleDifferenceMax();
+ return matchMin && matchMax;
+ })
+ .collect(Collectors.toList());
+ }
+
+ log.info("查询完成,共{}条记录", reportList.size());
+ return new PageUtils(reportList, reportList.size(), data.getLimit(), data.getPage());
+ }
+
+ /**
+ * 转换为报表DTO
+ *
+ * @param data 申请单数据
+ * @return 报表DTO
+ */
+ private ErfSampleCycleReportDto convertToReportDto(ErfExpApplyData data) {
+ ErfSampleCycleReportDto dto = new ErfSampleCycleReportDto();
+
+ // 基本信息
+ dto.setApplyNo(data.getApplyNo());
+ dto.setTitle(data.getTitle());
+ dto.setExperimentType(data.getExperimentType());
+ dto.setBuNo(data.getBuNo());
+ dto.setBuName(data.getBuDesc());
+ dto.setCreatorName(data.getCreatorName());
+ dto.setStatus(data.getStatus());
+ dto.setCreateTime(data.getCreateTime());
+
+ // 时间信息
+ dto.setExpectedFinishDate(data.getExpectedFinishDate());
+ dto.setActualFinishDate(data.getActualFinishDate());
+
+ // 计算周期差异
+ if (data.getExpectedFinishDate() != null && data.getActualFinishDate() != null) {
+ LocalDate expectedDate = convertToLocalDate(data.getExpectedFinishDate());
+ LocalDate actualDate = convertToLocalDate(data.getActualFinishDate());
+
+ // 计算天数差异(实际 - 期望)
+ long daysDiff = ChronoUnit.DAYS.between(expectedDate, actualDate);
+ dto.setCycleDifference((int) daysDiff);
+
+ // 判断是否延期
+ dto.setIsDelayed(daysDiff > 0);
+ }
+
+ // 样品状态和数量
+ dto.setFinalStatus(data.getFinalStatus());
+ dto.setFinalQuantity(data.getFinalQuantity());
+
+ // 样品状态描述
+ if ("NORMAL".equals(data.getFinalStatus())) {
+ dto.setFinalStatusDesc("正常入库");
+ } else if ("SCRAPPED".equals(data.getFinalStatus())) {
+ dto.setFinalStatusDesc("报废");
+ } else {
+ dto.setFinalStatusDesc("未确认");
+ }
+
+ return dto;
+ }
+
+ /**
+ * 将Date转换为LocalDate
+ *
+ * @param date Date对象
+ * @return LocalDate对象
+ */
+ private LocalDate convertToLocalDate(java.util.Date date) {
+ if (date instanceof java.sql.Date) {
+ return ((java.sql.Date) date).toLocalDate();
+ }
+ return date.toInstant()
+ .atZone(ZoneId.systemDefault())
+ .toLocalDate();
+ }
+}
diff --git a/src/main/resources/mapper/erf/ErfExpApplyMapper.xml b/src/main/resources/mapper/erf/ErfExpApplyMapper.xml
index c9481466..a692b3fe 100644
--- a/src/main/resources/mapper/erf/ErfExpApplyMapper.xml
+++ b/src/main/resources/mapper/erf/ErfExpApplyMapper.xml
@@ -99,6 +99,21 @@
AND product_type LIKE '%' + #{data.productType} + '%'
+
+ AND creator_name LIKE '%' + #{data.creatorName} + '%'
+
+
+ AND CAST(expected_finish_date AS DATE) >= #{data.expectedFinishStartDate}
+
+
+ AND CAST(expected_finish_date AS DATE) <= #{data.expectedFinishEndDate}
+
+
+ AND CAST(actual_finish_date AS DATE) >= #{data.actualFinishStartDate}
+
+
+ AND CAST(actual_finish_date AS DATE) <= #{data.actualFinishEndDate}
+
ORDER BY create_time DESC
@@ -268,6 +283,153 @@
AND order_ref6 = #{orderRef6}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
INSERT INTO sys_oss