From 0be02df3d287f802c76e5738916301a877dfaa0e Mon Sep 17 00:00:00 2001
From: gongchunyi <deslre0381@gmail.com>
Date: 星期一, 27 四月 2026 16:56:12 +0800
Subject: [PATCH] feat: 生产报工与报工台账功能更改完成

---
 doc/20260427_create_table_production_product_report_daily.sql                         |   22 +
 src/main/java/com/ruoyi/production/pojo/ProductionProductReportDaily.java             |   66 +++
 src/main/resources/mapper/production/ProductWorkOrderMapper.xml                       |   30 +
 src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java |  561 +++++++++++++++++++------
 src/main/java/com/ruoyi/production/mapper/ProductionProductReportDailyMapper.java     |   22 +
 doc/20260427_alter_report_duration_minutes_to_decimal.sql                             |    5 
 src/main/resources/mapper/production/ProductionProductReportDailyMapper.xml           |   32 +
 src/main/java/com/ruoyi/production/mapper/ProductionProductMainMapper.java            |    8 
 src/main/java/com/ruoyi/production/controller/ProductionProductMainController.java    |   83 +++
 src/main/java/com/ruoyi/production/dto/ProductionProductMainDto.java                  |   41 +
 src/main/java/com/ruoyi/production/mapper/ProductWorkOrderMapper.java                 |    9 
 src/main/java/com/ruoyi/production/service/ProductionProductMainService.java          |   25 +
 src/main/java/com/ruoyi/production/mapper/ProductOrderMapper.java                     |   10 
 src/main/java/com/ruoyi/production/service/impl/ProductWorkOrderServiceImpl.java      |   96 ++++
 src/main/java/com/ruoyi/production/dto/ProductionReportStateDto.java                  |   22 +
 src/main/resources/mapper/production/ProductOrderMapper.xml                           |   10 
 src/main/java/com/ruoyi/production/pojo/ProductionProductMain.java                    |   10 
 src/main/java/com/ruoyi/production/controller/ProductWorkOrderController.java         |    2 
 doc/20260427_alter_production_product_main_add_report_duration.sql                    |    4 
 src/main/java/com/ruoyi/production/dto/ProductionReportDailySummaryDto.java           |   19 
 src/main/resources/mapper/production/ProductionProductMainMapper.xml                  |  206 ++++++++
 src/main/resources/mapper/system/SysUserMapper.xml                                    |    2 
 src/main/java/com/ruoyi/production/dto/ProductWorkOrderDto.java                       |   13 
 23 files changed, 1,128 insertions(+), 170 deletions(-)

diff --git a/doc/20260427_alter_production_product_main_add_report_duration.sql b/doc/20260427_alter_production_product_main_add_report_duration.sql
new file mode 100644
index 0000000..49a4a7a
--- /dev/null
+++ b/doc/20260427_alter_production_product_main_add_report_duration.sql
@@ -0,0 +1,4 @@
+alter table production_product_main
+    add column report_start_time datetime null comment '鎶ュ伐寮�濮嬫椂闂�' after status,
+    add column report_end_time datetime null comment '鎶ュ伐缁撴潫鏃堕棿' after report_start_time,
+    add column report_duration_minutes decimal(16,2) null comment '瀹為檯鎶ュ伐鏃堕暱(鍒嗛挓)' after report_end_time;
diff --git a/doc/20260427_alter_report_duration_minutes_to_decimal.sql b/doc/20260427_alter_report_duration_minutes_to_decimal.sql
new file mode 100644
index 0000000..7c395ec
--- /dev/null
+++ b/doc/20260427_alter_report_duration_minutes_to_decimal.sql
@@ -0,0 +1,5 @@
+alter table production_product_main
+    modify column report_duration_minutes decimal(16,2) null comment '瀹為檯鎶ュ伐鏃堕暱(鍒嗛挓)';
+
+alter table production_product_report_daily
+    modify column duration_minutes decimal(16,2) not null comment '褰撴棩鏃堕暱(鍒嗛挓)';
diff --git a/doc/20260427_create_table_production_product_report_daily.sql b/doc/20260427_create_table_production_product_report_daily.sql
new file mode 100644
index 0000000..4b83aae
--- /dev/null
+++ b/doc/20260427_create_table_production_product_report_daily.sql
@@ -0,0 +1,22 @@
+drop table if exists production_product_report_daily;
+create table production_product_report_daily
+(
+    id                            bigint auto_increment primary key,
+    product_main_id               bigint   not null comment '鎶ュ伐涓昏〃ID',
+    work_order_id                 bigint   not null comment '宸ュ崟ID',
+    product_process_route_item_id bigint   not null comment '宸ヨ壓璺嚎椤圭洰ID',
+    user_id                       bigint   not null comment '鎶ュ伐浜篒D',
+    report_date                   date     not null comment '鎶ュ伐鏃ユ湡(鎸夊ぉ缁熻)',
+    start_time                    datetime not null comment '褰撴棩寮�濮嬫椂闂�',
+    end_time                      datetime not null comment '褰撴棩缁撴潫鏃堕棿',
+    duration_minutes              decimal(16,2) not null comment '褰撴棩鏃堕暱(鍒嗛挓)',
+    create_time                   datetime null comment '鍒涘缓鏃堕棿',
+    create_user                   int      null comment '鍒涘缓鐢ㄦ埛',
+    update_time                   datetime null comment '淇敼鏃堕棿',
+    update_user                   int      null comment '淇敼鐢ㄦ埛',
+    tenant_id                     bigint   not null comment '绉熸埛ID',
+    dept_id                       bigint   null comment '閮ㄩ棬ID',
+    key idx_product_main_id (product_main_id),
+    key idx_work_order_user_date (work_order_id, user_id, report_date),
+    key idx_user_date (user_id, report_date)
+) comment '鐢熶骇鎶ュ伐-姣忔棩鏃堕暱鏄庣粏';
diff --git a/src/main/java/com/ruoyi/production/controller/ProductWorkOrderController.java b/src/main/java/com/ruoyi/production/controller/ProductWorkOrderController.java
index 029e457..fe8a410 100644
--- a/src/main/java/com/ruoyi/production/controller/ProductWorkOrderController.java
+++ b/src/main/java/com/ruoyi/production/controller/ProductWorkOrderController.java
@@ -36,7 +36,7 @@
     @PostMapping ("/updateProductWorkOrder")
     public R updateProductWorkOrder(@RequestBody ProductWorkOrderDto productWorkOrderDto) {
         return R.ok(productWorkOrderservice.updateProductWorkOrder(productWorkOrderDto));
-    }
+    }   
 
     /**
      * pda鏍规嵁浜岀淮鐮佺殑宸ュ崟id鏌ヨ鏁版嵁
diff --git a/src/main/java/com/ruoyi/production/controller/ProductionProductMainController.java b/src/main/java/com/ruoyi/production/controller/ProductionProductMainController.java
index e4a0813..e4ef6a4 100644
--- a/src/main/java/com/ruoyi/production/controller/ProductionProductMainController.java
+++ b/src/main/java/com/ruoyi/production/controller/ProductionProductMainController.java
@@ -3,18 +3,20 @@
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.framework.web.domain.R;
-import com.ruoyi.production.dto.ProductProcessRouteItemDto;
 import com.ruoyi.production.dto.ProductionProductMainDto;
-import com.ruoyi.production.dto.SalesLedgerProductionAccountingDto;
+import com.ruoyi.production.dto.ProductionReportDailySummaryDto;
+import com.ruoyi.production.dto.ProductionReportStateDto;
+import com.ruoyi.production.pojo.ProductionProductMain;
 import com.ruoyi.production.service.ProductionProductMainService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletResponse;
-import java.util.Arrays;
+import java.time.LocalDate;
 import java.util.List;
 
 @RequestMapping("productionProductMain")
@@ -31,9 +33,48 @@
      * @param productionProductMainDto
      * @return
      */
+    @ApiOperation("鎶ュ伐鍙拌处姹囨�诲垎椤�(褰撳墠鐧诲綍浜�)")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "current", value = "椤电爜", dataType = "long", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "姣忛〉鏁伴噺", dataType = "long", paramType = "query"),
+            @ApiImplicitParam(name = "workOrderNo", value = "宸ュ崟缂栧彿(妯$硦)", dataType = "string", paramType = "query"),
+            @ApiImplicitParam(name = "workOrderStatus", value = "宸ュ崟鐘舵��", dataType = "string", paramType = "query")
+    })
     @GetMapping("listPage")
-    public R page(Page<ProductionProductMainDto> page, ProductionProductMainDto productionProductMainDto) {
+    public R<?> page(Page<ProductionProductMainDto> page, ProductionProductMainDto productionProductMainDto) {
         return R.ok(productionProductMainService.listPageProductionProductMainDto(page, productionProductMainDto));
+    }
+
+    /**
+     * 鎶ュ伐鏄庣粏鏌ヨ(姣忔潯鎶ュ伐璁板綍)
+     */
+    @ApiOperation("鎶ュ伐鏄庣粏鍒嗛〉(姣忔潯鎶ュ伐璁板綍, 褰撳墠鐧诲綍浜�)")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "current", value = "椤电爜", dataType = "long", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "姣忛〉鏁伴噺", dataType = "long", paramType = "query"),
+            @ApiImplicitParam(name = "workOrderId", value = "宸ュ崟ID(寤鸿蹇呬紶)", dataType = "long", paramType = "query"),
+            @ApiImplicitParam(name = "startDate", value = "寮�濮嬫棩鏈�(鎸夌粨鏉熸椂闂磋繃婊�, yyyy-MM-dd)", dataType = "string", paramType = "query"),
+            @ApiImplicitParam(name = "endDate", value = "缁撴潫鏃ユ湡(鎸夌粨鏉熸椂闂磋繃婊�, yyyy-MM-dd)", dataType = "string", paramType = "query")
+    })
+    @GetMapping("listPageDetail")
+    public R<?> pageDetail(Page<ProductionProductMainDto> page, ProductionProductMainDto productionProductMainDto) {
+        return R.ok(productionProductMainService.listPageProductionProductMainDetailDto(page, productionProductMainDto));
+    }
+
+    /**
+     * 鎶ュ伐鏄庣粏姹囨��(姣忎釜浜哄憳姣忓ぉ)
+     */
+    @ApiOperation("鎶ュ伐姣忔棩姹囨�诲垎椤�(姣忎汉姣忓ぉ, 褰撳墠鐧诲綍浜�)")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "current", value = "椤电爜", dataType = "long", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "姣忛〉鏁伴噺", dataType = "long", paramType = "query"),
+            @ApiImplicitParam(name = "workOrderId", value = "宸ュ崟ID(寤鸿蹇呬紶)", dataType = "long", paramType = "query"),
+            @ApiImplicitParam(name = "startDate", value = "寮�濮嬫棩鏈�(report_date, yyyy-MM-dd)", dataType = "string", paramType = "query"),
+            @ApiImplicitParam(name = "endDate", value = "缁撴潫鏃ユ湡(report_date, yyyy-MM-dd)", dataType = "string", paramType = "query")
+    })
+    @GetMapping("listPageDaily")
+    public R<?> pageDaily(Page<ProductionProductMainDto> page, ProductionProductMainDto productionProductMainDto) {
+        return R.ok(productionProductMainService.listPageProductionProductMainDailyDto(page, productionProductMainDto));
     }
 
     /**
@@ -42,13 +83,41 @@
      * @return
      */
     @PostMapping("addProductMain")
-    public R addProductMain(@RequestBody ProductionProductMainDto productionProductMainDto) {
+    public R<?> addProductMain(@RequestBody ProductionProductMainDto productionProductMainDto) {
         return R.ok(productionProductMainService.addProductMain(productionProductMainDto));
+    }
+
+    /**
+     * 鎵爜鍚庢煡璇㈠綋鍓嶇敤鎴锋槸鍚﹀瓨鍦ㄨ繘琛屼腑鐨勬姤宸�
+     */
+    @ApiOperation("鏌ヨ杩涜涓殑鎶ュ伐(褰撳墠鐧诲綍浜�)")
+    @GetMapping("getRunning")
+    public R<ProductionProductMain> getRunning(Long workOrderId, Long productProcessRouteItemId) {
+        ProductionProductMain productionProductMain = productionProductMainService.getRunning(workOrderId, productProcessRouteItemId);
+        return R.ok(productionProductMain);
+    }
+
+    /**
+     * 姣忔棩鎶ュ伐鏃堕暱姹囨��(褰撳墠鐧诲綍浜�)
+     */
+    @ApiOperation("姣忔棩鎶ュ伐鏃堕暱姹囨��(褰撳墠鐧诲綍浜�)")
+    @GetMapping("dailyDuration")
+    public R<List<ProductionReportDailySummaryDto>> dailyDuration(Long workOrderId, Long productProcessRouteItemId, LocalDate startDate, LocalDate endDate) {
+        return R.ok(productionProductMainService.dailyDuration(workOrderId, productProcessRouteItemId, startDate, endDate));
+    }
+
+    /**
+     * 鏌ヨ鎶ュ伐鐘舵��: 1-寮�濮嬫姤宸� 2-缁撴潫鎶ュ伐
+     */
+    @ApiOperation("鏌ヨ鎶ュ伐鐘舵��(褰撳墠鐧诲綍浜�)")
+    @GetMapping("reportState")
+    public R<ProductionReportStateDto> reportState(Long workOrderId, Long productProcessRouteItemId) {
+        return R.ok(productionProductMainService.reportState(workOrderId, productProcessRouteItemId));
     }
 
     @ApiOperation("鍒犻櫎鎶ュ伐")
     @DeleteMapping("/delete")
-    public R delete(@RequestBody ProductionProductMainDto productionProductMainDto) {
+    public R<?> delete(@RequestBody ProductionProductMainDto productionProductMainDto) {
         return R.ok(productionProductMainService.removeProductMain(productionProductMainDto.getId()));
     }
 
diff --git a/src/main/java/com/ruoyi/production/dto/ProductWorkOrderDto.java b/src/main/java/com/ruoyi/production/dto/ProductWorkOrderDto.java
index 8a88b2b..7de0676 100644
--- a/src/main/java/com/ruoyi/production/dto/ProductWorkOrderDto.java
+++ b/src/main/java/com/ruoyi/production/dto/ProductWorkOrderDto.java
@@ -2,6 +2,7 @@
 
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.ruoyi.production.pojo.ProductWorkOrder;
+import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
@@ -10,6 +11,7 @@
 
 @EqualsAndHashCode(callSuper = true)
 @Data
+@ApiModel(value = "ProductWorkOrderDto", description = "浜у搧宸ュ崟鍒嗛〉/鎶ュ伐瑙嗚杩斿洖 DTO")
 public class ProductWorkOrderDto extends ProductWorkOrder {
 
     //浜у搧鍚嶇О
@@ -54,4 +56,15 @@
 
     @TableField(exist = false)
     private Long currentUserId;
+
+    /**
+     * 浠婃棩鎶ュ伐鐘舵��(浠� type=2 鏃跺洖濉�)锛�1-鏈紑濮� 2-宸插紑濮�(杩涜涓�) 3-宸茬粨鏉�
+     */
+    @ApiModelProperty("浠婃棩鎶ュ伐鐘舵��(1-鏈紑濮� 2-宸插紑濮� 3-宸茬粨鏉�)")
+    @TableField(exist = false)
+    private Integer todayReportState;
+
+    @ApiModelProperty("鎶ュ伐鏃堕棿鎬诲拰(鍒嗛挓)")
+    @TableField(exist = false)
+    private BigDecimal totalReportDurationMinutes;
 }
diff --git a/src/main/java/com/ruoyi/production/dto/ProductionProductMainDto.java b/src/main/java/com/ruoyi/production/dto/ProductionProductMainDto.java
index 9e5e121..5df78e1 100644
--- a/src/main/java/com/ruoyi/production/dto/ProductionProductMainDto.java
+++ b/src/main/java/com/ruoyi/production/dto/ProductionProductMainDto.java
@@ -4,16 +4,19 @@
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.ruoyi.framework.aspectj.lang.annotation.Excel;
 import com.ruoyi.production.pojo.ProductionProductMain;
+import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
+import lombok.EqualsAndHashCode;
 import org.springframework.format.annotation.DateTimeFormat;
 
 import java.math.BigDecimal;
 import java.time.LocalDate;
-import java.time.LocalDateTime;
 
 @Data
+@EqualsAndHashCode(callSuper = true)
 @ExcelIgnoreUnannotated
+@ApiModel(value = "ProductionProductMainDto", description = "鐢熶骇鎶ュ伐鍙拌处/鏄庣粏 DTO")
 public class ProductionProductMainDto extends ProductionProductMain {
     @ApiModelProperty(value = "宸ュ崟缂栧彿")
     @Excel(name = "宸ュ崟缂栧彿")
@@ -50,8 +53,19 @@
     @Excel(name = "閿�鍞悎鍚屽彿")
     private String salesContractNo;
 
+    @ApiModelProperty(value = "鐢熶骇璁㈠崟鍙�")
+    @Excel(name = "鐢熶骇璁㈠崟鍙�")
+    private String productOrderNpsNo;
+
+    @ApiModelProperty(value = "寮�濮嬫棩鏈�(鏄庣粏鏌ヨ鐢�)")
+    private LocalDate startDate;
+
+    @ApiModelProperty(value = "缁撴潫鏃ユ湡(鏄庣粏鏌ヨ鐢�)")
+    private LocalDate endDate;
+
     @JsonFormat(pattern = "yyyy-MM-dd")
     @DateTimeFormat(pattern = "yyyy-MM-dd")
+    @ApiModelProperty(value = "鏃ユ湡(鏄庣粏/姹囨�讳腑浣跨敤)")
     private LocalDate schedulingDate;
     private String schedulingUserName;
     private String customerName;
@@ -61,5 +75,30 @@
     private BigDecimal workHours;
     private BigDecimal wages;
 
+    @ApiModelProperty(value = "鎶ュ伐鍔ㄤ綔 1-寮�濮� 2-缁撴潫")
+    private Integer actionType;
+
+    @ApiModelProperty(value = "瀹為檯鎶ュ伐鏃堕暱(鍒嗛挓)")
+    @Excel(name = "瀹為檯鎶ュ伐鏃堕暱(鍒嗛挓)")
+    private BigDecimal reportDurationMinutes;
+
+    @ApiModelProperty(value = "椤圭洰鎬诲伐鏃�(灏忔椂)")
+    private BigDecimal projectTotalHours;
+
+    @ApiModelProperty(value = "宸ュ簭鏍囧噯宸ユ椂(灏忔椂)")
+    private BigDecimal processStandardHours;
+
+    @ApiModelProperty(value = "瀹為檯鎶ュ伐宸ユ椂(灏忔椂)")
+    private BigDecimal actualReportHours;
+
+    @ApiModelProperty(value = "姣忔棩浜哄憳宸ユ椂(灏忔椂)")
+    private BigDecimal dailyPersonHours;
+
+    @ApiModelProperty(value = "浜у嚭鎬绘暟閲�")
+    private BigDecimal outputTotalQuantity;
+
+    @ApiModelProperty(value = "鎶ュ簾鎬绘暟閲�")
+    private BigDecimal scrapTotalQuantity;
+
 
 }
diff --git a/src/main/java/com/ruoyi/production/dto/ProductionReportDailySummaryDto.java b/src/main/java/com/ruoyi/production/dto/ProductionReportDailySummaryDto.java
new file mode 100644
index 0000000..1d672c0
--- /dev/null
+++ b/src/main/java/com/ruoyi/production/dto/ProductionReportDailySummaryDto.java
@@ -0,0 +1,19 @@
+package com.ruoyi.production.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+@Data
+@ApiModel(value = "ProductionReportDailySummaryDto", description = "姣忔棩鎶ュ伐鏃堕暱姹囨�昏繑鍥�")
+public class ProductionReportDailySummaryDto {
+
+    @ApiModelProperty("鏃ユ湡")
+    private LocalDate reportDate;
+
+    @ApiModelProperty("鏃堕暱(鍒嗛挓)")
+    private BigDecimal durationMinutes;
+}
diff --git a/src/main/java/com/ruoyi/production/dto/ProductionReportStateDto.java b/src/main/java/com/ruoyi/production/dto/ProductionReportStateDto.java
new file mode 100644
index 0000000..8034b00
--- /dev/null
+++ b/src/main/java/com/ruoyi/production/dto/ProductionReportStateDto.java
@@ -0,0 +1,22 @@
+package com.ruoyi.production.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+@ApiModel("ProductionReportStateDto")
+public class ProductionReportStateDto {
+
+    @ApiModelProperty("鐘舵��: 1-寮�濮嬫姤宸� 2-缁撴潫鎶ュ伐")
+    private Integer state;
+
+    @ApiModelProperty("杩涜涓殑鎶ュ伐ID")
+    private Long runningId;
+
+    @ApiModelProperty("寮�濮嬫椂闂�")
+    private LocalDateTime startTime;
+}
+
diff --git a/src/main/java/com/ruoyi/production/mapper/ProductOrderMapper.java b/src/main/java/com/ruoyi/production/mapper/ProductOrderMapper.java
index 72aa6cd..1569341 100644
--- a/src/main/java/com/ruoyi/production/mapper/ProductOrderMapper.java
+++ b/src/main/java/com/ruoyi/production/mapper/ProductOrderMapper.java
@@ -3,7 +3,6 @@
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.ruoyi.production.dto.ProductBomDto;
 import com.ruoyi.production.dto.ProductOrderDto;
 import com.ruoyi.production.dto.ProductStructureDto;
 import com.ruoyi.production.pojo.ProcessRoute;
@@ -11,12 +10,13 @@
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 
+import java.math.BigDecimal;
 import java.util.List;
 
 @Mapper
 public interface ProductOrderMapper extends BaseMapper<ProductOrder> {
 
-    IPage<ProductOrderDto> pageProductOrder(Page page, @Param("c") ProductOrderDto productOrder);
+    IPage<ProductOrderDto> pageProductOrder(Page<ProductOrderDto> page, @Param("c") ProductOrderDto productOrder);
 
     List<ProcessRoute> listProcessRoute(@Param("productModelId") Long productModelId);
 
@@ -27,4 +27,10 @@
     Integer countCompleted(@Param("startDate") String startDate, @Param("endDate") String endDate);
 
     Integer countPending(@Param("startDate") String startDate, @Param("endDate") String endDate);
+
+    /**
+     * 鍘熷瓙澧炲姞瀹屾垚鏁伴噺锛岄槻姝㈠苟鍙戣秴鍑鸿鍗曟暟閲�
+     * @return 褰卞搷琛屾暟(1 鎴愬姛锛�0 澶辫触)
+     */
+    int addCompleteQtyIfNotExceed(@Param("id") Long id, @Param("delta") BigDecimal delta);
 }
diff --git a/src/main/java/com/ruoyi/production/mapper/ProductWorkOrderMapper.java b/src/main/java/com/ruoyi/production/mapper/ProductWorkOrderMapper.java
index d637f7d..18671fc 100644
--- a/src/main/java/com/ruoyi/production/mapper/ProductWorkOrderMapper.java
+++ b/src/main/java/com/ruoyi/production/mapper/ProductWorkOrderMapper.java
@@ -1,6 +1,5 @@
 package com.ruoyi.production.mapper;
 
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -9,8 +8,8 @@
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 
+import java.math.BigDecimal;
 import java.util.List;
-import java.util.Map;
 
 @Mapper
 public interface ProductWorkOrderMapper extends BaseMapper<ProductWorkOrder> {
@@ -22,4 +21,10 @@
     List<ProductWorkOrderDto> selectWorkOrderStartStats(@Param("startDate") String startDate, @Param("endDate") String endDate);
 
     ProductWorkOrder selectMax(@Param("datePrefix") String datePrefix);
+
+    /**
+     * 鍘熷瓙澧炲姞瀹屾垚鏁伴噺锛岄槻姝㈠苟鍙戣秴鍑鸿鍒掓暟閲�
+     * @return 褰卞搷琛屾暟(1 鎴愬姛锛�0 澶辫触)
+     */
+    int addCompleteQtyIfNotExceed(@Param("id") Long id, @Param("delta") BigDecimal delta);
 }
diff --git a/src/main/java/com/ruoyi/production/mapper/ProductionProductMainMapper.java b/src/main/java/com/ruoyi/production/mapper/ProductionProductMainMapper.java
index e63e032..4ac7d47 100644
--- a/src/main/java/com/ruoyi/production/mapper/ProductionProductMainMapper.java
+++ b/src/main/java/com/ruoyi/production/mapper/ProductionProductMainMapper.java
@@ -16,7 +16,11 @@
 @Mapper
 public interface ProductionProductMainMapper extends BaseMapper<ProductionProductMain> {
 
-    IPage<ProductionProductMainDto> listPageProductionProductMainDto(Page page, @Param("c") ProductionProductMainDto productionProductMainDto);
+    IPage<ProductionProductMainDto> listPageProductionProductMainDto(Page<ProductionProductMainDto> page, @Param("c") ProductionProductMainDto productionProductMainDto);
+
+    IPage<ProductionProductMainDto> listPageProductionProductMainDetailDto(Page<ProductionProductMainDto> page, @Param("c") ProductionProductMainDto productionProductMainDto);
+
+    IPage<ProductionProductMainDto> listPageProductionProductMainDailyDto(Page<ProductionProductMainDto> page, @Param("c") ProductionProductMainDto productionProductMainDto);
 
     /**
      * 鏍规嵁宸ュ崟ID鎵归噺鍒犻櫎鐢熶骇涓昏〃鏁版嵁
@@ -30,7 +34,7 @@
      */
     ProductOrder getOrderByMainId(@Param("productMainId") Long productMainId);
 
-    IPage<ProductionProductMainDto> listProductionDetails(@Param("ew") SalesLedgerProductionAccountingDto salesLedgerProductionAccountingDto, Page page);
+    IPage<ProductionProductMainDto> listProductionDetails(@Param("ew") SalesLedgerProductionAccountingDto salesLedgerProductionAccountingDto, Page<ProductionProductMainDto> page);
 
     ArrayList<Long> listMain(List<Long> idList);
 }
diff --git a/src/main/java/com/ruoyi/production/mapper/ProductionProductReportDailyMapper.java b/src/main/java/com/ruoyi/production/mapper/ProductionProductReportDailyMapper.java
new file mode 100644
index 0000000..614756d
--- /dev/null
+++ b/src/main/java/com/ruoyi/production/mapper/ProductionProductReportDailyMapper.java
@@ -0,0 +1,22 @@
+package com.ruoyi.production.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.production.dto.ProductionReportDailySummaryDto;
+import com.ruoyi.production.pojo.ProductionProductReportDaily;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.time.LocalDate;
+import java.util.List;
+
+@Mapper
+public interface ProductionProductReportDailyMapper extends BaseMapper<ProductionProductReportDaily> {
+
+    List<ProductionReportDailySummaryDto> listDailySummary(
+            @Param("workOrderId") Long workOrderId,
+            @Param("productProcessRouteItemId") Long productProcessRouteItemId,
+            @Param("userId") Long userId,
+            @Param("startDate") LocalDate startDate,
+            @Param("endDate") LocalDate endDate
+    );
+}
diff --git a/src/main/java/com/ruoyi/production/pojo/ProductionProductMain.java b/src/main/java/com/ruoyi/production/pojo/ProductionProductMain.java
index a0301a0..040c57f 100644
--- a/src/main/java/com/ruoyi/production/pojo/ProductionProductMain.java
+++ b/src/main/java/com/ruoyi/production/pojo/ProductionProductMain.java
@@ -7,6 +7,7 @@
 import lombok.Data;
 import org.springframework.format.annotation.DateTimeFormat;
 
+import java.math.BigDecimal;
 import java.time.LocalDateTime;
 
 @Data
@@ -35,6 +36,15 @@
     @ApiModelProperty(value = "鎶ュ伐鐘舵��")
     private Integer status;
 
+    @ApiModelProperty(value = "鎶ュ伐寮�濮嬫椂闂�")
+    private LocalDateTime reportStartTime;
+
+    @ApiModelProperty(value = "鎶ュ伐缁撴潫鏃堕棿")
+    private LocalDateTime reportEndTime;
+
+    @ApiModelProperty(value = "瀹為檯鎶ュ伐鏃堕暱(鍒嗛挓)")
+    private BigDecimal reportDurationMinutes;
+
     @ApiModelProperty(value = "鍒涘缓鏃堕棿")
     @TableField(fill = FieldFill.INSERT)
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
diff --git a/src/main/java/com/ruoyi/production/pojo/ProductionProductReportDaily.java b/src/main/java/com/ruoyi/production/pojo/ProductionProductReportDaily.java
new file mode 100644
index 0000000..518a817
--- /dev/null
+++ b/src/main/java/com/ruoyi/production/pojo/ProductionProductReportDaily.java
@@ -0,0 +1,66 @@
+package com.ruoyi.production.pojo;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+@Data
+@TableName("production_product_report_daily")
+public class ProductionProductReportDaily implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty("鎶ュ伐涓昏〃ID")
+    private Long productMainId;
+
+    @ApiModelProperty("宸ュ崟ID")
+    private Long workOrderId;
+
+    @ApiModelProperty("宸ヨ壓璺嚎椤圭洰ID")
+    private Long productProcessRouteItemId;
+
+    @ApiModelProperty("鎶ュ伐浜篒D")
+    private Long userId;
+
+    @ApiModelProperty("鎶ュ伐鏃ユ湡(鎸夊ぉ)")
+    private LocalDate reportDate;
+
+    @ApiModelProperty("褰撴棩寮�濮嬫椂闂�(鐗囨)")
+    private LocalDateTime startTime;
+
+    @ApiModelProperty("褰撴棩缁撴潫鏃堕棿(鐗囨)")
+    private LocalDateTime endTime;
+
+    @ApiModelProperty("褰撴棩鏃堕暱(鍒嗛挓)")
+    private BigDecimal durationMinutes;
+
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Integer createUser;
+
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Integer updateUser;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Long tenantId;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Long deptId;
+}
diff --git a/src/main/java/com/ruoyi/production/service/ProductionProductMainService.java b/src/main/java/com/ruoyi/production/service/ProductionProductMainService.java
index 72d868c..f0124f2 100644
--- a/src/main/java/com/ruoyi/production/service/ProductionProductMainService.java
+++ b/src/main/java/com/ruoyi/production/service/ProductionProductMainService.java
@@ -4,16 +4,39 @@
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.ruoyi.production.dto.ProductionProductMainDto;
+import com.ruoyi.production.dto.ProductionReportDailySummaryDto;
+import com.ruoyi.production.dto.ProductionReportStateDto;
 import com.ruoyi.production.pojo.ProductionProductMain;
 
+import java.time.LocalDate;
 import java.util.ArrayList;
 import java.util.List;
 
 public interface ProductionProductMainService extends IService<ProductionProductMain> {
-    IPage<ProductionProductMainDto> listPageProductionProductMainDto(Page page, ProductionProductMainDto productionProductMainDto);
+
+    IPage<ProductionProductMainDto> listPageProductionProductMainDto(Page<ProductionProductMainDto> page, ProductionProductMainDto productionProductMainDto);
+
+    IPage<ProductionProductMainDto> listPageProductionProductMainDetailDto(Page<ProductionProductMainDto> page, ProductionProductMainDto productionProductMainDto);
+
+    IPage<ProductionProductMainDto> listPageProductionProductMainDailyDto(Page<ProductionProductMainDto> page, ProductionProductMainDto productionProductMainDto);
 
     Boolean addProductMain(ProductionProductMainDto productionProductMainDto);
 
+    /**
+     * 鏌ヨ褰撳墠鐧诲綍浜鸿繘琛屼腑鐨勬姤宸�(鍚屽伐鍗曞悓宸ュ簭)
+     */
+    ProductionProductMain getRunning(Long workOrderId, Long productProcessRouteItemId);
+
+    /**
+     * 姣忔棩鎶ュ伐鏃堕暱姹囨��(褰撳墠鐧诲綍浜�)
+     */
+    List<ProductionReportDailySummaryDto> dailyDuration(Long workOrderId, Long productProcessRouteItemId, LocalDate startDate, LocalDate endDate);
+
+    /**
+     * 鏌ヨ鎶ュ伐鐘舵��(褰撳墠鐧诲綍浜�)
+     */
+    ProductionReportStateDto reportState(Long workOrderId, Long productProcessRouteItemId);
+
     Boolean removeProductMain(Long id);
 
     ArrayList<Long> listMain(List<Long> idList);
diff --git a/src/main/java/com/ruoyi/production/service/impl/ProductWorkOrderServiceImpl.java b/src/main/java/com/ruoyi/production/service/impl/ProductWorkOrderServiceImpl.java
index a137d8d..747a0bd 100644
--- a/src/main/java/com/ruoyi/production/service/impl/ProductWorkOrderServiceImpl.java
+++ b/src/main/java/com/ruoyi/production/service/impl/ProductWorkOrderServiceImpl.java
@@ -13,9 +13,13 @@
 import com.ruoyi.common.utils.MatrixToImageWriter;
 import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.production.dto.ProductWorkOrderDto;
+import com.ruoyi.production.mapper.ProductionProductMainMapper;
+import com.ruoyi.production.mapper.ProductionProductReportDailyMapper;
 import com.ruoyi.production.mapper.ProductWorkOrderFileMapper;
 import com.ruoyi.production.mapper.ProductWorkOrderMapper;
 import com.ruoyi.production.mapper.ProductWorkOrderRapporteurMapper;
+import com.ruoyi.production.pojo.ProductionProductMain;
+import com.ruoyi.production.pojo.ProductionProductReportDaily;
 import com.ruoyi.production.pojo.ProductWorkOrder;
 import com.ruoyi.production.pojo.ProductWorkOrderFile;
 import com.ruoyi.production.pojo.ProductWorkOrderRapporteur;
@@ -30,7 +34,9 @@
 import javax.servlet.http.HttpServletResponse;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.math.BigDecimal;
 import java.net.URLEncoder;
+import java.time.LocalDate;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -39,6 +45,7 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.stream.Collectors;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 
 @Service
 @Transactional(rollbackFor = Exception.class)
@@ -52,14 +59,21 @@
     private ProductWorkOrderRapporteurMapper productWorkOrderRapporteurMapper;
     @Autowired
     private SysUserMapper sysUserMapper;
+    @Autowired
+    private ProductionProductMainMapper productionProductMainMapper;
+    @Autowired
+    private ProductionProductReportDailyMapper productionProductReportDailyMapper;
 
     @Value("${file.temp-dir}")
     private String tempDir;
 
     @Override
     public IPage<ProductWorkOrderDto> listPage(Page<ProductWorkOrderDto> page, ProductWorkOrderDto productWorkOrder) {
-        if (productWorkOrder != null && Integer.valueOf(2).equals(productWorkOrder.getType())) {
-            productWorkOrder.setCurrentUserId(SecurityUtils.getUserId());
+        boolean reportView = productWorkOrder != null && Integer.valueOf(2).equals(productWorkOrder.getType());
+        Long currentUserId = null;
+        if (reportView) {
+            currentUserId = SecurityUtils.getUserId();
+            productWorkOrder.setCurrentUserId(currentUserId);
         }
         IPage<ProductWorkOrderDto> pageData = productWorkOrdermapper.pageProductWorkOrder(page, productWorkOrder);
         List<ProductWorkOrderDto> records = pageData.getRecords();
@@ -92,6 +106,81 @@
             List<Long> userIds = rapporteurMap.get(item.getId());
             item.setReportWorkersId(userIds == null ? new Long[0] : userIds.toArray(new Long[0]));
         });
+
+        // type=2 鏃讹細鍥炲~鈥滃綋鍓嶆姤宸ヤ汉浠婃棩鐘舵�佲��
+        if (reportView && currentUserId != null) {
+            // 1) 杩涜涓�(status=0)鐨勬姤宸�
+            List<ProductionProductMain> runningList = productionProductMainMapper.selectList(
+                    Wrappers.<ProductionProductMain>lambdaQuery()
+                            .in(ProductionProductMain::getWorkOrderId, workOrderIds)
+                            .eq(ProductionProductMain::getUserId, currentUserId)
+                            .eq(ProductionProductMain::getStatus, 0)
+            );
+            final java.util.Set<Long> runningWorkOrders = CollectionUtils.isNotEmpty(runningList)
+                    ? runningList.stream().map(ProductionProductMain::getWorkOrderId).filter(Objects::nonNull).collect(Collectors.toSet())
+                    : java.util.Collections.emptySet();
+
+            // 2) 浠婃棩宸茬粨鏉燂紙浠婃棩鏈� daily 鏄庣粏锛�
+            LocalDate today = LocalDate.now();
+            List<ProductionProductReportDaily> todayDailyList = productionProductReportDailyMapper.selectList(
+                    Wrappers.<ProductionProductReportDaily>lambdaQuery()
+                            .in(ProductionProductReportDaily::getWorkOrderId, workOrderIds)
+                            .eq(ProductionProductReportDaily::getUserId, currentUserId)
+                            .eq(ProductionProductReportDaily::getReportDate, today)
+            );
+            final java.util.Set<Long> endedTodayWorkOrders = CollectionUtils.isNotEmpty(todayDailyList)
+                    ? todayDailyList.stream().map(ProductionProductReportDaily::getWorkOrderId).filter(Objects::nonNull).collect(Collectors.toSet())
+                    : java.util.Collections.emptySet();
+
+            records.forEach(item -> {
+                Long woId = item.getId();
+                if (woId == null) {
+                    item.setTodayReportState(1);
+                    return;
+                }
+                if (runningWorkOrders.contains(woId)) {
+                    item.setTodayReportState(2);
+                    return;
+                }
+                if (endedTodayWorkOrders.contains(woId)) {
+                    item.setTodayReportState(3);
+                    return;
+                }
+                item.setTodayReportState(1);
+            });
+        }
+
+        // 鍥炲~鎶ュ伐鏃堕棿鎬诲拰(鍒嗛挓): type=2 鎸夊綋鍓嶇櫥褰曚汉姹囨�伙紝鍏朵粬鎸夊伐鍗曞叏鍛樻眹鎬�
+        QueryWrapper<ProductionProductReportDaily> totalDurationQw = new QueryWrapper<>();
+        totalDurationQw.select("work_order_id as workOrderId", "sum(duration_minutes) as totalMinutes")
+                .in("work_order_id", workOrderIds)
+                .groupBy("work_order_id");
+        if (reportView && currentUserId != null) {
+            totalDurationQw.eq("user_id", currentUserId);
+        }
+        List<Map<String, Object>> durationRows = productionProductReportDailyMapper.selectMaps(totalDurationQw);
+        Map<Long, BigDecimal> durationMap = new HashMap<>();
+        if (CollectionUtils.isNotEmpty(durationRows)) {
+            for (Map<String, Object> row : durationRows) {
+                Object workOrderObj = row.get("workOrderId");
+                if (workOrderObj == null) {
+                    workOrderObj = row.get("work_order_id");
+                }
+                Object totalObj = row.get("totalMinutes");
+                if (totalObj == null) {
+                    totalObj = row.get("total_minutes");
+                }
+                if (workOrderObj == null || totalObj == null) {
+                    continue;
+                }
+                Long woId = workOrderObj instanceof Number ? ((Number) workOrderObj).longValue() : Long.valueOf(workOrderObj.toString());
+                BigDecimal totalMinutes = totalObj instanceof BigDecimal
+                        ? (BigDecimal) totalObj
+                        : new BigDecimal(totalObj.toString());
+                durationMap.put(woId, totalMinutes);
+            }
+        }
+        records.forEach(item -> item.setTotalReportDurationMinutes(durationMap.getOrDefault(item.getId(), BigDecimal.ZERO)));
         return pageData;
     }
 
@@ -118,9 +207,10 @@
             return rows;
         }
 
-        List<Long> existUserIds = sysUserMapper.selectUserByIds(candidateUserIds).stream()
+        List<Long> existUserIds = sysUserMapper.selectUsersByIds(candidateUserIds).stream()
                 .map(SysUser::getUserId)
                 .filter(Objects::nonNull)
+                .distinct()
                 .collect(Collectors.toList());
         if (existUserIds.size() != candidateUserIds.size()) {
             List<Long> invalidUserIds = candidateUserIds.stream()
diff --git a/src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java b/src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java
index 7ae9771..b21c4fd 100644
--- a/src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java
+++ b/src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java
@@ -15,10 +15,15 @@
 import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum;
 import com.ruoyi.common.enums.StockInUnQualifiedRecordTypeEnum;
 import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.common.utils.bean.BeanUtils;
 import com.ruoyi.procurementrecord.utils.StockUtils;
-import com.ruoyi.production.dto.ProductStructureDto;
 import com.ruoyi.production.dto.ProductionProductMainDto;
+import com.ruoyi.production.dto.ProductionReportDailySummaryDto;
+import com.ruoyi.production.dto.ProductionReportStateDto;
+import com.ruoyi.production.enums.ProductProcessEnum;
+import com.ruoyi.production.mapper.ProductionProductReportDailyMapper;
+import com.ruoyi.production.pojo.ProductionProductReportDaily;
 import com.ruoyi.production.mapper.*;
 import com.ruoyi.production.pojo.*;
 import com.ruoyi.production.service.ProductionProductMainService;
@@ -26,13 +31,14 @@
 import com.ruoyi.project.system.mapper.SysUserMapper;
 import com.ruoyi.quality.mapper.*;
 import com.ruoyi.quality.pojo.*;
-import com.ruoyi.quality.service.IQualityInspectService;
 import lombok.AllArgsConstructor;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import com.ruoyi.production.mapper.ProductionProductMainMapper;
 
 import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.Duration;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
@@ -46,8 +52,8 @@
 @Transactional(rollbackFor = Exception.class)
 public class ProductionProductMainServiceImpl extends ServiceImpl<ProductionProductMainMapper, ProductionProductMain> implements ProductionProductMainService {
 
-    private IQualityInspectService qualityInspectService;
     private ProductionProductMainMapper productionProductMainMapper;
+    private ProductionProductReportDailyMapper productionProductReportDailyMapper;
 
 
     private ProductWorkOrderMapper productWorkOrderMapper;
@@ -56,25 +62,16 @@
     private SysUserMapper userMapper;
 
     private ProductionProductOutputMapper productionProductOutputMapper;
-
-
     private ProductModelMapper productModelMapper;
+    private ProductMapper productMapper;
+    private ProductProcessMapper productProcessMapper;
 
     private QualityInspectMapper qualityInspectMapper;
     private QualityUnqualifiedMapper qualityUnqualifiedMapper;
 
-    private ProductProcessMapper productProcessMapper;
-    private ProductProcessRouteMapper productProcessRouteMapper;
-
-    private ProductMapper productMapper;
-
-
+    private QualityInspectParamMapper qualityInspectParamMapper;
     private QualityTestStandardParamMapper qualityTestStandardParamMapper;
     private QualityTestStandardMapper qualityTestStandardMapper;
-
-    private QualityInspectParamMapper qualityInspectParamMapper;
-
-    private ProductStructureMapper productStructureMapper;
 
     private ProductionProductInputMapper productionProductInputMapper;
 
@@ -86,27 +83,162 @@
 
 
     @Override
-    public IPage<ProductionProductMainDto> listPageProductionProductMainDto(Page page, ProductionProductMainDto productionProductMainDto) {
-        return productionProductMainMapper.listPageProductionProductMainDto(page, productionProductMainDto);
+    public IPage<ProductionProductMainDto> listPageProductionProductMainDto(Page<ProductionProductMainDto> page, ProductionProductMainDto productionProductMainDto) {
+        if (productionProductMainDto == null) {
+            productionProductMainDto = new ProductionProductMainDto();
+        }
+//        productionProductMainDto.setUserId(SecurityUtils.getUserId());
+        IPage<ProductionProductMainDto> result = productionProductMainMapper.listPageProductionProductMainDto(page, productionProductMainDto);
+        fillHourDefaults(result.getRecords());
+        return result;
+    }
+
+    @Override
+    public IPage<ProductionProductMainDto> listPageProductionProductMainDetailDto(Page<ProductionProductMainDto> page, ProductionProductMainDto productionProductMainDto) {
+        if (productionProductMainDto == null) {
+            productionProductMainDto = new ProductionProductMainDto();
+        }
+        IPage<ProductionProductMainDto> result = productionProductMainMapper.listPageProductionProductMainDetailDto(page, productionProductMainDto);
+        fillHourDefaults(result.getRecords());
+        return result;
+    }
+
+    @Override
+    public IPage<ProductionProductMainDto> listPageProductionProductMainDailyDto(Page<ProductionProductMainDto> page, ProductionProductMainDto productionProductMainDto) {
+        if (productionProductMainDto == null) {
+            productionProductMainDto = new ProductionProductMainDto();
+        }
+        IPage<ProductionProductMainDto> result = productionProductMainMapper.listPageProductionProductMainDailyDto(page, productionProductMainDto);
+        fillHourDefaults(result.getRecords());
+        return result;
+    }
+
+    private void fillHourDefaults(List<ProductionProductMainDto> records) {
+        if (records == null || records.isEmpty()) {
+            return;
+        }
+        records.forEach(item -> {
+            if (item.getProjectTotalHours() == null) {
+                item.setProjectTotalHours(BigDecimal.ZERO);
+            }
+            if (item.getProcessStandardHours() == null) {
+                item.setProcessStandardHours(BigDecimal.ZERO);
+            }
+            if (item.getActualReportHours() == null) {
+                item.setActualReportHours(BigDecimal.ZERO);
+            }
+            if (item.getDailyPersonHours() == null) {
+                item.setDailyPersonHours(BigDecimal.ZERO);
+            }
+        });
     }
 
     @Override
     public Boolean addProductMain(ProductionProductMainDto dto) {
-        SysUser user = userMapper.selectUserById(dto.getUserId());
-        ProductionProductMain productionProductMain = new ProductionProductMain();
-        //褰撳墠宸ヨ壓璺嚎瀵瑰簲鐨勫伐搴忚鎯�
-        ProductProcessRouteItem productProcessRouteItem = productProcessRouteItemMapper.selectById(dto.getProductProcessRouteItemId());
-        if (productProcessRouteItem == null) {
-            throw new RuntimeException("宸ヨ壓璺嚎椤逛笉瀛樺湪");
+        if (dto.getActionType() == null) {
+            if (dto.getId() != null) {
+                if (dto.getQuantity() == null || dto.getQuantity().compareTo(BigDecimal.ZERO) <= 0) {
+                    throw new ServiceException("缁撴潫鎶ュ伐澶辫触: 鏈鐢熶骇鏁伴噺蹇呴』澶т簬0");
+                }
+                return finishReport(dto);
+            }
+
+            Long workOrderId = dto.getWorkOrderId();
+            Long itemId = dto.getProductProcessRouteItemId();
+            if (workOrderId == null || itemId == null) {
+                throw new ServiceException("宸ュ崟ID鍜屽伐鑹鸿矾绾块」鐩甀D涓嶈兘涓虹┖");
+            }
+            ProductionProductMain running = getRunning(workOrderId, itemId);
+            if (running != null) {
+                if (dto.getQuantity() == null || dto.getQuantity().compareTo(BigDecimal.ZERO) <= 0) {
+                    throw new ServiceException("缁撴潫鎶ュ伐澶辫触: 鏈鐢熶骇鏁伴噺蹇呴』澶т簬0");
+                }
+                dto.setId(running.getId());
+                return finishReport(dto);
+            }
+            return startReport(dto);
         }
-        //褰撳墠鍏蜂綋宸ュ簭
-        ProductProcess productProcess = productProcessMapper.selectById(productProcessRouteItem.getProcessId());
-        //宸ヨ壓璺嚎涓綋鍓嶅伐搴忓搴旂殑浜у嚭瑙勬牸鍨嬪彿
-        ProductModel productModel = productModelMapper.selectById(productProcessRouteItem.getProductModelId());
-        //鏌ヨ璇ョ敓浜ц鍗曞搴旂殑bom
-        ProductProcessRoute productProcessRoute = productProcessRouteMapper.selectById(productProcessRouteItem.getProductRouteId());
-        /*鏂板鎶ュ伐涓昏〃*/
-        //鏌ヨ鏈�澶ф姤宸ョ紪鍙�
+
+        if (dto.getActionType() == 1) {
+            return startReport(dto);
+        }
+        if (dto.getActionType() == 2) {
+            return finishReport(dto);
+        }
+        throw new ServiceException("鏃犳晥鎶ュ伐鍔ㄤ綔: " + dto.getActionType());
+    }
+
+    @Override
+    public ProductionProductMain getRunning(Long workOrderId, Long productProcessRouteItemId) {
+        if (workOrderId == null || productProcessRouteItemId == null) {
+            throw new ServiceException("宸ュ崟ID鍜屽伐鑹鸿矾绾块」鐩甀D涓嶈兘涓虹┖");
+        }
+        Long currentUserId = SecurityUtils.getUserId();
+        return productionProductMainMapper.selectOne(
+                Wrappers.<ProductionProductMain>lambdaQuery()
+                        .eq(ProductionProductMain::getWorkOrderId, workOrderId)
+                        .eq(ProductionProductMain::getProductProcessRouteItemId, productProcessRouteItemId)
+                        .eq(ProductionProductMain::getUserId, currentUserId)
+                        .eq(ProductionProductMain::getStatus, 0)
+                        .orderByDesc(ProductionProductMain::getReportStartTime)
+                        .last("limit 1")
+        );
+    }
+
+    @Override
+    public List<ProductionReportDailySummaryDto> dailyDuration(Long workOrderId, Long productProcessRouteItemId, LocalDate startDate, LocalDate endDate) {
+        Long userId = SecurityUtils.getUserId();
+        return productionProductReportDailyMapper.listDailySummary(
+                workOrderId,
+                productProcessRouteItemId,
+                userId,
+                startDate,
+                endDate
+        );
+    }
+
+    @Override
+    public ProductionReportStateDto reportState(Long workOrderId, Long productProcessRouteItemId) {
+        ProductionProductMain running = getRunning(workOrderId, productProcessRouteItemId);
+        ProductionReportStateDto dto = new ProductionReportStateDto();
+        if (running == null) {
+            dto.setState(1);
+            return dto;
+        }
+        dto.setState(2);
+        dto.setRunningId(running.getId());
+        dto.setStartTime(running.getReportStartTime());
+        return dto;
+    }
+
+    private Boolean startReport(ProductionProductMainDto dto) {
+        if (dto.getWorkOrderId() == null || dto.getProductProcessRouteItemId() == null) {
+            throw new ServiceException("寮�濮嬫姤宸ュけ璐�: 宸ュ崟ID鍜屽伐鑹鸿矾绾块」鐩甀D涓嶈兘涓虹┖");
+        }
+        if (dto.getUserId() == null) {
+            dto.setUserId(SecurityUtils.getUserId());
+        }
+        if (dto.getUserId() == null) {
+            throw new ServiceException("寮�濮嬫姤宸ュけ璐�: 鏃犳硶鑾峰彇褰撳墠鐧诲綍浜�");
+        }
+
+        QueryWrapper<ProductionProductMain> runningWrapper = new QueryWrapper<>();
+        runningWrapper.eq("work_order_id", dto.getWorkOrderId())
+                .eq("product_process_route_item_id", dto.getProductProcessRouteItemId())
+                .eq("user_id", dto.getUserId())
+                .eq("status", 0);
+        Long runningCount = productionProductMainMapper.selectCount(runningWrapper);
+        if (runningCount != null && runningCount > 0) {
+            // 宸叉湁杩涜涓殑鎶ュ伐鏃讹紝涓嶅啀鏂板缓锛岀户缁部鐢ㄥ埌缁撴潫鎶ュ伐
+            return true;
+        }
+
+        SysUser user = userMapper.selectUserById(dto.getUserId());
+        if (user == null) {
+            throw new ServiceException("鎶ュ伐浜轰笉瀛樺湪");
+        }
+
+        ProductionProductMain productionProductMain = new ProductionProductMain();
         String datePrefix = "BG" + LocalDate.now().format(DateTimeFormatter.ofPattern("yyMMdd"));
         QueryWrapper<ProductionProductMain> queryWrapper = new QueryWrapper<>();
         queryWrapper.select("MAX(product_no) as maxNo")
@@ -134,110 +266,139 @@
         String productNo = String.format("%s%03d", datePrefix, sequenceNumber);
         productionProductMain.setProductNo(productNo);
         productionProductMain.setUserId(dto.getUserId());
-        productionProductMain.setUserName(dto.getUserName());
+        productionProductMain.setUserName(user.getNickName());
         productionProductMain.setProductProcessRouteItemId(dto.getProductProcessRouteItemId());
         productionProductMain.setWorkOrderId(dto.getWorkOrderId());
         productionProductMain.setStatus(0);
+        productionProductMain.setReportStartTime(LocalDateTime.now());
         productionProductMainMapper.insert(productionProductMain);
-        /*鏂板鎶ュ伐鎶曞叆琛�*/
-        List<ProductStructureDto> productStructureDtos = productStructureMapper.listBybomAndProcess(productProcessRoute.getBomId(), productProcess.getId());
-        if (productStructureDtos.size() == 0) {
-            //濡傛灉璇ュ伐搴忔病鏈変骇鍝佺粨鏋勭殑鎶曞叆鍝�,閭h繖涓姇鍏ュ搧鍜屼骇鍑哄搧鏄悓涓�涓�
-            ProductStructureDto productStructureDto = new ProductStructureDto();
-            productStructureDto.setProductModelId(productProcessRouteItem.getProductModelId());
-            productStructureDto.setUnitQuantity(BigDecimal.ONE);
-            productStructureDtos.add(productStructureDto);
-        }
-        for (ProductStructureDto productStructureDto : productStructureDtos) {
+        return true;
+    }
 
-            ProductionProductInput productionProductInput = new ProductionProductInput();
-            productionProductInput.setProductModelId(productStructureDto.getProductModelId());
-            productionProductInput.setQuantity(productStructureDto.getUnitQuantity().multiply(dto.getQuantity()));
-            productionProductInput.setProductMainId(productionProductMain.getId());
-            productionProductInputMapper.insert(productionProductInput);
-            stockUtils.substractStock(productStructureDto.getProductModelId(), productionProductInput.getQuantity(), StockOutQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_OUT.getCode(), productionProductMain.getId());
-
+    private Boolean finishReport(ProductionProductMainDto dto) {
+        if (dto.getId() == null) {
+            throw new ServiceException("缁撴潫鎶ュ伐澶辫触: 鎶ュ伐ID涓嶈兘涓虹┖");
         }
+        if (dto.getQuantity() == null || dto.getQuantity().compareTo(BigDecimal.ZERO) <= 0) {
+            throw new ServiceException("缁撴潫鎶ュ伐澶辫触: 鏈鐢熶骇鏁伴噺蹇呴』澶т簬0");
+        }
+        ProductionProductMain productionProductMain = productionProductMainMapper.selectById(dto.getId());
+        if (productionProductMain == null) {
+            throw new ServiceException("缁撴潫鎶ュ伐澶辫触: 鎶ュ伐璁板綍涓嶅瓨鍦�");
+        }
+        if (productionProductMain.getStatus() != null && productionProductMain.getStatus() == 1) {
+            throw new ServiceException("璇ユ姤宸ュ凡缁撴潫锛岃鍕块噸澶嶆彁浜�");
+        }
+        if (productionProductMain.getReportStartTime() == null) {
+            throw new ServiceException("璇ユ姤宸ョ己灏戝紑濮嬫椂闂达紝鏃犳硶缁撴潫");
+        }
+
+        LocalDateTime endTime = LocalDateTime.now();
+        long durationSeconds = Duration.between(productionProductMain.getReportStartTime(), endTime).getSeconds();
+        BigDecimal durationMinutes = secondsToMinutesExact(durationSeconds);
+        int finishRows = productionProductMainMapper.update(
+                null,
+                Wrappers.<ProductionProductMain>lambdaUpdate()
+                        .set(ProductionProductMain::getReportEndTime, endTime)
+                        .set(ProductionProductMain::getReportDurationMinutes, durationMinutes)
+                        .set(ProductionProductMain::getStatus, 1)
+                        .eq(ProductionProductMain::getId, productionProductMain.getId())
+                        .eq(ProductionProductMain::getStatus, 0)
+        );
+        if (finishRows <= 0) {
+            throw new ServiceException("璇ユ姤宸ュ凡缁撴潫锛岃鍕块噸澶嶆彁浜�");
+        }
+        productionProductMain.setReportEndTime(endTime);
+        productionProductMain.setReportDurationMinutes(durationMinutes);
+        productionProductMain.setStatus(1);
+
+        // 鍐欏叆鈥滄瘡鏃ユ椂闀挎槑缁嗏�濓紝璺ㄥぉ鑷姩鎷嗗垎
+        saveDailyDurations(productionProductMain, productionProductMain.getReportStartTime(), endTime);
+
+        dto.setWorkOrderId(productionProductMain.getWorkOrderId());
+        dto.setProductProcessRouteItemId(productionProductMain.getProductProcessRouteItemId());
+        if (dto.getUserId() == null) {
+            dto.setUserId(productionProductMain.getUserId());
+        }
+        if (dto.getUserName() == null) {
+            dto.setUserName(productionProductMain.getUserName());
+        }
+        if (dto.getScrapQty() == null) {
+            dto.setScrapQty(BigDecimal.ZERO);
+        }
+
+        SysUser user = userMapper.selectUserById(dto.getUserId());
+        if (user == null) {
+            throw new ServiceException("鎶ュ伐浜轰笉瀛樺湪");
+        }
+
+        //  浣跨敤宸ュ崟鍏宠仈鐨勭敓浜ц鍗曚骇鍝佸瀷鍙�
+        ProductWorkOrder productWorkOrder = productWorkOrderMapper.selectById(dto.getWorkOrderId());
+        if (productWorkOrder == null) {
+            throw new ServiceException("缁撴潫鎶ュ伐澶辫触: 宸ュ崟涓嶅瓨鍦�");
+        }
+        ProductOrder productOrder = productOrderMapper.selectById(productWorkOrder.getProductOrderId());
+        if (productOrder == null) {
+            throw new ServiceException("缁撴潫鎶ュ伐澶辫触: 鍏宠仈鐢熶骇璁㈠崟涓嶅瓨鍦�");
+        }
+        Long outputProductModelId = productOrder.getProductModelId();
+        if (outputProductModelId == null) {
+            throw new ServiceException("缁撴潫鎶ュ伐澶辫触: 鐢熶骇璁㈠崟鏈厤缃骇鍝佸瀷鍙�");
+        }
+
+        /*鏂板鎶ュ伐鎶曞叆琛�(鏃燘OM鍦烘櫙: 鎶曞叆=浜у嚭鍨嬪彿, 鏁伴噺鎸夋湰娆℃姤宸ユ暟閲�)*/
+        ProductionProductInput productionProductInput = new ProductionProductInput();
+        productionProductInput.setProductModelId(outputProductModelId);
+        productionProductInput.setQuantity(dto.getQuantity());
+        productionProductInput.setProductMainId(productionProductMain.getId());
+        productionProductInputMapper.insert(productionProductInput);
+        stockUtils.substractStock(outputProductModelId, dto.getQuantity(), StockOutQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_OUT.getCode(), productionProductMain.getId());
+
         /*鏂板鎶ュ伐浜у嚭琛�*/
         ProductionProductOutput productionProductOutput = new ProductionProductOutput();
         productionProductOutput.setProductMainId(productionProductMain.getId());
-        productionProductOutput.setProductModelId(productProcessRouteItem.getProductModelId());
+        productionProductOutput.setProductModelId(outputProductModelId);
         productionProductOutput.setQuantity(dto.getQuantity() != null ? dto.getQuantity() : BigDecimal.ZERO);
         productionProductOutput.setScrapQty(dto.getScrapQty() != null ? dto.getScrapQty() : BigDecimal.ZERO);
         productionProductOutputMapper.insert(productionProductOutput);
         //鍚堟牸鏁伴噺=鎶ュ伐鏁伴噺-鎶ュ簾鏁伴噺
         BigDecimal productQty = productionProductOutput.getQuantity().subtract(productionProductOutput.getScrapQty());
+        // 鏄惁闇�瑕佽川妫�锛氭寜 product_process.is_quality 鍒ゆ柇锛�1-闇�瑕侊紝0-涓嶉渶瑕侊級
+        boolean needQuality = isNeedQualityByWorkOrder(productWorkOrder);
+
         //鍙湁鍚堟牸鏁伴噺>0鎵嶈兘澧炲姞鐩稿簲鏁版嵁
         if (productQty.compareTo(BigDecimal.ZERO) > 0) {
-            /*鏂板璐ㄦ*/
-            List<ProductProcessRouteItem> productProcessRouteItems = productProcessRouteItemMapper.selectList(Wrappers.<ProductProcessRouteItem>lambdaQuery().eq(ProductProcessRouteItem::getProductRouteId, productProcessRouteItem.getProductRouteId()));
-            if (productProcessRouteItem.getIsQuality()) {
-                //瀵瑰簲鐨勮繃绋嬫鎴栬�呭嚭鍘傛
-                int inspectType = 1;
-                String process = productProcess.getName();//宸ュ簭
-                if (productProcessRouteItem.getDragSort() == productProcessRouteItems.size()) {
-                    //鏈�鍚庝竴閬撳伐搴忕敓鎴愬嚭鍘傛
-                    inspectType = 2;
-                    process = null;
-                }
-                Product product = productMapper.selectById(productModel.getProductId());
-                QualityInspect qualityInspect = new QualityInspect();
-                qualityInspect.setProductId(product.getId());
-                qualityInspect.setProductName(product.getProductName());
-                qualityInspect.setModel(productModel.getModel());
-                qualityInspect.setUnit(productModel.getUnit());
-                qualityInspect.setQuantity(productQty);
-                qualityInspect.setProcess(process);
-                qualityInspect.setInspectState(0);
-                qualityInspect.setInspectType(inspectType);
-                qualityInspect.setProductMainId(productionProductMain.getId());
-                qualityInspect.setProductModelId(productModel.getId());
-                qualityInspectMapper.insert(qualityInspect);
-                List<QualityTestStandard> qualityTestStandard = qualityTestStandardMapper.getQualityTestStandardByProductId(product.getId(), inspectType, process);
-                if (qualityTestStandard.size() > 0) {
-                    qualityInspect.setTestStandardId(qualityTestStandard.get(0).getId());
-                    qualityInspectMapper.updateById(qualityInspect);
-                    qualityTestStandardParamMapper.selectList(Wrappers.<QualityTestStandardParam>lambdaQuery()
-                                    .eq(QualityTestStandardParam::getTestStandardId, qualityTestStandard.get(0).getId()))//榛樿鑾峰彇鏈�鏂扮殑
-                            .forEach(qualityTestStandardParam -> {
-                                QualityInspectParam param = new QualityInspectParam();
-                                BeanUtils.copyProperties(qualityTestStandardParam, param);
-                                param.setId(null);
-                                param.setInspectId(qualityInspect.getId());
-                                qualityInspectParamMapper.insert(param);
-                            });
-                }
-            }else {
-                //鐩存帴鍏ュ簱
-                stockUtils.addStock(productProcessRouteItem.getProductModelId(), productQty, StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode(), productionProductMain.getId());
+            // 闇�瑕佽川妫�鏃舵墠鏂板杩囩▼妫�/鍑哄巶妫�
+            if (needQuality) {
+                createQualityInspect(productionProductMain.getId(), outputProductModelId, productQty, 1, "鐢熶骇鎶ュ伐");
             }
+            stockUtils.addStock(outputProductModelId, productQty, StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode(), productionProductMain.getId());
             /*鏇存柊宸ュ崟鍜岀敓浜ц鍗�*/
-            ProductWorkOrder productWorkOrder = productWorkOrderMapper.selectById(dto.getWorkOrderId());
-            productWorkOrder.setCompleteQuantity(productWorkOrder.getCompleteQuantity().add(productQty));
-            if (ObjectUtils.isNull(productWorkOrder.getActualStartTime())) {
-                productWorkOrder.setActualStartTime(LocalDateTime.now());//瀹為檯寮�濮嬫椂闂�
+            int woRows = productWorkOrderMapper.addCompleteQtyIfNotExceed(dto.getWorkOrderId(), productQty);
+            if (woRows <= 0) {
+                ProductWorkOrder current = productWorkOrderMapper.selectById(dto.getWorkOrderId());
+                throw new ServiceException("鏈鐢熶骇鏁伴噺涓嶈兘澶т簬鍓╀綑鏁伴噺锛屽墿浣欐暟閲�: "
+                        + (current == null ? "0" : current.getPlanQuantity().subtract(current.getCompleteQuantity())));
             }
-            if (productWorkOrder.getCompleteQuantity().compareTo(productWorkOrder.getPlanQuantity()) == 0) {
-                productWorkOrder.setActualEndTime(LocalDateTime.now());//瀹為檯缁撴潫鏃堕棿
+
+            // 鏃犲伐鑹鸿矾绾垮満鏅細鎶ュ伐鍗宠鍏ョ敓浜ц鍗曞畬鎴愭暟閲�
+            int poRows = productOrderMapper.addCompleteQtyIfNotExceed(productOrder.getId(), productQty);
+            if (poRows <= 0) {
+                ProductOrder currentOrder = productOrderMapper.selectById(productOrder.getId());
+                throw new ServiceException("鏈鐢熶骇鏁伴噺涓嶈兘澶т簬璁㈠崟鍓╀綑鏁伴噺锛屽墿浣欐暟閲�: "
+                        + (currentOrder == null ? "0" : currentOrder.getQuantity().subtract(currentOrder.getCompleteQuantity())));
             }
-            productWorkOrderMapper.updateById(productWorkOrder);
-            //鐢熶骇璁㈠崟
-            ProductOrder productOrder = productOrderMapper.selectById(productWorkOrder.getProductOrderId());
-            if (ObjectUtils.isNull(productOrder.getStartTime())) {
-                productOrder.setStartTime(LocalDateTime.now());//寮�濮嬫椂闂�
+            ProductOrder latestOrder = productOrderMapper.selectById(productOrder.getId());
+            if (needQuality
+                    && latestOrder != null
+                    && latestOrder.getCompleteQuantity() != null
+                    && latestOrder.getQuantity() != null
+                    && latestOrder.getCompleteQuantity().compareTo(latestOrder.getQuantity()) >= 0) {
+                // 璁㈠崟瀹屾垚鏃舵柊澧炲嚭鍘傛
+                createQualityInspect(productionProductMain.getId(), outputProductModelId, productQty, 2, null);
             }
-            if (productProcessRouteItem.getDragSort() == productProcessRouteItems.size()) {
-                //濡傛灉鏄渶鍚庝竴閬撳伐搴忔姤宸ヤ箣鍚庣敓浜ц鍗曞畬鎴愭暟閲�+
-                productOrder.setCompleteQuantity(productOrder.getCompleteQuantity().add(productQty));
-                if (productOrder.getCompleteQuantity().compareTo(productOrder.getQuantity()) == 0) {
-                    productOrder.setEndTime(LocalDateTime.now());//缁撴潫鏃堕棿
-                }
-            }
-            productOrderMapper.updateById(productOrder);
             /*娣诲姞鐢熶骇鏍哥畻        鍖哄垎宸ュ簭鏄浠惰繕鏄鏃�*/
-            BigDecimal workHours = (productProcess.getType() == 1)
-                    ? productProcess.getSalaryQuota().multiply(productQty)
-                    : productProcess.getSalaryQuota();
+            BigDecimal workHours = durationMinutes.divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP);
 
             SalesLedgerProductionAccounting salesLedgerProductionAccounting = SalesLedgerProductionAccounting.builder()
                     .productMainId(productionProductMain.getId())
@@ -245,7 +406,7 @@
                     .schedulingUserName(user.getNickName())
                     .finishedNum(productQty)
                     .workHours(workHours)
-                    .process(productProcess.getName())
+                    .process(resolveProcessTypeName(productWorkOrder))
                     .schedulingDate(LocalDate.now())
                     .tenantId(dto.getTenantId())
                     .build();
@@ -254,27 +415,159 @@
         //濡傛灉鎶ュ簾鏁伴噺>0,闇�瑕佽繘鍏ユ姤搴熺殑搴撳瓨
         if (ObjectUtils.isNotEmpty(dto.getScrapQty())) {
             if (dto.getScrapQty().compareTo(BigDecimal.ZERO) > 0) {
-                stockUtils.addUnStock(productModel.getId(), dto.getScrapQty(), StockInUnQualifiedRecordTypeEnum.PRODUCTION_SCRAP.getCode(), productionProductMain.getId());
+                stockUtils.addUnStock(outputProductModelId, dto.getScrapQty(), StockInUnQualifiedRecordTypeEnum.PRODUCTION_SCRAP.getCode(), productionProductMain.getId());
             }
         }
         return true;
+    }
+
+    /**
+     * 鍒涘缓璐ㄦ鍙婅川妫�鍙傛暟
+     */
+    private void createQualityInspect(Long productMainId, Long productModelId, BigDecimal qty, Integer inspectType, String process) {
+        ProductModel productModel = productModelMapper.selectById(productModelId);
+        if (productModel == null) {
+            return;
+        }
+        Product product = productMapper.selectById(productModel.getProductId());
+        if (product == null) {
+            return;
+        }
+        QualityInspect qualityInspect = new QualityInspect();
+        qualityInspect.setProductId(product.getId());
+        qualityInspect.setProductName(product.getProductName());
+        qualityInspect.setModel(productModel.getModel());
+        qualityInspect.setUnit(productModel.getUnit());
+        qualityInspect.setQuantity(qty);
+        qualityInspect.setProcess(process);
+        qualityInspect.setInspectState(0);
+        qualityInspect.setInspectType(inspectType);
+        qualityInspect.setProductMainId(productMainId);
+        qualityInspect.setProductModelId(productModelId);
+        qualityInspectMapper.insert(qualityInspect);
+
+        List<QualityTestStandard> qualityTestStandard = qualityTestStandardMapper.getQualityTestStandardByProductId(product.getId(), inspectType, process);
+        if (qualityTestStandard.isEmpty()) {
+            return;
+        }
+        qualityInspect.setTestStandardId(qualityTestStandard.get(0).getId());
+        qualityInspectMapper.updateById(qualityInspect);
+        qualityTestStandardParamMapper.selectList(Wrappers.<QualityTestStandardParam>lambdaQuery()
+                        .eq(QualityTestStandardParam::getTestStandardId, qualityTestStandard.get(0).getId()))
+                .forEach(qualityTestStandardParam -> {
+                    QualityInspectParam param = new QualityInspectParam();
+                    BeanUtils.copyProperties(qualityTestStandardParam, param);
+                    param.setId(null);
+                    param.setInspectId(qualityInspect.getId());
+                    qualityInspectParamMapper.insert(param);
+                });
+    }
+
+    /**
+     * 鏄惁闇�瑕佽川妫�锛氫緷鎹伐搴忚〃 is_quality锛�1-闇�瑕侊紝0-涓嶉渶瑕侊級
+     */
+    private boolean isNeedQualityByWorkOrder(ProductWorkOrder workOrder) {
+        if (workOrder == null || workOrder.getProductProcessRouteItemId() == null) {
+            return true;
+        }
+        ProductProcessRouteItem routeItem = productProcessRouteItemMapper.selectById(workOrder.getProductProcessRouteItemId());
+        if (routeItem == null || routeItem.getProcessId() == null) {
+            return true;
+        }
+        ProductProcess process = productProcessMapper.selectById(routeItem.getProcessId());
+        if (process == null || process.getIsQuality() == null) {
+            return true;
+        }
+        return process.getIsQuality();
+    }
+
+    /**
+     * 鑾峰彇宸ュ簭瀵瑰簲鐨勨�滈儴浠剁被鍨嬧�濇枃妗�
+     */
+    private String resolveProcessTypeName(ProductWorkOrder workOrder) {
+        if (workOrder == null || workOrder.getProductProcessRouteItemId() == null) {
+            return "鍏朵粬";
+        }
+        ProductProcessRouteItem routeItem = productProcessRouteItemMapper.selectById(workOrder.getProductProcessRouteItemId());
+        if (routeItem == null || routeItem.getProcessId() == null) {
+            return "鍏朵粬";
+        }
+        ProductProcess process = productProcessMapper.selectById(routeItem.getProcessId());
+        if (process == null || process.getType() == null) {
+            return "鍏朵粬";
+        }
+        for (ProductProcessEnum value : ProductProcessEnum.values()) {
+            if (value.getCode().equals(process.getType())) {
+                return value.getInfo();
+            }
+        }
+        return "鍏朵粬";
+    }
+
+    private void saveDailyDurations(ProductionProductMain main, LocalDateTime start, LocalDateTime end) {
+        if (main == null || start == null || end == null) {
+            return;
+        }
+        if (end.isBefore(start)) {
+            return;
+        }
+
+        // 闃叉閲嶅鍐欙紙渚嬪璇噸澶嶇粨鏉熸椂锛夛紝鍏堝垹鍐嶆彃
+        productionProductReportDailyMapper.delete(
+                Wrappers.<ProductionProductReportDaily>lambdaQuery()
+                        .eq(ProductionProductReportDaily::getProductMainId, main.getId())
+        );
+
+        LocalDateTime cursor = start;
+        while (cursor.isBefore(end)) {
+            LocalDate date = cursor.toLocalDate();
+            LocalDateTime nextDayStart = date.plusDays(1).atStartOfDay();
+            LocalDateTime sliceEnd = end.isBefore(nextDayStart) ? end : nextDayStart;
+
+            long seconds = Duration.between(cursor, sliceEnd).getSeconds();
+            BigDecimal minutes = secondsToMinutesExact(seconds);
+            if (minutes.compareTo(BigDecimal.ZERO) > 0) {
+                ProductionProductReportDaily daily = new ProductionProductReportDaily();
+                daily.setProductMainId(main.getId());
+                daily.setWorkOrderId(main.getWorkOrderId());
+                daily.setProductProcessRouteItemId(main.getProductProcessRouteItemId());
+                daily.setUserId(main.getUserId());
+                daily.setReportDate(date);
+                daily.setStartTime(cursor);
+                daily.setEndTime(sliceEnd);
+                daily.setDurationMinutes(minutes);
+                productionProductReportDailyMapper.insert(daily);
+            }
+            cursor = sliceEnd;
+        }
+    }
+
+    /**
+     * 绉掕浆鍒嗛挓锛氬寘鍚垎绉掑苟鍚戜笂鍙栨暣鍒板垎閽�
+     */
+    private BigDecimal secondsToMinutesExact(long seconds) {
+        if (seconds <= 0L) {
+            return BigDecimal.ZERO;
+        }
+        return BigDecimal.valueOf(seconds).divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP);
     }
 
     @Override
     public Boolean removeProductMain(Long id) {
         //鍒ゆ柇璇ユ潯鎶ュ伐鏄惁涓嶅悎鏍煎鐞�,濡傛灉涓嶅悎鏍煎鐞嗕簡锛屽垯涓嶅厑璁稿垹闄�
         List<QualityInspect> qualityInspects = qualityInspectMapper.selectList(Wrappers.<QualityInspect>lambdaQuery().eq(QualityInspect::getProductMainId, id));
-        if (qualityInspects.size() > 0){
+        if (qualityInspects.size() > 0) {
             List<QualityUnqualified> qualityUnqualifieds = qualityUnqualifiedMapper.selectList(Wrappers.<QualityUnqualified>lambdaQuery()
                     .in(QualityUnqualified::getInspectId, qualityInspects.stream().map(QualityInspect::getId).collect(Collectors.toList())));
-            if (qualityUnqualifieds.size() > 0 && qualityUnqualifieds.get(0).getInspectState()==1) {
+            if (qualityUnqualifieds.size() > 0 && qualityUnqualifieds.get(0).getInspectState() == 1) {
                 throw new ServiceException("璇ユ潯鎶ュ伐宸茬粡涓嶅悎鏍煎鐞嗕簡锛屼笉鍏佽鍒犻櫎");
             }
         }
         ProductionProductMain productionProductMain = productionProductMainMapper.selectById(id);
-        //璇ユ姤宸ュ搴旂殑宸ヨ壓璺嚎璇︽儏
-        ProductProcessRouteItem productProcessRouteItem = productProcessRouteItemMapper.selectById(productionProductMain.getProductProcessRouteItemId());
-        ProductionProductOutput productionProductOutput = productionProductOutputMapper.selectList(Wrappers.<ProductionProductOutput>lambdaQuery().eq(ProductionProductOutput::getProductMainId, productionProductMain.getId())).get(0);
+        List<ProductionProductOutput> outputList = productionProductOutputMapper.selectList(
+                Wrappers.<ProductionProductOutput>lambdaQuery().eq(ProductionProductOutput::getProductMainId, productionProductMain.getId())
+        );
+        ProductionProductOutput productionProductOutput = outputList.isEmpty() ? null : outputList.get(0);
         /*鍒犻櫎鏍哥畻*/
         salesLedgerProductionAccountingMapper.delete(
                 new LambdaQueryWrapper<SalesLedgerProductionAccounting>()
@@ -282,35 +575,31 @@
         );
         /*鏇存柊宸ュ崟鍜岀敓浜ц鍗�*/
         ProductWorkOrder productWorkOrder = productWorkOrderMapper.selectById(productionProductMain.getWorkOrderId());
+        BigDecimal validQuantity = BigDecimal.ZERO;
         if (productWorkOrder != null && productionProductOutput != null) {
             BigDecimal outputQty = productionProductOutput.getQuantity() == null ? BigDecimal.ZERO : productionProductOutput.getQuantity();
             BigDecimal scrapQty = productionProductOutput.getScrapQty() == null ? BigDecimal.ZERO : productionProductOutput.getScrapQty();
             BigDecimal completeQty = productWorkOrder.getCompleteQuantity() == null ? BigDecimal.ZERO : productWorkOrder.getCompleteQuantity();
 
-            BigDecimal validQuantity = outputQty.subtract(scrapQty);
+            validQuantity = outputQty.subtract(scrapQty);
 
             productWorkOrder.setCompleteQuantity(completeQty.subtract(validQuantity));
             productWorkOrder.setActualEndTime(null);
             productWorkOrderMapper.updateById(productWorkOrder);
-        } else {
+        } else if (productWorkOrder == null) {
             throw new ServiceException("鎿嶄綔澶辫触锛氬伐鍗曚俊鎭垨浜у嚭璁板綍涓嶅瓨鍦�");
         }
-        //鍒ゆ柇鏄惁鏄渶鍚庝竴閬撳伐搴�
-        List<ProductProcessRouteItem> productProcessRouteItems = productProcessRouteItemMapper.selectList(Wrappers.<ProductProcessRouteItem>lambdaQuery().eq(ProductProcessRouteItem::getProductRouteId, productProcessRouteItem.getProductRouteId()));
-        if (productProcessRouteItem.getDragSort() != null && productProcessRouteItems != null && productProcessRouteItem.getDragSort() == productProcessRouteItems.size()) {
+        // 鏃犲伐鑹鸿矾绾垮満鏅細鍒犻櫎鎶ュ伐鏃跺彧瑕佹湁鏈夋晥浜у嚭灏辨墸鍑忕敓浜ц鍗曞畬鎴愭暟閲�
+        if (productionProductOutput != null && validQuantity.compareTo(BigDecimal.ZERO) > 0) {
             ProductOrder productOrder = productOrderMapper.selectById(productWorkOrder.getProductOrderId());
-            if (productOrder != null) {
-                BigDecimal orderCompleteQty = productOrder.getCompleteQuantity() == null ? BigDecimal.ZERO : productOrder.getCompleteQuantity();
-                BigDecimal totalQty = productionProductOutput.getQuantity() != null ? productionProductOutput.getQuantity() : BigDecimal.ZERO;
-                BigDecimal scrapQty = productionProductOutput.getScrapQty() != null ? productionProductOutput.getScrapQty() : BigDecimal.ZERO;
-                BigDecimal actualQualifiedQty = totalQty.subtract(scrapQty);
-                BigDecimal newCompleteQty = orderCompleteQty.subtract(actualQualifiedQty);
-                productOrder.setCompleteQuantity(newCompleteQty.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : newCompleteQty);
-                productOrder.setEndTime(null);
-                productOrderMapper.updateById(productOrder);
-            } else {
+            if (productOrder == null) {
                 throw new ServiceException("鍏宠仈鐨勭敓浜ц鍗曚笉瀛樺湪");
             }
+            BigDecimal orderCompleteQty = productOrder.getCompleteQuantity() == null ? BigDecimal.ZERO : productOrder.getCompleteQuantity();
+            BigDecimal newCompleteQty = orderCompleteQty.subtract(validQuantity);
+            productOrder.setCompleteQuantity(newCompleteQty.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : newCompleteQty);
+            productOrder.setEndTime(null);
+            productOrderMapper.updateById(productOrder);
         }
         //鍒犻櫎璐ㄦ
         qualityInspectMapper.selectList(
@@ -321,7 +610,7 @@
                     new LambdaQueryWrapper<QualityInspectParam>()
                             .eq(QualityInspectParam::getInspectId, q.getId()));
             qualityInspectMapper.deleteById(q.getId());
-                stockUtils.deleteStockInRecord(q.getId(), StockInQualifiedRecordTypeEnum.QUALITYINSPECT_STOCK_IN.getCode());
+            stockUtils.deleteStockInRecord(q.getId(), StockInQualifiedRecordTypeEnum.QUALITYINSPECT_STOCK_IN.getCode());
         });
 
         // 鍒犻櫎浜у嚭璁板綍
diff --git a/src/main/resources/mapper/production/ProductOrderMapper.xml b/src/main/resources/mapper/production/ProductOrderMapper.xml
index 6b993bf..f8d10e0 100644
--- a/src/main/resources/mapper/production/ProductOrderMapper.xml
+++ b/src/main/resources/mapper/production/ProductOrderMapper.xml
@@ -110,4 +110,14 @@
         WHERE create_time &gt;= #{startDate} AND create_time &lt;= #{endDate}
           AND complete_quantity &lt; quantity
     </select>
+
+    <update id="addCompleteQtyIfNotExceed">
+        update product_order
+        set
+            complete_quantity = complete_quantity + #{delta},
+            start_time = ifnull(start_time, now()),
+            end_time = case when (complete_quantity + #{delta}) = quantity then now() else end_time end
+        where id = #{id}
+          and (complete_quantity + #{delta}) <![CDATA[ <= ]]> quantity
+    </update>
 </mapper>
diff --git a/src/main/resources/mapper/production/ProductWorkOrderMapper.xml b/src/main/resources/mapper/production/ProductWorkOrderMapper.xml
index e132174..2c47577 100644
--- a/src/main/resources/mapper/production/ProductWorkOrderMapper.xml
+++ b/src/main/resources/mapper/production/ProductWorkOrderMapper.xml
@@ -21,7 +21,15 @@
     <select id="pageProductWorkOrder" resultType="com.ruoyi.production.dto.ProductWorkOrderDto">
         SELECT
         pwo.*,
-        pp.NAME as processName,
+        CASE pp.type
+            WHEN 1 THEN '鍔犲伐'
+            WHEN 2 THEN '鍒澘鍐疯姱鍒朵綔'
+            WHEN 3 THEN '绠¤矾缁勫'
+            WHEN 4 THEN '缃愪綋杩炴帴鍙婅皟璇�'
+            WHEN 5 THEN '娴嬭瘯鎵撳帇'
+            WHEN 6 THEN '鍏朵粬'
+            ELSE pp.NAME
+        END as processName,
         pm.model,
         pm.unit,
         p.product_name AS productName,
@@ -62,7 +70,15 @@
     <select id="getProductWorkOrderFlowCard" resultType="com.ruoyi.production.dto.ProductWorkOrderDto">
         SELECT
         pwo.*,
-        pp.NAME as processName,
+        CASE pp.type
+            WHEN 1 THEN '鍔犲伐'
+            WHEN 2 THEN '鍒澘鍐疯姱鍒朵綔'
+            WHEN 3 THEN '绠¤矾缁勫'
+            WHEN 4 THEN '缃愪綋杩炴帴鍙婅皟璇�'
+            WHEN 5 THEN '娴嬭瘯鎵撳帇'
+            WHEN 6 THEN '鍏朵粬'
+            ELSE pp.NAME
+        END as processName,
         pm.model,
         pm.unit,
         p.product_name AS productName,
@@ -105,4 +121,14 @@
         limit 1
         ;
     </select>
+
+    <update id="addCompleteQtyIfNotExceed">
+        update product_work_order
+        set
+            complete_quantity = complete_quantity + #{delta},
+            actual_start_time = ifnull(actual_start_time, now()),
+            actual_end_time = case when (complete_quantity + #{delta}) = plan_quantity then now() else actual_end_time end
+        where id = #{id}
+          and (complete_quantity + #{delta}) <![CDATA[ <= ]]> plan_quantity
+    </update>
 </mapper>
diff --git a/src/main/resources/mapper/production/ProductionProductMainMapper.xml b/src/main/resources/mapper/production/ProductionProductMainMapper.xml
index a122e90..3c3dbca 100644
--- a/src/main/resources/mapper/production/ProductionProductMainMapper.xml
+++ b/src/main/resources/mapper/production/ProductionProductMainMapper.xml
@@ -10,20 +10,55 @@
         <result property="tenantId" column="tenant_id"/>
         <result property="createTime" column="create_time"/>
         <result property="status" column="status"/>
+        <result property="reportStartTime" column="report_start_time"/>
+        <result property="reportEndTime" column="report_end_time"/>
+        <result property="reportDurationMinutes" column="report_duration_minutes"/>
     </resultMap>
 
     <select id="listPageProductionProductMainDto" resultType="com.ruoyi.production.dto.ProductionProductMainDto">
-        select ppm.*,
-        pwo.work_order_no as workOrderNo,
-        pwo.status as workOrderStatus,
-        u.nick_name as nickName,
-        p.product_name as productName,
-        pp.name as process,
-        pm.model as productModelName,
-        ppo.quantity,
-        ppo.scrap_qty,
-        pm.unit,
-        sl.sales_contract_no salesContractNo
+        select
+        min(ppm.id) as id,
+        null as productNo,
+        min(ppm.user_id) as userId,
+        min(ppm.work_order_id) as workOrderId,
+        min(ppm.status) as status,
+        min(ppm.report_start_time) as reportStartTime,
+        max(ppm.report_end_time) as reportEndTime,
+        sum(ifnull(ppm.report_duration_minutes, 0)) as reportDurationMinutes,
+        max(pwo.work_order_no) as workOrderNo,
+        max(po.nps_no) as productOrderNpsNo,
+        max(pwo.status) as workOrderStatus,
+        max(u.nick_name) as nickName,
+        max(p.product_name) as productName,
+        max(CASE pp.type
+            WHEN 1 THEN '鍔犲伐'
+            WHEN 2 THEN '鍒澘鍐疯姱鍒朵綔'
+            WHEN 3 THEN '绠¤矾缁勫'
+            WHEN 4 THEN '缃愪綋杩炴帴鍙婅皟璇�'
+            WHEN 5 THEN '娴嬭瘯鎵撳帇'
+            WHEN 6 THEN '鍏朵粬'
+            ELSE pp.name
+        END) as process,
+        max(pm.model) as productModelName,
+        sum(ifnull(ppo.quantity, 0)) as quantity,
+        sum(ifnull(ppo.scrap_qty, 0)) as scrapQty,
+        sum(ifnull(ppo.quantity, 0)) as outputTotalQuantity,
+        sum(ifnull(ppo.scrap_qty, 0)) as scrapTotalQuantity,
+        round(ifnull((
+            select sum(ifnull(pri.planned_work_hours, 0))
+            from product_process_route_item pri
+            where pri.product_order_id = po.id
+        ), 0), 2) as projectTotalHours,
+        ifnull(max(pp.salary_quota), 0) as processStandardHours,
+        round(sum(ifnull(ppm.report_duration_minutes, 0)) / 60, 2) as actualReportHours,
+        round(ifnull((
+            select sum(d.duration_minutes)
+            from production_product_report_daily d
+            where d.user_id = ppm.user_id
+              and d.report_date = curdate()
+        ), 0) / 60, 2) as dailyPersonHours,
+        max(pm.unit) as unit,
+        max(sl.sales_contract_no) as salesContractNo
         from
         production_product_main ppm
         left join product_work_order pwo on pwo.id = ppm.work_order_id
@@ -48,10 +83,155 @@
             <if test="c.status != null and c.status != ''">
                 and ppm.status = #{c.status}
             </if>
+            <if test="c.userId != null">
+                and ppm.user_id = #{c.userId}
+            </if>
         </where>
-        order by ppm.id
-
+        group by ppm.work_order_id, po.sales_ledger_id, po.sale_ledger_product_id, ppm.user_id
+        order by max(ppm.id) desc
     </select>
+
+    <select id="listPageProductionProductMainDailyDto" resultType="com.ruoyi.production.dto.ProductionProductMainDto">
+        select
+        min(ppm.id) as id,
+        min(ppm.product_no) as productNo,
+        d.user_id as userId,
+        d.work_order_id as workOrderId,
+        max(pwo.work_order_no) as workOrderNo,
+        max(po.nps_no) as productOrderNpsNo,
+        max(pwo.status) as workOrderStatus,
+        max(u.nick_name) as nickName,
+        max(p.product_name) as productName,
+        max(CASE pp.type
+            WHEN 1 THEN '鍔犲伐'
+            WHEN 2 THEN '鍒澘鍐疯姱鍒朵綔'
+            WHEN 3 THEN '绠¤矾缁勫'
+            WHEN 4 THEN '缃愪綋杩炴帴鍙婅皟璇�'
+            WHEN 5 THEN '娴嬭瘯鎵撳帇'
+            WHEN 6 THEN '鍏朵粬'
+            ELSE pp.name
+        END) as process,
+        max(pm.model) as productModelName,
+        max(pm.unit) as unit,
+        max(sl.sales_contract_no) as salesContractNo,
+        d.report_date as schedulingDate,
+        sum(d.duration_minutes) as reportDurationMinutes,
+        round(sum(d.duration_minutes) / 60, 2) as dailyPersonHours,
+        round(sum(d.duration_minutes) / 60, 2) as actualReportHours,
+        sum(ifnull(ppo.quantity, 0)) as quantity,
+        sum(ifnull(ppo.scrap_qty, 0)) as scrapQty,
+        sum(ifnull(ppo.quantity, 0)) as outputTotalQuantity,
+        sum(ifnull(ppo.scrap_qty, 0)) as scrapTotalQuantity,
+        round(ifnull((
+            select sum(ifnull(pri.planned_work_hours, 0))
+            from product_process_route_item pri
+            where pri.product_order_id = po.id
+        ), 0), 2) as projectTotalHours,
+        ifnull(max(pp.salary_quota), 0) as processStandardHours
+        from production_product_report_daily d
+        left join production_product_main ppm on ppm.id = d.product_main_id
+        left join product_work_order pwo on pwo.id = d.work_order_id
+        left join product_process_route_item ppri on ppri.id = pwo.product_process_route_item_id
+        left join product_process pp on pp.id = ppri.process_id
+        left join product_order po on po.id = pwo.product_order_id
+        left join production_product_output ppo
+            on ppm.id = ppo.product_main_id
+           and date(ppm.report_end_time) = d.report_date
+        left join product_model pm on pm.id = ppo.product_model_id
+        left join product p on p.id = pm.product_id
+        left join sales_ledger sl on sl.id = po.sales_ledger_id
+        left join sys_user u on u.user_id = d.user_id
+        <where>
+            <if test="c.workOrderId != null">
+                and d.work_order_id = #{c.workOrderId}
+            </if>
+            <if test="c.nickName != null and c.nickName != ''">
+                and u.nick_name like concat('%',#{c.nickName},'%')
+            </if>
+            <if test="c.workOrderNo != null and c.workOrderNo != ''">
+                and pwo.work_order_no like concat('%',#{c.workOrderNo},'%')
+            </if>
+            <if test="c.workOrderStatus != null and c.workOrderStatus != ''">
+                and pwo.status = #{c.workOrderStatus}
+            </if>
+            <if test="c.userId != null">
+                and d.user_id = #{c.userId}
+            </if>
+            <if test="c.startDate != null">
+                and d.report_date <![CDATA[ >= ]]> #{c.startDate}
+            </if>
+            <if test="c.endDate != null">
+                and d.report_date <![CDATA[ <= ]]> #{c.endDate}
+            </if>
+        </where>
+        group by d.work_order_id, d.user_id, d.report_date, po.sales_ledger_id, po.sale_ledger_product_id
+        order by d.report_date desc, d.user_id
+    </select>
+
+    <select id="listPageProductionProductMainDetailDto" resultType="com.ruoyi.production.dto.ProductionProductMainDto">
+        select
+        ppm.*,
+        pwo.work_order_no as workOrderNo,
+        po.nps_no as productOrderNpsNo,
+        pwo.status as workOrderStatus,
+        u.nick_name as nickName,
+        p.product_name as productName,
+        CASE pp.type
+            WHEN 1 THEN '鍔犲伐'
+            WHEN 2 THEN '鍒澘鍐疯姱鍒朵綔'
+            WHEN 3 THEN '绠¤矾缁勫'
+            WHEN 4 THEN '缃愪綋杩炴帴鍙婅皟璇�'
+            WHEN 5 THEN '娴嬭瘯鎵撳帇'
+            WHEN 6 THEN '鍏朵粬'
+            ELSE pp.name
+        END as process,
+        pm.model as productModelName,
+        ppo.quantity,
+        ppo.scrap_qty as scrapQty,
+        ppo.quantity as outputTotalQuantity,
+        ppo.scrap_qty as scrapTotalQuantity,
+        pm.unit,
+        sl.sales_contract_no salesContractNo,
+        round(ifnull((
+            select sum(ifnull(pri.planned_work_hours, 0))
+            from product_process_route_item pri
+            where pri.product_order_id = po.id
+        ), 0), 2) as projectTotalHours,
+        ifnull(pp.salary_quota, 0) as processStandardHours,
+        round(ifnull(ppm.report_duration_minutes, 0) / 60, 2) as actualReportHours,
+        round(ifnull((
+            select sum(d.duration_minutes)
+            from production_product_report_daily d
+            where d.user_id = ppm.user_id
+              and d.report_date = curdate()
+        ), 0) / 60, 2) as dailyPersonHours
+        from production_product_main ppm
+        left join product_work_order pwo on pwo.id = ppm.work_order_id
+        left join product_process_route_item ppri on ppri.id = pwo.product_process_route_item_id
+        left join product_process pp on pp.id = ppri.process_id
+        left join product_order po on po.id = pwo.product_order_id
+        left join production_product_output ppo on ppm.id = ppo.product_main_id
+        left join product_model pm on pm.id = ppo.product_model_id
+        left join product p on p.id = pm.product_id
+        left join sales_ledger sl on sl.id = po.sales_ledger_id
+        left join sys_user u on u.user_id = ppm.user_id
+        <where>
+            <if test="c.workOrderId != null">
+                and ppm.work_order_id = #{c.workOrderId}
+            </if>
+            <if test="c.userId != null">
+                and ppm.user_id = #{c.userId}
+            </if>
+            <if test="c.startDate != null">
+                and date(ppm.report_end_time) <![CDATA[ >= ]]> #{c.startDate}
+            </if>
+            <if test="c.endDate != null">
+                and date(ppm.report_end_time) <![CDATA[ <= ]]> #{c.endDate}
+            </if>
+        </where>
+        order by ppm.id desc
+    </select>
+
     <select id="getOrderByMainId" resultType="com.ruoyi.production.pojo.ProductOrder">
         select po.*
         from product_order po
diff --git a/src/main/resources/mapper/production/ProductionProductReportDailyMapper.xml b/src/main/resources/mapper/production/ProductionProductReportDailyMapper.xml
new file mode 100644
index 0000000..bd6136b
--- /dev/null
+++ b/src/main/resources/mapper/production/ProductionProductReportDailyMapper.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.production.mapper.ProductionProductReportDailyMapper">
+
+    <select id="listDailySummary" resultType="com.ruoyi.production.dto.ProductionReportDailySummaryDto">
+        select
+        report_date as reportDate,
+        sum(duration_minutes) as durationMinutes
+        from production_product_report_daily
+        where 1=1
+        <if test="workOrderId != null">
+            and work_order_id = #{workOrderId}
+        </if>
+        <if test="productProcessRouteItemId != null">
+            and product_process_route_item_id = #{productProcessRouteItemId}
+        </if>
+        <if test="userId != null">
+            and user_id = #{userId}
+        </if>
+        <if test="startDate != null">
+            and report_date <![CDATA[ >= ]]> #{startDate}
+        </if>
+        <if test="endDate != null">
+            and report_date <![CDATA[ <= ]]> #{endDate}
+        </if>
+        group by report_date
+        order by report_date
+    </select>
+
+</mapper>
diff --git a/src/main/resources/mapper/system/SysUserMapper.xml b/src/main/resources/mapper/system/SysUserMapper.xml
index e4f818a..c1b83e6 100644
--- a/src/main/resources/mapper/system/SysUserMapper.xml
+++ b/src/main/resources/mapper/system/SysUserMapper.xml
@@ -153,6 +153,7 @@
 	<select id="checkEmailUnique" parameterType="String" resultMap="SysUserResult">
 		select user_id, email from sys_user where email = #{email} and del_flag = '0' limit 1
 	</select>
+
 	<select id="selectUserByIds" resultType="com.ruoyi.project.system.domain.SysUser">
 		<include refid="selectUserVo"/>
 		where u.user_id in <foreach collection="userIds" item="item" open="(" separator="," close=")">
@@ -160,6 +161,7 @@
         </foreach>
 		and u.del_flag = '0'
 	</select>
+
 	<select id="selectRegistrantIds" resultType="com.ruoyi.project.system.domain.SysUser">
 		SELECT user_id, nick_name FROM sys_user
 		<where>

--
Gitblit v1.9.3