From 24019a8c1d8e78656e85b29ee7be223e0dd253a0 Mon Sep 17 00:00:00 2001
From: huminmin <mac@MacBook-Pro.local>
Date: 星期五, 05 六月 2026 09:11:33 +0800
Subject: [PATCH] Merge branch 'dev_new' of http://114.132.189.42:9002/r/lims-ruoyi-after into dev_new

---
 report-server/src/main/java/com/ruoyi/report/service/NormalDistributionService.java          |   34 
 report-server/src/main/java/com/ruoyi/report/dto/WorkStatisticsDto.java                      |   27 
 report-server/src/main/java/com/ruoyi/report/controller/ReportDeviceRecordController.java    |   55 
 report-server/src/main/java/com/ruoyi/report/vo/DashboardOverviewVo.java                     |   42 
 report-server/src/main/java/com/ruoyi/report/controller/SampleProgressController.java        |   62 
 report-server/pom.xml                                                                        |   52 
 report-server/src/main/java/com/ruoyi/report/dto/TestItemDataDto.java                        |   41 
 report-server/src/main/java/com/ruoyi/report/service/TestItemDataService.java                |   41 
 report-server/src/main/java/com/ruoyi/report/service/impl/SpcChartServiceImpl.java           |  267 +++
 report-server/src/main/resources/mapper/ReportDeviceRecordMapper.xml                         |   73 +
 report-server/src/main/resources/mapper/SampleRecordMapper.xml                               |   67 
 report-server/src/main/java/com/ruoyi/report/controller/WorkStatisticsController.java        |   53 
 report-server/src/main/java/com/ruoyi/report/vo/SampleProgressVo.java                        |   57 
 report-server/src/main/java/com/ruoyi/report/mapper/WorkStatisticsMapper.java                |   32 
 report-server/src/main/java/com/ruoyi/report/vo/SampleRecordVo.java                          |   48 
 report-server/src/main/java/com/ruoyi/report/controller/DashboardController.java             |   82 +
 report-server/src/main/java/com/ruoyi/report/dto/SampleProgressDto.java                      |   36 
 report-server/src/main/java/com/ruoyi/report/controller/SampleRecordController.java          |   54 
 report-server/src/main/java/com/ruoyi/report/service/PassRateService.java                    |   39 
 report-server/src/main/java/com/ruoyi/report/service/SampleProgressService.java              |   35 
 report-server/src/main/java/com/ruoyi/report/vo/NormalDistributionVo.java                    |   42 
 report-server/src/main/java/com/ruoyi/report/controller/PassRateController.java              |   71 
 report-server/src/main/java/com/ruoyi/report/service/SampleRecordService.java                |   30 
 report-server/src/main/java/com/ruoyi/report/dto/SpcChartDto.java                            |   41 
 report-server/src/main/java/com/ruoyi/report/service/WorkStatisticsService.java              |   29 
 report-server/src/main/java/com/ruoyi/report/mapper/SpcChartMapper.java                      |   32 
 report-server/src/main/resources/mapper/DashboardMapper.xml                                  |  164 ++
 report-server/src/main/java/com/ruoyi/report/service/impl/NormalDistributionServiceImpl.java |  193 ++
 report-server/src/main/java/com/ruoyi/report/service/impl/SampleRecordServiceImpl.java       |   89 +
 report-server/src/main/java/com/ruoyi/report/controller/NormalDistributionController.java    |   61 
 report-server/src/main/java/com/ruoyi/report/service/impl/TestItemDataServiceImpl.java       |  144 +
 report-server/src/main/java/com/ruoyi/report/vo/ParetoVo.java                                |   23 
 report-server/src/main/java/com/ruoyi/report/mapper/TestItemDataMapper.java                  |   37 
 report-server/src/main/java/com/ruoyi/report/service/ReportDeviceRecordService.java          |   31 
 report-server/src/main/resources/mapper/TestItemDataMapper.xml                               |  132 +
 report-server/src/main/java/com/ruoyi/report/service/impl/SampleProgressServiceImpl.java     |  125 +
 report-server/src/main/java/com/ruoyi/report/vo/TaskCalendarVo.java                          |   27 
 report-server/src/main/java/com/ruoyi/report/dto/PassRateDto.java                            |   41 
 report-server/src/main/java/com/ruoyi/report/service/impl/PassRateServiceImpl.java           |  122 +
 report-server/src/main/java/com/ruoyi/report/mapper/SampleProgressMapper.java                |   33 
 report-server/src/main/java/com/ruoyi/report/mapper/SampleRecordMapper.java                  |   27 
 pom.xml                                                                                      |    8 
 report-server/src/main/java/com/ruoyi/report/service/impl/WorkStatisticsServiceImpl.java     |  103 +
 report-server/src/main/resources/mapper/WorkStatisticsMapper.xml                             |   87 +
 report-server/src/main/java/com/ruoyi/report/controller/TestItemDataController.java          |   73 +
 report-server/src/main/java/com/ruoyi/report/vo/TestItemDataVo.java                          |   57 
 report-server/src/main/java/com/ruoyi/report/mapper/ReportDeviceRecordMapper.java            |   28 
 report-server/src/main/java/com/ruoyi/report/service/SpcChartService.java                    |   39 
 report-server/src/main/java/com/ruoyi/report/dto/DeviceRecordDto.java                        |   30 
 report-server/src/main/java/com/ruoyi/report/service/impl/DashboardServiceImpl.java          |  107 +
 ruoyi-admin/pom.xml                                                                          |    6 
 report-server/src/main/java/com/ruoyi/report/dto/SampleRecordDto.java                        |   33 
 report-server/src/main/java/com/ruoyi/report/mapper/PassRateMapper.java                      |   41 
 report-server/src/main/java/com/ruoyi/report/vo/RankingVo.java                               |   30 
 report-server/src/main/java/com/ruoyi/report/vo/WorkStatisticsVo.java                        |   36 
 report-server/src/main/java/com/ruoyi/report/mapper/NormalDistributionMapper.java            |   31 
 report-server/src/main/resources/mapper/SampleProgressMapper.xml                             |   98 +
 report-server/src/main/resources/mapper/NormalDistributionMapper.xml                         |   57 
 report-server/src/main/java/com/ruoyi/report/dto/DashboardDto.java                           |   24 
 report-server/src/main/java/com/ruoyi/report/mapper/DashboardMapper.java                     |   78 +
 report-server/src/main/resources/mapper/PassRateMapper.xml                                   |  126 +
 report-server/src/main/java/com/ruoyi/report/service/DashboardService.java                   |   46 
 ruoyi-admin/src/main/resources/application-druid.yml                                         |    8 
 report-server/src/main/java/com/ruoyi/report/vo/DeviceRecordVo.java                          |   54 
 ruoyi-admin/src/main/resources/report_chart_sql.sql                                          |  177 ++
 report-server/src/main/java/com/ruoyi/report/service/impl/ReportDeviceRecordServiceImpl.java |   61 
 report-server/src/main/java/com/ruoyi/report/controller/SpcChartController.java              |   70 
 report-server/src/main/java/com/ruoyi/report/dto/NormalDistributionDto.java                  |   27 
 report-server/src/main/resources/mapper/SpcChartMapper.xml                                   |   57 
 report-server/src/main/java/com/ruoyi/report/vo/SpcResultVo.java                             |   58 
 70 files changed, 4,337 insertions(+), 4 deletions(-)

diff --git a/pom.xml b/pom.xml
index 2a75212..5c1e5e2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -278,6 +278,13 @@
                 <version>${ruoyi.version}</version>
             </dependency>
 
+            <!--鎶ヨ〃鍥捐〃妯″潡-->
+            <dependency>
+                <groupId>com.ruoyi</groupId>
+                <artifactId>report-server</artifactId>
+                <version>${ruoyi.version}</version>
+            </dependency>
+
             <!-- minio -->
             <dependency>
                 <groupId>io.minio</groupId>
@@ -392,6 +399,7 @@
         <module>cnas-device</module>
         <module>cnas-process</module>
         <module>cnas-personnel</module>
+        <module>report-server</module>
     </modules>
     <packaging>pom</packaging>
 
diff --git a/report-server/pom.xml b/report-server/pom.xml
new file mode 100644
index 0000000..fc82a41
--- /dev/null
+++ b/report-server/pom.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>ruoyi</artifactId>
+        <groupId>com.ruoyi</groupId>
+        <version>3.8.9</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>report-server</artifactId>
+
+    <dependencies>
+        <!-- 閫氱敤宸ュ叿-->
+        <dependency>
+            <groupId>com.ruoyi</groupId>
+            <artifactId>ruoyi-common</artifactId>
+        </dependency>
+
+        <!-- 鏍稿績妯″潡-->
+        <dependency>
+            <groupId>com.ruoyi</groupId>
+            <artifactId>ruoyi-framework</artifactId>
+        </dependency>
+
+        <!--鍩虹妯″潡-->
+        <dependency>
+            <groupId>com.ruoyi</groupId>
+            <artifactId>basic-server</artifactId>
+        </dependency>
+
+        <!-- 绯荤粺妯″潡-->
+        <dependency>
+            <groupId>com.ruoyi</groupId>
+            <artifactId>ruoyi-system</artifactId>
+        </dependency>
+
+        <!--涓氬姟妯″潡-->
+        <dependency>
+            <groupId>com.ruoyi</groupId>
+            <artifactId>inspect-server</artifactId>
+        </dependency>
+
+    </dependencies>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+    </properties>
+
+</project>
diff --git a/report-server/src/main/java/com/ruoyi/report/controller/DashboardController.java b/report-server/src/main/java/com/ruoyi/report/controller/DashboardController.java
new file mode 100644
index 0000000..1255689
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/controller/DashboardController.java
@@ -0,0 +1,82 @@
+package com.ruoyi.report.controller;
+
+import com.ruoyi.common.core.domain.Result;
+import com.ruoyi.report.dto.DashboardDto;
+import com.ruoyi.report.service.DashboardService;
+import com.ruoyi.report.vo.DashboardOverviewVo;
+import com.ruoyi.report.vo.RankingVo;
+import com.ruoyi.report.vo.TaskCalendarVo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 鏁板瓧鍖栬闊崇湅鏉挎帶鍒跺櫒
+ */
+@RequestMapping("/report/dashboard")
+@RestController
+@AllArgsConstructor
+@Api(tags = "鏁板瓧鍖栬闊崇湅鏉�")
+public class DashboardController {
+
+    private DashboardService dashboardService;
+
+    /**
+     * 鑾峰彇鐪嬫澘姒傝鏁版嵁
+     */
+    @ApiOperation(value = "鑾峰彇鐪嬫澘姒傝鏁版嵁")
+    @GetMapping("/overview")
+    public Result overview(DashboardDto dto) {
+        return Result.success(dashboardService.getOverview(dto));
+    }
+
+    /**
+     * 鍘嗗彶15澶╂暟鎹�
+     */
+    @ApiOperation(value = "鍘嗗彶15澶╂暟鎹�")
+    @GetMapping("/history15Days")
+    public Result history15Days(DashboardDto dto) {
+        return Result.success(dashboardService.getHistory15Days(dto));
+    }
+
+    /**
+     * 鏈潵15澶╀换鍔�
+     */
+    @ApiOperation(value = "鏈潵15澶╀换鍔�")
+    @GetMapping("/future15Days")
+    public Result future15Days(DashboardDto dto) {
+        return Result.success(dashboardService.getFuture15Days(dto));
+    }
+
+    /**
+     * 鎻愪氦鎺掕
+     */
+    @ApiOperation(value = "鎻愪氦鎺掕")
+    @GetMapping("/ranking")
+    public Result ranking(DashboardDto dto) {
+        return Result.success(dashboardService.getRanking(dto));
+    }
+
+    /**
+     * 妫�楠岀粨鏋滅粺璁�
+     */
+    @ApiOperation(value = "妫�楠岀粨鏋滅粺璁�")
+    @GetMapping("/insResult")
+    public Result insResult(DashboardDto dto) {
+        return Result.success(dashboardService.getInsResult(dto));
+    }
+
+    /**
+     * 鑾峰彇璇煶鎾姤闃熷垪
+     */
+    @ApiOperation(value = "鑾峰彇璇煶鎾姤闃熷垪")
+    @GetMapping("/voiceQueue")
+    public Result voiceQueue() {
+        return Result.success(dashboardService.getVoiceQueue());
+    }
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/controller/NormalDistributionController.java b/report-server/src/main/java/com/ruoyi/report/controller/NormalDistributionController.java
new file mode 100644
index 0000000..c46f045
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/controller/NormalDistributionController.java
@@ -0,0 +1,61 @@
+package com.ruoyi.report.controller;
+
+import com.ruoyi.common.core.domain.Result;
+import com.ruoyi.report.dto.NormalDistributionDto;
+import com.ruoyi.report.service.NormalDistributionService;
+import com.ruoyi.report.vo.NormalDistributionVo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * 姝f�佸垎甯冨浘鎺у埗鍣�
+ */
+@RequestMapping("/chart/normalDistribution")
+@RestController
+@AllArgsConstructor
+@Api(tags = "姝f�佸垎甯冨浘")
+public class NormalDistributionController {
+
+    private NormalDistributionService normalDistributionService;
+
+    /**
+     * 姝f�佸垎甯冨垎鏋�
+     */
+    @ApiOperation(value = "姝f�佸垎甯冨垎鏋�")
+    @PostMapping("/analyze")
+    public Result analyze(@RequestBody NormalDistributionDto dto) {
+        return Result.success(normalDistributionService.analyze(dto));
+    }
+
+    /**
+     * 瀵煎嚭鍒嗘瀽鏁版嵁
+     */
+    @ApiOperation(value = "瀵煎嚭鍒嗘瀽鏁版嵁")
+    @GetMapping("/export")
+    public void export(NormalDistributionDto dto, HttpServletResponse response) {
+        normalDistributionService.export(dto, response);
+    }
+
+    /**
+     * 鏌ヨ鍙�夋娴嬮」
+     */
+    @ApiOperation(value = "鏌ヨ鍙�夋娴嬮」")
+    @GetMapping("/itemNames")
+    public Result getItemNames(NormalDistributionDto dto) {
+        return Result.success(normalDistributionService.getItemNames(dto));
+    }
+
+    /**
+     * 鏌ヨ鍙�夋牱鍝佸悕绉板垪琛�
+     */
+    @ApiOperation(value = "鏌ヨ鍙�夋牱鍝佸悕绉板垪琛�")
+    @GetMapping("/projectList")
+    public Result getProjectList(NormalDistributionDto dto) {
+        return Result.success(normalDistributionService.getSampleNames(dto));
+    }
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/controller/PassRateController.java b/report-server/src/main/java/com/ruoyi/report/controller/PassRateController.java
new file mode 100644
index 0000000..4686bcd
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/controller/PassRateController.java
@@ -0,0 +1,71 @@
+package com.ruoyi.report.controller;
+
+import com.ruoyi.common.core.domain.Result;
+import com.ruoyi.report.dto.PassRateDto;
+import com.ruoyi.report.service.PassRateService;
+import com.ruoyi.report.vo.ParetoVo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 鍚堟牸鐜囩粺璁℃帶鍒跺櫒
+ */
+@RequestMapping("/chart/passRate")
+@RestController
+@AllArgsConstructor
+@Api(tags = "鍚堟牸鐜囩粺璁�")
+public class PassRateController {
+
+    private PassRateService passRateService;
+
+    /**
+     * 鍘熸潗鏂欏悎鏍肩巼
+     */
+    @ApiOperation(value = "鍘熸潗鏂欏悎鏍肩巼")
+    @GetMapping("/rawMaterial")
+    public Result rawMaterial(PassRateDto dto) {
+        return Result.success(passRateService.getRawMaterialPassRate(dto));
+    }
+
+    /**
+     * 渚涘簲鍟嗕笉鍚堟牸缁熻
+     */
+    @ApiOperation(value = "渚涘簲鍟嗕笉鍚堟牸缁熻")
+    @GetMapping("/supplier")
+    public Result supplier(PassRateDto dto) {
+        return Result.success(passRateService.getSupplierUnqualified(dto));
+    }
+
+    /**
+     * 甯曠疮鎵樺浘鏁版嵁
+     */
+    @ApiOperation(value = "甯曠疮鎵樺浘鏁版嵁")
+    @GetMapping("/pareto")
+    public Result pareto(PassRateDto dto) {
+        return Result.success(passRateService.getPareto(dto));
+    }
+
+    /**
+     * 宸ュ簭鍚堟牸鐜�
+     */
+    @ApiOperation(value = "宸ュ簭鍚堟牸鐜�")
+    @GetMapping("/process")
+    public Result process(PassRateDto dto) {
+        return Result.success(passRateService.getProcessPassRate(dto));
+    }
+
+    /**
+     * 鏈哄彴涓嶅悎鏍肩粺璁�
+     */
+    @ApiOperation(value = "鏈哄彴涓嶅悎鏍肩粺璁�")
+    @GetMapping("/machine")
+    public Result machine(PassRateDto dto) {
+        return Result.success(passRateService.getMachineUnqualified(dto));
+    }
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/controller/ReportDeviceRecordController.java b/report-server/src/main/java/com/ruoyi/report/controller/ReportDeviceRecordController.java
new file mode 100644
index 0000000..c94f7da
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/controller/ReportDeviceRecordController.java
@@ -0,0 +1,55 @@
+package com.ruoyi.report.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.common.core.domain.Result;
+import com.ruoyi.report.dto.DeviceRecordDto;
+import com.ruoyi.report.service.ReportDeviceRecordService;
+import com.ruoyi.report.vo.DeviceRecordVo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 璁惧浣跨敤璁板綍鎺у埗鍣�
+ */
+@RequestMapping("/report/deviceRecord")
+@RestController
+@AllArgsConstructor
+@Api(tags = "璁惧浣跨敤璁板綍鎶ヨ〃")
+public class ReportDeviceRecordController {
+
+    private ReportDeviceRecordService reportDeviceRecordService;
+
+    /**
+     * 鍒嗛〉鏌ヨ璁惧浣跨敤璁板綍
+     */
+    @ApiOperation(value = "鍒嗛〉鏌ヨ璁惧浣跨敤璁板綍")
+    @GetMapping("/page")
+    public Result page(DeviceRecordDto dto, Page page) {
+        return Result.success(reportDeviceRecordService.pageDeviceRecord(page, dto));
+    }
+
+    /**
+     * 璁惧浣跨敤缁熻
+     */
+    @ApiOperation(value = "璁惧浣跨敤缁熻")
+    @GetMapping("/statistics")
+    public Result statistics(DeviceRecordDto dto) {
+        return Result.success(reportDeviceRecordService.getStatistics(dto));
+    }
+
+    /**
+     * 瀵煎嚭璁板綍
+     */
+    @ApiOperation(value = "瀵煎嚭璁板綍")
+    @GetMapping("/export")
+    public void export(DeviceRecordDto dto, HttpServletResponse response) {
+        reportDeviceRecordService.exportDeviceRecord(dto, response);
+    }
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/controller/SampleProgressController.java b/report-server/src/main/java/com/ruoyi/report/controller/SampleProgressController.java
new file mode 100644
index 0000000..853cbab
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/controller/SampleProgressController.java
@@ -0,0 +1,62 @@
+package com.ruoyi.report.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.common.core.domain.Result;
+import com.ruoyi.report.dto.SampleProgressDto;
+import com.ruoyi.report.service.SampleProgressService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.Map;
+
+/**
+ * 鏍峰搧杩涘害鎶ヨ〃鎺у埗鍣�
+ */
+@RequestMapping("/report/sampleProgress")
+@RestController
+@AllArgsConstructor
+@Api(tags = "鏍峰搧杩涘害鎶ヨ〃")
+public class SampleProgressController {
+
+    private SampleProgressService sampleProgressService;
+
+    /**
+     * 鍒嗛〉鏌ヨ鏍峰搧杩涘害
+     */
+    @ApiOperation(value = "鍒嗛〉鏌ヨ鏍峰搧杩涘害")
+    @GetMapping("/page")
+    public Result page(SampleProgressDto dto, Page page) {
+        return Result.success(sampleProgressService.pageSampleProgress(page, dto));
+    }
+
+    /**
+     * 鏌ヨ鏍峰搧杩涘害缁熻
+     */
+    @ApiOperation(value = "鏌ヨ鏍峰搧杩涘害缁熻")
+    @GetMapping("/statistics")
+    public Result statistics(SampleProgressDto dto) {
+        return Result.success(sampleProgressService.getStatistics(dto));
+    }
+
+    /**
+     * 瀵煎嚭鏍峰搧杩涘害鎶ヨ〃
+     */
+    @ApiOperation(value = "瀵煎嚭鏍峰搧杩涘害鎶ヨ〃")
+    @GetMapping("/export")
+    public void export(SampleProgressDto dto, HttpServletResponse response) {
+        sampleProgressService.exportSampleProgress(dto, response);
+    }
+
+    /**
+     * 鏌ヨ杩涘害鍙鍖栨暟鎹�
+     */
+    @ApiOperation(value = "鏌ヨ杩涘害鍙鍖栨暟鎹�")
+    @GetMapping("/chart")
+    public Result getChartData(SampleProgressDto dto) {
+        return Result.success(sampleProgressService.getChartData(dto));
+    }
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/controller/SampleRecordController.java b/report-server/src/main/java/com/ruoyi/report/controller/SampleRecordController.java
new file mode 100644
index 0000000..66e9780
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/controller/SampleRecordController.java
@@ -0,0 +1,54 @@
+package com.ruoyi.report.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.common.core.domain.Result;
+import com.ruoyi.report.dto.SampleRecordDto;
+import com.ruoyi.report.service.SampleRecordService;
+import com.ruoyi.report.vo.SampleRecordVo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+/**
+ * 鏍峰搧棰嗘牱璁板綍鎺у埗鍣�
+ */
+@RequestMapping("/report/sampleRecord")
+@RestController
+@AllArgsConstructor
+@Api(tags = "鏍峰搧棰嗘牱璁板綍")
+public class SampleRecordController {
+
+    private SampleRecordService sampleRecordService;
+
+    /**
+     * 鍒嗛〉鏌ヨ棰嗘牱璁板綍
+     */
+    @ApiOperation(value = "鍒嗛〉鏌ヨ棰嗘牱璁板綍")
+    @GetMapping("/page")
+    public Result page(SampleRecordDto dto, Page page) {
+        return Result.success(sampleRecordService.pageSampleRecord(page, dto));
+    }
+
+    /**
+     * 鏌ヨ鏍峰搧娴佽浆璁板綍
+     */
+    @ApiOperation(value = "鏌ヨ鏍峰搧娴佽浆璁板綍")
+    @GetMapping("/flow")
+    public Result flow(@RequestParam Long sampleId) {
+        return Result.success(sampleRecordService.getFlowRecord(sampleId));
+    }
+
+    /**
+     * 瀵煎嚭璁板綍
+     */
+    @ApiOperation(value = "瀵煎嚭璁板綍")
+    @GetMapping("/export")
+    public void export(SampleRecordDto dto, HttpServletResponse response) {
+        sampleRecordService.exportSampleRecord(dto, response);
+    }
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/controller/SpcChartController.java b/report-server/src/main/java/com/ruoyi/report/controller/SpcChartController.java
new file mode 100644
index 0000000..e9ef66c
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/controller/SpcChartController.java
@@ -0,0 +1,70 @@
+package com.ruoyi.report.controller;
+
+import com.ruoyi.common.core.domain.Result;
+import com.ruoyi.report.dto.SpcChartDto;
+import com.ruoyi.report.service.SpcChartService;
+import com.ruoyi.report.vo.SpcResultVo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * SPC鎺у埗鍥炬帶鍒跺櫒
+ */
+@RequestMapping("/chart/spc")
+@RestController
+@AllArgsConstructor
+@Api(tags = "SPC鎺у埗鍥�")
+public class SpcChartController {
+
+    private SpcChartService spcChartService;
+
+    /**
+     * SPC鍒嗘瀽
+     */
+    @ApiOperation(value = "SPC鍒嗘瀽")
+    @PostMapping("/analyze")
+    public Result analyze(@RequestBody SpcChartDto dto) {
+        return Result.success(spcChartService.analyze(dto));
+    }
+
+    /**
+     * 鍒剁▼鑳藉姏鍒嗘瀽
+     */
+    @ApiOperation(value = "鍒剁▼鑳藉姏鍒嗘瀽")
+    @GetMapping("/capability")
+    public Result capability(SpcChartDto dto) {
+        return Result.success(spcChartService.getCapability(dto));
+    }
+
+    /**
+     * 瀵煎嚭鍒嗘瀽鏁版嵁
+     */
+    @ApiOperation(value = "瀵煎嚭鍒嗘瀽鏁版嵁")
+    @GetMapping("/export")
+    public void export(SpcChartDto dto, HttpServletResponse response) {
+        spcChartService.export(dto, response);
+    }
+
+    /**
+     * 鏌ヨ鍙�夋娴嬮」
+     */
+    @ApiOperation(value = "鏌ヨ鍙�夋娴嬮」")
+    @GetMapping("/itemNames")
+    public Result getItemNames(SpcChartDto dto) {
+        return Result.success(spcChartService.getItemNames(dto));
+    }
+
+    /**
+     * 鏌ヨ鍙�夋牱鍝佸悕绉板垪琛�
+     */
+    @ApiOperation(value = "鏌ヨ鍙�夋牱鍝佸悕绉板垪琛�")
+    @GetMapping("/projectList")
+    public Result getProjectList(SpcChartDto dto) {
+        return Result.success(spcChartService.getSampleNames(dto));
+    }
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/controller/TestItemDataController.java b/report-server/src/main/java/com/ruoyi/report/controller/TestItemDataController.java
new file mode 100644
index 0000000..c20fc52
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/controller/TestItemDataController.java
@@ -0,0 +1,73 @@
+package com.ruoyi.report.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.common.core.domain.Result;
+import com.ruoyi.report.dto.TestItemDataDto;
+import com.ruoyi.report.service.TestItemDataService;
+import com.ruoyi.report.vo.TestItemDataVo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 妫�娴嬮」鐩暟鎹帶鍒跺櫒
+ */
+@RequestMapping("/report/testItemData")
+@RestController
+@AllArgsConstructor
+@Api(tags = "妫�娴嬮」鐩暟鎹�")
+public class TestItemDataController {
+
+    private TestItemDataService testItemDataService;
+
+    /**
+     * 鍒嗛〉鏌ヨ妫�娴嬮」鐩暟鎹�
+     */
+    @ApiOperation(value = "鍒嗛〉鏌ヨ妫�娴嬮」鐩暟鎹�")
+    @GetMapping("/page")
+    public Result page(TestItemDataDto dto, Page page) {
+        return Result.success(testItemDataService.pageTestItemData(page, dto));
+    }
+
+    /**
+     * 鏌ヨ妫�娴嬮」鐩鎯�
+     */
+    @ApiOperation(value = "鏌ヨ妫�娴嬮」鐩鎯�")
+    @GetMapping("/detail")
+    public Result detail(@RequestParam Long sampleId) {
+        return Result.success(testItemDataService.getDetail(sampleId));
+    }
+
+    /**
+     * 鏁版嵁妯悜姣旇緝
+     */
+    @ApiOperation(value = "鏁版嵁妯悜姣旇緝")
+    @PostMapping("/compare")
+    public Result compare(@RequestBody TestItemDataDto dto) {
+        return Result.success(testItemDataService.compare(dto));
+    }
+
+    /**
+     * 瀵煎嚭鏁版嵁
+     */
+    @ApiOperation(value = "瀵煎嚭鏁版嵁")
+    @GetMapping("/export")
+    public void export(TestItemDataDto dto, HttpServletResponse response) {
+        testItemDataService.exportTestItemData(dto, response);
+    }
+
+    /**
+     * 鏌ヨ妫�娴嬮」鍚嶇О鍒楄〃
+     */
+    @ApiOperation(value = "鏌ヨ妫�娴嬮」鍚嶇О鍒楄〃")
+    @GetMapping("/itemNames")
+    public Result getItemNames(TestItemDataDto dto) {
+        return Result.success(testItemDataService.getItemNames(dto));
+    }
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/controller/WorkStatisticsController.java b/report-server/src/main/java/com/ruoyi/report/controller/WorkStatisticsController.java
new file mode 100644
index 0000000..a8a25f2
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/controller/WorkStatisticsController.java
@@ -0,0 +1,53 @@
+package com.ruoyi.report.controller;
+
+import com.ruoyi.common.core.domain.Result;
+import com.ruoyi.report.dto.WorkStatisticsDto;
+import com.ruoyi.report.service.WorkStatisticsService;
+import com.ruoyi.report.vo.WorkStatisticsVo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 宸ヤ綔缁熻鎺у埗鍣�
+ */
+@RequestMapping("/chart/workStatistics")
+@RestController
+@AllArgsConstructor
+@Api(tags = "宸ヤ綔缁熻")
+public class WorkStatisticsController {
+
+    private WorkStatisticsService workStatisticsService;
+
+    /**
+     * 鎸変汉鍛樼粺璁�
+     */
+    @ApiOperation(value = "鎸変汉鍛樼粺璁�")
+    @GetMapping("/byUser")
+    public Result byUser(WorkStatisticsDto dto) {
+        return Result.success(workStatisticsService.getByUser(dto));
+    }
+
+    /**
+     * 鍙婃椂鐜囩粺璁�
+     */
+    @ApiOperation(value = "鍙婃椂鐜囩粺璁�")
+    @GetMapping("/timelyRate")
+    public Result timelyRate(WorkStatisticsDto dto) {
+        return Result.success(workStatisticsService.getTimelyRate(dto));
+    }
+
+    /**
+     * 宸ヤ綔瓒嬪娍鍥�
+     */
+    @ApiOperation(value = "宸ヤ綔瓒嬪娍鍥�")
+    @GetMapping("/trend")
+    public Result trend(WorkStatisticsDto dto) {
+        return Result.success(workStatisticsService.getTrend(dto));
+    }
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/dto/DashboardDto.java b/report-server/src/main/java/com/ruoyi/report/dto/DashboardDto.java
new file mode 100644
index 0000000..efd52b6
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/dto/DashboardDto.java
@@ -0,0 +1,24 @@
+package com.ruoyi.report.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 鐪嬫澘鏌ヨDTO
+ */
+@Data
+public class DashboardDto {
+
+    @ApiModelProperty("寮�濮嬫椂闂�")
+    private String startTime;
+
+    @ApiModelProperty("缁撴潫鏃堕棿")
+    private String endTime;
+
+    @ApiModelProperty("鏃堕棿绫诲瀷(1:鏈懆/2:鏈湀/3:鏈勾)")
+    private String dateType;
+
+    @ApiModelProperty("妫�楠岀被鍨�")
+    private String orderType;
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/dto/DeviceRecordDto.java b/report-server/src/main/java/com/ruoyi/report/dto/DeviceRecordDto.java
new file mode 100644
index 0000000..67023f5
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/dto/DeviceRecordDto.java
@@ -0,0 +1,30 @@
+package com.ruoyi.report.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 璁惧浣跨敤璁板綍鏌ヨDTO
+ */
+@Data
+public class DeviceRecordDto {
+
+    @ApiModelProperty("璁惧缂栧彿")
+    private String deviceCode;
+
+    @ApiModelProperty("璁惧鍚嶇О")
+    private String deviceName;
+
+    @ApiModelProperty("寮�濮嬫椂闂�")
+    private String startTime;
+
+    @ApiModelProperty("缁撴潫鏃堕棿")
+    private String endTime;
+
+    @ApiModelProperty("浣跨敤浜�")
+    private String useUser;
+
+    @ApiModelProperty("鏍峰搧缂栧彿")
+    private String sampleCode;
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/dto/NormalDistributionDto.java b/report-server/src/main/java/com/ruoyi/report/dto/NormalDistributionDto.java
new file mode 100644
index 0000000..152165e
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/dto/NormalDistributionDto.java
@@ -0,0 +1,27 @@
+package com.ruoyi.report.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 姝f�佸垎甯冨垎鏋愭煡璇TO
+ */
+@Data
+public class NormalDistributionDto {
+
+    @ApiModelProperty("妫�娴嬮」鍚嶇О")
+    private String itemName;
+
+    @ApiModelProperty("鏍峰搧鍚嶇О")
+    private String sampleName;
+
+    @ApiModelProperty("寮�濮嬫椂闂�")
+    private String startDate;
+
+    @ApiModelProperty("缁撴潫鏃堕棿")
+    private String endDate;
+
+    @ApiModelProperty("鍒嗙粍鏁伴噺")
+    private Integer binCount;
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/dto/PassRateDto.java b/report-server/src/main/java/com/ruoyi/report/dto/PassRateDto.java
new file mode 100644
index 0000000..f236130
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/dto/PassRateDto.java
@@ -0,0 +1,41 @@
+package com.ruoyi.report.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 鍚堟牸鐜囩粺璁℃煡璇TO
+ */
+@Data
+public class PassRateDto {
+
+    @ApiModelProperty("寮�濮嬫椂闂�")
+    private String startTime;
+
+    @ApiModelProperty("缁撴潫鏃堕棿")
+    private String endTime;
+
+    @ApiModelProperty("鏃堕棿绫诲瀷(1:鏈懆/2:鏈湀/3:鏈勾)")
+    private String dateType;
+
+    @ApiModelProperty("妫�楠岀被鍨�(1:鍘熸潗鏂�/2:鍗婃垚鍝�/3:鎴愬搧)")
+    private String orderType;
+
+    @ApiModelProperty("鏍峰搧鍚嶇О")
+    private String sampleName;
+
+    @ApiModelProperty("渚涘簲鍟嗗悕绉�")
+    private String supplierName;
+
+    @ApiModelProperty("宸ュ簭")
+    private String process;
+
+    @ApiModelProperty("鏈哄彴")
+    private String machine;
+
+    @ApiModelProperty("涓嶅悎鏍奸」鐩垪琛�")
+    private List<String> itemNames;
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/dto/SampleProgressDto.java b/report-server/src/main/java/com/ruoyi/report/dto/SampleProgressDto.java
new file mode 100644
index 0000000..df8a80e
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/dto/SampleProgressDto.java
@@ -0,0 +1,36 @@
+package com.ruoyi.report.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 鏍峰搧杩涘害鏌ヨDTO
+ */
+@Data
+public class SampleProgressDto {
+
+    @ApiModelProperty("濮旀墭缂栧彿/鐢宠鍗曞彿")
+    private String entrustCode;
+
+    @ApiModelProperty("鏍峰搧缂栧彿")
+    private String sampleCode;
+
+    @ApiModelProperty("鏍峰搧鍚嶇О")
+    private String sampleName;
+
+    @ApiModelProperty("鎶ュ憡缂栧彿")
+    private String reportCode;
+
+    @ApiModelProperty("寮�濮嬫椂闂�")
+    private String startTime;
+
+    @ApiModelProperty("缁撴潫鏃堕棿")
+    private String endTime;
+
+    @ApiModelProperty("妫�娴嬬姸鎬�")
+    private Integer insState;
+
+    @ApiModelProperty("瀹㈡埛鍚嶇О")
+    private String custom;
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/dto/SampleRecordDto.java b/report-server/src/main/java/com/ruoyi/report/dto/SampleRecordDto.java
new file mode 100644
index 0000000..392e1ba
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/dto/SampleRecordDto.java
@@ -0,0 +1,33 @@
+package com.ruoyi.report.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 鏍峰搧棰嗘牱璁板綍鏌ヨDTO
+ */
+@Data
+public class SampleRecordDto {
+
+    @ApiModelProperty("鏍峰搧缂栧彿")
+    private String sampleCode;
+
+    @ApiModelProperty("鏍峰搧鍚嶇О")
+    private String sampleName;
+
+    @ApiModelProperty("瀹㈡埛鍚嶇О")
+    private String custom;
+
+    @ApiModelProperty("寮�濮嬫椂闂�")
+    private String startTime;
+
+    @ApiModelProperty("缁撴潫鏃堕棿")
+    private String endTime;
+
+    @ApiModelProperty("棰嗙敤浜�")
+    private String receiveUser;
+
+    @ApiModelProperty("鏍峰搧ID")
+    private Long sampleId;
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/dto/SpcChartDto.java b/report-server/src/main/java/com/ruoyi/report/dto/SpcChartDto.java
new file mode 100644
index 0000000..f496c79
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/dto/SpcChartDto.java
@@ -0,0 +1,41 @@
+package com.ruoyi.report.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * SPC鍒嗘瀽鏌ヨDTO
+ */
+@Data
+public class SpcChartDto {
+
+    @ApiModelProperty("椤圭洰ID")
+    private Long projectId;
+
+    @ApiModelProperty("妫�娴嬮」鍚嶇О")
+    private String itemName;
+
+    @ApiModelProperty("寮�濮嬫椂闂�")
+    private String startDate;
+
+    @ApiModelProperty("缁撴潫鏃堕棿")
+    private String endDate;
+
+    @ApiModelProperty("瀛愮粍澶у皬")
+    private Integer subgroupSize;
+
+    @ApiModelProperty("鎺у埗涓婇檺UCL")
+    private BigDecimal ucl;
+
+    @ApiModelProperty("鎺у埗涓嬮檺LCL")
+    private BigDecimal lcl;
+
+    @ApiModelProperty("鐩爣鍊�")
+    private BigDecimal targetValue;
+
+    @ApiModelProperty("鏍峰搧鍚嶇О")
+    private String sampleName;
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/dto/TestItemDataDto.java b/report-server/src/main/java/com/ruoyi/report/dto/TestItemDataDto.java
new file mode 100644
index 0000000..7564b2b
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/dto/TestItemDataDto.java
@@ -0,0 +1,41 @@
+package com.ruoyi.report.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 妫�娴嬮」鐩暟鎹煡璇TO
+ */
+@Data
+public class TestItemDataDto {
+
+    @ApiModelProperty("鐢熶骇璁㈠崟")
+    private String productionOrder;
+
+    @ApiModelProperty("鎵规鍙�")
+    private String batchNo;
+
+    @ApiModelProperty("鏍峰搧缂栧彿")
+    private String sampleCode;
+
+    @ApiModelProperty("鏍峰搧鍚嶇О")
+    private String sampleName;
+
+    @ApiModelProperty("寮�濮嬫椂闂�")
+    private String startTime;
+
+    @ApiModelProperty("缁撴潫鏃堕棿")
+    private String endTime;
+
+    @ApiModelProperty("妫�娴嬬姸鎬�")
+    private Integer insState;
+
+    @ApiModelProperty("妫�娴嬮」鍚嶇О")
+    private String itemName;
+
+    @ApiModelProperty("鏍峰搧ID鍒楄〃(鐢ㄤ簬姣旇緝)")
+    private List<Long> sampleIds;
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/dto/WorkStatisticsDto.java b/report-server/src/main/java/com/ruoyi/report/dto/WorkStatisticsDto.java
new file mode 100644
index 0000000..2409062
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/dto/WorkStatisticsDto.java
@@ -0,0 +1,27 @@
+package com.ruoyi.report.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 宸ヤ綔缁熻鏌ヨDTO
+ */
+@Data
+public class WorkStatisticsDto {
+
+    @ApiModelProperty("寮�濮嬫椂闂�")
+    private String startTime;
+
+    @ApiModelProperty("缁撴潫鏃堕棿")
+    private String endTime;
+
+    @ApiModelProperty("鏃堕棿绫诲瀷(1:鏈懆/2:鏈湀/3:鏈勾)")
+    private String dateType;
+
+    @ApiModelProperty("鐢ㄦ埛ID")
+    private Long userId;
+
+    @ApiModelProperty("閮ㄩ棬ID")
+    private Long deptId;
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/mapper/DashboardMapper.java b/report-server/src/main/java/com/ruoyi/report/mapper/DashboardMapper.java
new file mode 100644
index 0000000..d2c99f9
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/mapper/DashboardMapper.java
@@ -0,0 +1,78 @@
+package com.ruoyi.report.mapper;
+
+import com.ruoyi.report.dto.DashboardDto;
+import com.ruoyi.report.vo.RankingVo;
+import com.ruoyi.report.vo.TaskCalendarVo;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 鏁板瓧鍖栬闊崇湅鏉縈apper
+ */
+@Mapper
+public interface DashboardMapper {
+
+    /**
+     * 鑾峰彇寰呴鏍峰搧鏁�
+     */
+    Integer getWaitReceive();
+
+    /**
+     * 鑾峰彇寰呮鏍峰搧鏁�
+     */
+    Integer getWaitInspection();
+
+    /**
+     * 鑾峰彇寰呭鏍告牱鍝佹暟
+     */
+    Integer getWaitAudit();
+
+    /**
+     * 鑾峰彇寰呯紪鍒舵姤鍛婃暟
+     */
+    Integer getWaitReport();
+
+    /**
+     * 浠婃棩鏂板鏍峰搧
+     */
+    Integer getTodayNewSample();
+
+    /**
+     * 浠婃棩瀹屾垚鏍峰搧
+     */
+    Integer getTodayFinished();
+
+    /**
+     * 鍘嗗彶N澶╂暟鎹�
+     */
+    List<TaskCalendarVo> getHistoryDays(@Param("days") Integer days, @Param("dto") DashboardDto dto);
+
+    /**
+     * 鏈潵N澶╀换鍔�
+     */
+    List<TaskCalendarVo> getFutureDays(@Param("days") Integer days, @Param("dto") DashboardDto dto);
+
+    /**
+     * 鎻愪氦鎺掕(鍘熷璁板綍)
+     */
+    List<RankingVo> getOriginalRecordRanking(@Param("dto") DashboardDto dto);
+
+    /**
+     * 鎻愪氦鎺掕(鎶ュ憡)
+     */
+    List<RankingVo> getReportRanking(@Param("dto") DashboardDto dto);
+
+    /**
+     * 杩�30澶╂楠岀粨鏋�
+     */
+    List<Map<String, Object>> getInsResultByDays(@Param("days") Integer days, @Param("orderType") String orderType);
+
+    /**
+     * 鑾峰彇璇煶鎾姤闃熷垪
+     */
+    List<Map<String, Object>> getVoiceQueue();
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/mapper/NormalDistributionMapper.java b/report-server/src/main/java/com/ruoyi/report/mapper/NormalDistributionMapper.java
new file mode 100644
index 0000000..6ef510d
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/mapper/NormalDistributionMapper.java
@@ -0,0 +1,31 @@
+package com.ruoyi.report.mapper;
+
+import com.ruoyi.report.dto.NormalDistributionDto;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 姝f�佸垎甯冨浘Mapper
+ */
+@Mapper
+public interface NormalDistributionMapper {
+
+    /**
+     * 鏌ヨ妫�娴嬮」鏁版嵁
+     */
+    List<Map<String, Object>> getItemData(@Param("dto") NormalDistributionDto dto);
+
+    /**
+     * 鏌ヨ鍙�夋娴嬮」
+     */
+    List<String> getItemNames(@Param("dto") NormalDistributionDto dto);
+
+    /**
+     * 鏌ヨ鍙�夋牱鍝佸悕绉板垪琛�
+     */
+    List<String> getSampleNames(@Param("dto") NormalDistributionDto dto);
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/mapper/PassRateMapper.java b/report-server/src/main/java/com/ruoyi/report/mapper/PassRateMapper.java
new file mode 100644
index 0000000..93a5cec
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/mapper/PassRateMapper.java
@@ -0,0 +1,41 @@
+package com.ruoyi.report.mapper;
+
+import com.ruoyi.report.dto.PassRateDto;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 鍚堟牸鐜囩粺璁apper
+ */
+@Mapper
+public interface PassRateMapper {
+
+    /**
+     * 鍘熸潗鏂欏悎鏍肩巼
+     */
+    List<Map<String, Object>> getRawMaterialPassRate(@Param("dto") PassRateDto dto);
+
+    /**
+     * 渚涘簲鍟嗕笉鍚堟牸缁熻
+     */
+    List<Map<String, Object>> getSupplierUnqualified(@Param("dto") PassRateDto dto);
+
+    /**
+     * 涓嶅悎鏍奸」鐩粺璁�(鐢ㄤ簬甯曠疮鎵樺浘)
+     */
+    List<Map<String, Object>> getUnqualifiedItemStats(@Param("dto") PassRateDto dto);
+
+    /**
+     * 宸ュ簭鍚堟牸鐜�
+     */
+    List<Map<String, Object>> getProcessPassRate(@Param("dto") PassRateDto dto);
+
+    /**
+     * 鏈哄彴涓嶅悎鏍肩粺璁�
+     */
+    List<Map<String, Object>> getMachineUnqualified(@Param("dto") PassRateDto dto);
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/mapper/ReportDeviceRecordMapper.java b/report-server/src/main/java/com/ruoyi/report/mapper/ReportDeviceRecordMapper.java
new file mode 100644
index 0000000..7f3f5dd
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/mapper/ReportDeviceRecordMapper.java
@@ -0,0 +1,28 @@
+package com.ruoyi.report.mapper;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.report.dto.DeviceRecordDto;
+import com.ruoyi.report.vo.DeviceRecordVo;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 璁惧浣跨敤璁板綍鎶ヨ〃Mapper
+ */
+@Mapper
+public interface ReportDeviceRecordMapper {
+
+    /**
+     * 鍒嗛〉鏌ヨ璁惧浣跨敤璁板綍
+     */
+    Page<DeviceRecordVo> pageDeviceRecord(Page page, @Param("dto") DeviceRecordDto dto);
+
+    /**
+     * 璁惧浣跨敤缁熻
+     */
+    List<Map<String, Object>> getStatistics(@Param("dto") DeviceRecordDto dto);
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/mapper/SampleProgressMapper.java b/report-server/src/main/java/com/ruoyi/report/mapper/SampleProgressMapper.java
new file mode 100644
index 0000000..bb1e05c
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/mapper/SampleProgressMapper.java
@@ -0,0 +1,33 @@
+package com.ruoyi.report.mapper;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.report.dto.SampleProgressDto;
+import com.ruoyi.report.vo.SampleProgressVo;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 鏍峰搧杩涘害鎶ヨ〃Mapper
+ */
+@Mapper
+public interface SampleProgressMapper {
+
+    /**
+     * 鍒嗛〉鏌ヨ鏍峰搧杩涘害
+     */
+    Page<SampleProgressVo> pageSampleProgress(Page page, @Param("dto") SampleProgressDto dto);
+
+    /**
+     * 鑾峰彇缁熻鏁版嵁
+     */
+    Map<String, Object> getStatistics(@Param("dto") SampleProgressDto dto);
+
+    /**
+     * 鑾峰彇鍥捐〃鏁版嵁
+     */
+    List<Map<String, Object>> getChartData(@Param("dto") SampleProgressDto dto);
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/mapper/SampleRecordMapper.java b/report-server/src/main/java/com/ruoyi/report/mapper/SampleRecordMapper.java
new file mode 100644
index 0000000..aa83b91
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/mapper/SampleRecordMapper.java
@@ -0,0 +1,27 @@
+package com.ruoyi.report.mapper;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.report.dto.SampleRecordDto;
+import com.ruoyi.report.vo.SampleRecordVo;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 鏍峰搧棰嗘牱璁板綍Mapper
+ */
+@Mapper
+public interface SampleRecordMapper {
+
+    /**
+     * 鍒嗛〉鏌ヨ棰嗘牱璁板綍
+     */
+    Page<SampleRecordVo> pageSampleRecord(Page page, @Param("dto") SampleRecordDto dto);
+
+    /**
+     * 鏌ヨ鏍峰搧娴佽浆璁板綍
+     */
+    List<SampleRecordVo> getFlowRecord(@Param("sampleId") Long sampleId);
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/mapper/SpcChartMapper.java b/report-server/src/main/java/com/ruoyi/report/mapper/SpcChartMapper.java
new file mode 100644
index 0000000..b5e8925
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/mapper/SpcChartMapper.java
@@ -0,0 +1,32 @@
+package com.ruoyi.report.mapper;
+
+import com.ruoyi.report.dto.SpcChartDto;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * SPC鎺у埗鍥綧apper
+ */
+@Mapper
+public interface SpcChartMapper {
+
+    /**
+     * 鏌ヨ妫�娴嬮」鏁版嵁
+     */
+    List<Map<String, Object>> getItemData(@Param("dto") SpcChartDto dto);
+
+    /**
+     * 鏌ヨ鍙�夋娴嬮」
+     */
+    List<String> getItemNames(@Param("dto") SpcChartDto dto);
+
+    /**
+     * 鏌ヨ鍙�夋牱鍝佸悕绉板垪琛�
+     */
+    List<String> getSampleNames(@Param("dto") SpcChartDto dto);
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/mapper/TestItemDataMapper.java b/report-server/src/main/java/com/ruoyi/report/mapper/TestItemDataMapper.java
new file mode 100644
index 0000000..bc4e3fb
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/mapper/TestItemDataMapper.java
@@ -0,0 +1,37 @@
+package com.ruoyi.report.mapper;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.report.dto.TestItemDataDto;
+import com.ruoyi.report.vo.TestItemDataVo;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 妫�娴嬮」鐩暟鎹甅apper
+ */
+@Mapper
+public interface TestItemDataMapper {
+
+    /**
+     * 鍒嗛〉鏌ヨ妫�娴嬮」鐩暟鎹�
+     */
+    Page<TestItemDataVo> pageTestItemData(Page page, @Param("dto") TestItemDataDto dto);
+
+    /**
+     * 鏌ヨ妫�娴嬮」鐩鎯�
+     */
+    List<TestItemDataVo> getDetail(@Param("sampleId") Long sampleId);
+
+    /**
+     * 鏌ヨ妫�娴嬮」鍚嶇О鍒楄〃
+     */
+    List<String> getItemNames(@Param("dto") TestItemDataDto dto);
+
+    /**
+     * 鏍规嵁鏍峰搧ID鍒楄〃鏌ヨ妫�娴嬫暟鎹�
+     */
+    List<TestItemDataVo> listBySampleIds(@Param("sampleIds") List<Long> sampleIds);
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/mapper/WorkStatisticsMapper.java b/report-server/src/main/java/com/ruoyi/report/mapper/WorkStatisticsMapper.java
new file mode 100644
index 0000000..6b4bec4
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/mapper/WorkStatisticsMapper.java
@@ -0,0 +1,32 @@
+package com.ruoyi.report.mapper;
+
+import com.ruoyi.report.dto.WorkStatisticsDto;
+import com.ruoyi.report.vo.WorkStatisticsVo;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 宸ヤ綔缁熻Mapper
+ */
+@Mapper
+public interface WorkStatisticsMapper {
+
+    /**
+     * 鎸変汉鍛樼粺璁�
+     */
+    List<WorkStatisticsVo> getByUser(@Param("dto") WorkStatisticsDto dto);
+
+    /**
+     * 鍙婃椂鐜囩粺璁�
+     */
+    List<Map<String, Object>> getTimelyRate(@Param("dto") WorkStatisticsDto dto);
+
+    /**
+     * 宸ヤ綔瓒嬪娍鍥�
+     */
+    List<Map<String, Object>> getTrend(@Param("dto") WorkStatisticsDto dto);
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/service/DashboardService.java b/report-server/src/main/java/com/ruoyi/report/service/DashboardService.java
new file mode 100644
index 0000000..8a7b4b0
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/service/DashboardService.java
@@ -0,0 +1,46 @@
+package com.ruoyi.report.service;
+
+import com.ruoyi.report.dto.DashboardDto;
+import com.ruoyi.report.vo.DashboardOverviewVo;
+import com.ruoyi.report.vo.RankingVo;
+import com.ruoyi.report.vo.TaskCalendarVo;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 鏁板瓧鍖栬闊崇湅鏉挎湇鍔℃帴鍙�
+ */
+public interface DashboardService {
+
+    /**
+     * 鑾峰彇鐪嬫澘姒傝鏁版嵁
+     */
+    DashboardOverviewVo getOverview(DashboardDto dto);
+
+    /**
+     * 鍘嗗彶15澶╂暟鎹�
+     */
+    List<TaskCalendarVo> getHistory15Days(DashboardDto dto);
+
+    /**
+     * 鏈潵15澶╀换鍔�
+     */
+    List<TaskCalendarVo> getFuture15Days(DashboardDto dto);
+
+    /**
+     * 鎻愪氦鎺掕
+     */
+    List<RankingVo> getRanking(DashboardDto dto);
+
+    /**
+     * 妫�楠岀粨鏋滅粺璁�
+     */
+    Map<String, Object> getInsResult(DashboardDto dto);
+
+    /**
+     * 鑾峰彇璇煶鎾姤闃熷垪
+     */
+    List<Map<String, Object>> getVoiceQueue();
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/service/NormalDistributionService.java b/report-server/src/main/java/com/ruoyi/report/service/NormalDistributionService.java
new file mode 100644
index 0000000..6cd8f92
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/service/NormalDistributionService.java
@@ -0,0 +1,34 @@
+package com.ruoyi.report.service;
+
+import com.ruoyi.report.dto.NormalDistributionDto;
+import com.ruoyi.report.vo.NormalDistributionVo;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+/**
+ * 姝f�佸垎甯冨浘鏈嶅姟鎺ュ彛
+ */
+public interface NormalDistributionService {
+
+    /**
+     * 姝f�佸垎甯冨垎鏋�
+     */
+    NormalDistributionVo analyze(NormalDistributionDto dto);
+
+    /**
+     * 瀵煎嚭鍒嗘瀽鏁版嵁
+     */
+    void export(NormalDistributionDto dto, HttpServletResponse response);
+
+    /**
+     * 鏌ヨ鍙�夋娴嬮」
+     */
+    List<String> getItemNames(NormalDistributionDto dto);
+
+    /**
+     * 鏌ヨ鍙�夋牱鍝佸悕绉板垪琛�
+     */
+    List<String> getSampleNames(NormalDistributionDto dto);
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/service/PassRateService.java b/report-server/src/main/java/com/ruoyi/report/service/PassRateService.java
new file mode 100644
index 0000000..3c7c01a
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/service/PassRateService.java
@@ -0,0 +1,39 @@
+package com.ruoyi.report.service;
+
+import com.ruoyi.report.dto.PassRateDto;
+import com.ruoyi.report.vo.ParetoVo;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 鍚堟牸鐜囩粺璁℃湇鍔℃帴鍙�
+ */
+public interface PassRateService {
+
+    /**
+     * 鍘熸潗鏂欏悎鏍肩巼
+     */
+    List<Map<String, Object>> getRawMaterialPassRate(PassRateDto dto);
+
+    /**
+     * 渚涘簲鍟嗕笉鍚堟牸缁熻
+     */
+    List<Map<String, Object>> getSupplierUnqualified(PassRateDto dto);
+
+    /**
+     * 甯曠疮鎵樺浘鏁版嵁
+     */
+    ParetoVo getPareto(PassRateDto dto);
+
+    /**
+     * 宸ュ簭鍚堟牸鐜�
+     */
+    List<Map<String, Object>> getProcessPassRate(PassRateDto dto);
+
+    /**
+     * 鏈哄彴涓嶅悎鏍肩粺璁�
+     */
+    List<Map<String, Object>> getMachineUnqualified(PassRateDto dto);
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/service/ReportDeviceRecordService.java b/report-server/src/main/java/com/ruoyi/report/service/ReportDeviceRecordService.java
new file mode 100644
index 0000000..bf12896
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/service/ReportDeviceRecordService.java
@@ -0,0 +1,31 @@
+package com.ruoyi.report.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.report.dto.DeviceRecordDto;
+import com.ruoyi.report.vo.DeviceRecordVo;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 璁惧浣跨敤璁板綍鎶ヨ〃鏈嶅姟鎺ュ彛
+ */
+public interface ReportDeviceRecordService {
+
+    /**
+     * 鍒嗛〉鏌ヨ璁惧浣跨敤璁板綍
+     */
+    Page<DeviceRecordVo> pageDeviceRecord(Page page, DeviceRecordDto dto);
+
+    /**
+     * 璁惧浣跨敤缁熻
+     */
+    List<Map<String, Object>> getStatistics(DeviceRecordDto dto);
+
+    /**
+     * 瀵煎嚭璁板綍
+     */
+    void exportDeviceRecord(DeviceRecordDto dto, HttpServletResponse response);
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/service/SampleProgressService.java b/report-server/src/main/java/com/ruoyi/report/service/SampleProgressService.java
new file mode 100644
index 0000000..3091997
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/service/SampleProgressService.java
@@ -0,0 +1,35 @@
+package com.ruoyi.report.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.report.dto.SampleProgressDto;
+import com.ruoyi.report.vo.SampleProgressVo;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.Map;
+
+/**
+ * 鏍峰搧杩涘害鎶ヨ〃鏈嶅姟鎺ュ彛
+ */
+public interface SampleProgressService {
+
+    /**
+     * 鍒嗛〉鏌ヨ鏍峰搧杩涘害
+     */
+    Page<SampleProgressVo> pageSampleProgress(Page page, SampleProgressDto dto);
+
+    /**
+     * 鑾峰彇缁熻鏁版嵁
+     */
+    Map<String, Object> getStatistics(SampleProgressDto dto);
+
+    /**
+     * 瀵煎嚭鎶ヨ〃
+     */
+    void exportSampleProgress(SampleProgressDto dto, HttpServletResponse response);
+
+    /**
+     * 鑾峰彇鍥捐〃鏁版嵁
+     */
+    Map<String, Object> getChartData(SampleProgressDto dto);
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/service/SampleRecordService.java b/report-server/src/main/java/com/ruoyi/report/service/SampleRecordService.java
new file mode 100644
index 0000000..0b62e3f
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/service/SampleRecordService.java
@@ -0,0 +1,30 @@
+package com.ruoyi.report.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.report.dto.SampleRecordDto;
+import com.ruoyi.report.vo.SampleRecordVo;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+/**
+ * 鏍峰搧棰嗘牱璁板綍鏈嶅姟鎺ュ彛
+ */
+public interface SampleRecordService {
+
+    /**
+     * 鍒嗛〉鏌ヨ棰嗘牱璁板綍
+     */
+    Page<SampleRecordVo> pageSampleRecord(Page page, SampleRecordDto dto);
+
+    /**
+     * 鏌ヨ鏍峰搧娴佽浆璁板綍
+     */
+    List<SampleRecordVo> getFlowRecord(Long sampleId);
+
+    /**
+     * 瀵煎嚭璁板綍
+     */
+    void exportSampleRecord(SampleRecordDto dto, HttpServletResponse response);
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/service/SpcChartService.java b/report-server/src/main/java/com/ruoyi/report/service/SpcChartService.java
new file mode 100644
index 0000000..17aaccb
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/service/SpcChartService.java
@@ -0,0 +1,39 @@
+package com.ruoyi.report.service;
+
+import com.ruoyi.report.dto.SpcChartDto;
+import com.ruoyi.report.vo.SpcResultVo;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+/**
+ * SPC鎺у埗鍥炬湇鍔℃帴鍙�
+ */
+public interface SpcChartService {
+
+    /**
+     * SPC鍒嗘瀽
+     */
+    SpcResultVo analyze(SpcChartDto dto);
+
+    /**
+     * 鍒剁▼鑳藉姏鍒嗘瀽
+     */
+    SpcResultVo.Capability getCapability(SpcChartDto dto);
+
+    /**
+     * 瀵煎嚭鍒嗘瀽鏁版嵁
+     */
+    void export(SpcChartDto dto, HttpServletResponse response);
+
+    /**
+     * 鏌ヨ鍙�夋娴嬮」
+     */
+    List<String> getItemNames(SpcChartDto dto);
+
+    /**
+     * 鏌ヨ鍙�夋牱鍝佸悕绉板垪琛�
+     */
+    List<String> getSampleNames(SpcChartDto dto);
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/service/TestItemDataService.java b/report-server/src/main/java/com/ruoyi/report/service/TestItemDataService.java
new file mode 100644
index 0000000..71b642c
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/service/TestItemDataService.java
@@ -0,0 +1,41 @@
+package com.ruoyi.report.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.report.dto.TestItemDataDto;
+import com.ruoyi.report.vo.TestItemDataVo;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 妫�娴嬮」鐩暟鎹湇鍔℃帴鍙�
+ */
+public interface TestItemDataService {
+
+    /**
+     * 鍒嗛〉鏌ヨ妫�娴嬮」鐩暟鎹�
+     */
+    Page<TestItemDataVo> pageTestItemData(Page page, TestItemDataDto dto);
+
+    /**
+     * 鏌ヨ妫�娴嬮」鐩鎯�
+     */
+    List<TestItemDataVo> getDetail(Long sampleId);
+
+    /**
+     * 鏁版嵁妯悜姣旇緝
+     */
+    Map<String, Object> compare(TestItemDataDto dto);
+
+    /**
+     * 瀵煎嚭鏁版嵁
+     */
+    void exportTestItemData(TestItemDataDto dto, HttpServletResponse response);
+
+    /**
+     * 鏌ヨ妫�娴嬮」鍚嶇О鍒楄〃
+     */
+    List<String> getItemNames(TestItemDataDto dto);
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/service/WorkStatisticsService.java b/report-server/src/main/java/com/ruoyi/report/service/WorkStatisticsService.java
new file mode 100644
index 0000000..638f467
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/service/WorkStatisticsService.java
@@ -0,0 +1,29 @@
+package com.ruoyi.report.service;
+
+import com.ruoyi.report.dto.WorkStatisticsDto;
+import com.ruoyi.report.vo.WorkStatisticsVo;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 宸ヤ綔缁熻鏈嶅姟鎺ュ彛
+ */
+public interface WorkStatisticsService {
+
+    /**
+     * 鎸変汉鍛樼粺璁�
+     */
+    List<WorkStatisticsVo> getByUser(WorkStatisticsDto dto);
+
+    /**
+     * 鍙婃椂鐜囩粺璁�
+     */
+    List<Map<String, Object>> getTimelyRate(WorkStatisticsDto dto);
+
+    /**
+     * 宸ヤ綔瓒嬪娍鍥�
+     */
+    Map<String, Object> getTrend(WorkStatisticsDto dto);
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/service/impl/DashboardServiceImpl.java b/report-server/src/main/java/com/ruoyi/report/service/impl/DashboardServiceImpl.java
new file mode 100644
index 0000000..16f0406
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/service/impl/DashboardServiceImpl.java
@@ -0,0 +1,107 @@
+package com.ruoyi.report.service.impl;
+
+import com.ruoyi.report.dto.DashboardDto;
+import com.ruoyi.report.mapper.DashboardMapper;
+import com.ruoyi.report.service.DashboardService;
+import com.ruoyi.report.vo.DashboardOverviewVo;
+import com.ruoyi.report.vo.RankingVo;
+import com.ruoyi.report.vo.TaskCalendarVo;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+
+/**
+ * 鏁板瓧鍖栬闊崇湅鏉挎湇鍔″疄鐜�
+ */
+@Service
+@AllArgsConstructor
+public class DashboardServiceImpl implements DashboardService {
+
+    private DashboardMapper dashboardMapper;
+
+    @Override
+    public DashboardOverviewVo getOverview(DashboardDto dto) {
+        DashboardOverviewVo vo = new DashboardOverviewVo();
+
+        // 寰呭鐞嗙粺璁�
+        vo.setWaitReceive(dashboardMapper.getWaitReceive());
+        vo.setWaitInspection(dashboardMapper.getWaitInspection());
+        vo.setWaitAudit(dashboardMapper.getWaitAudit());
+        vo.setWaitReport(dashboardMapper.getWaitReport());
+
+        // 浠婃棩缁熻
+        vo.setTodayNewSample(dashboardMapper.getTodayNewSample());
+        vo.setTodayFinished(dashboardMapper.getTodayFinished());
+
+        // 杩�30澶╂楠岀粨鏋�
+        vo.setRawMaterialResult(dashboardMapper.getInsResultByDays(30, "1"));
+        vo.setSemiFinishedResult(dashboardMapper.getInsResultByDays(30, "2"));
+        vo.setFinishedProductResult(dashboardMapper.getInsResultByDays(30, "3"));
+
+        return vo;
+    }
+
+    @Override
+    public List<TaskCalendarVo> getHistory15Days(DashboardDto dto) {
+        return dashboardMapper.getHistoryDays(15, dto);
+    }
+
+    @Override
+    public List<TaskCalendarVo> getFuture15Days(DashboardDto dto) {
+        return dashboardMapper.getFutureDays(15, dto);
+    }
+
+    @Override
+    public List<RankingVo> getRanking(DashboardDto dto) {
+        // 鍚堝苟鍘熷璁板綍鎺掕鍜屾姤鍛婃帓琛�
+        List<RankingVo> originalRanking = dashboardMapper.getOriginalRecordRanking(dto);
+        List<RankingVo> reportRanking = dashboardMapper.getReportRanking(dto);
+
+        Map<Long, RankingVo> rankingMap = new HashMap<>();
+
+        // 澶勭悊鍘熷璁板綍鎺掕
+        for (int i = 0; i < originalRanking.size(); i++) {
+            RankingVo vo = originalRanking.get(i);
+            vo.setRank(i + 1);
+            vo.setFinishCount(vo.getSubmitCount());
+            rankingMap.put(vo.getUserId(), vo);
+        }
+
+        // 澶勭悊鎶ュ憡鎺掕
+        for (RankingVo report : reportRanking) {
+            RankingVo existing = rankingMap.get(report.getUserId());
+            if (existing != null) {
+                existing.setFinishCount(existing.getFinishCount() + report.getSubmitCount());
+            } else {
+                report.setRank(0);
+                report.setFinishCount(report.getSubmitCount());
+                rankingMap.put(report.getUserId(), report);
+            }
+        }
+
+        // 鎺掑簭骞惰缃帓鍚�
+        List<RankingVo> result = new ArrayList<>(rankingMap.values());
+        result.sort((a, b) -> b.getFinishCount().compareTo(a.getFinishCount()));
+        for (int i = 0; i < result.size(); i++) {
+            result.get(i).setRank(i + 1);
+        }
+
+        return result.size() > 10 ? result.subList(0, 10) : result;
+    }
+
+    @Override
+    public Map<String, Object> getInsResult(DashboardDto dto) {
+        Map<String, Object> result = new HashMap<>();
+        result.put("rawMaterial", dashboardMapper.getInsResultByDays(30, "1"));
+        result.put("semiFinished", dashboardMapper.getInsResultByDays(30, "2"));
+        result.put("finishedProduct", dashboardMapper.getInsResultByDays(30, "3"));
+        return result;
+    }
+
+    @Override
+    public List<Map<String, Object>> getVoiceQueue() {
+        return dashboardMapper.getVoiceQueue();
+    }
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/service/impl/NormalDistributionServiceImpl.java b/report-server/src/main/java/com/ruoyi/report/service/impl/NormalDistributionServiceImpl.java
new file mode 100644
index 0000000..7f82fcb
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/service/impl/NormalDistributionServiceImpl.java
@@ -0,0 +1,193 @@
+package com.ruoyi.report.service.impl;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.date.DateUtil;
+import com.alibaba.excel.EasyExcel;
+import com.ruoyi.framework.exception.ErrorException;
+import com.ruoyi.report.dto.NormalDistributionDto;
+import com.ruoyi.report.mapper.NormalDistributionMapper;
+import com.ruoyi.report.service.NormalDistributionService;
+import com.ruoyi.report.vo.NormalDistributionVo;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.MathContext;
+import java.math.RoundingMode;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 姝f�佸垎甯冨浘鏈嶅姟瀹炵幇
+ */
+@Service
+@AllArgsConstructor
+public class NormalDistributionServiceImpl implements NormalDistributionService {
+
+    private NormalDistributionMapper normalDistributionMapper;
+
+    @Override
+    public NormalDistributionVo analyze(NormalDistributionDto dto) {
+        // 鏌ヨ鏁版嵁
+        List<Map<String, Object>> itemData = normalDistributionMapper.getItemData(dto);
+
+        if (CollectionUtil.isEmpty(itemData)) {
+            return null;
+        }
+
+        if (itemData.size() < 10) {
+            throw new ErrorException("鏁版嵁閲忎笉瓒筹紝鑷冲皯闇�瑕�10涓暟鎹偣");
+        }
+
+        // 鑾峰彇鏁板�煎垪琛�
+        List<BigDecimal> values = itemData.stream()
+                .map(m -> new BigDecimal((String) m.get("lastValue")))
+                .sorted()
+                .collect(Collectors.toList());
+
+        // 璁$畻缁熻閲�
+        BigDecimal min = values.get(0);
+        BigDecimal max = values.get(values.size() - 1);
+        BigDecimal sum = values.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
+        BigDecimal mean = sum.divide(new BigDecimal(values.size()), 10, RoundingMode.HALF_UP);
+
+        // 璁$畻鏍囧噯宸�
+        BigDecimal variance = BigDecimal.ZERO;
+        for (BigDecimal v : values) {
+            BigDecimal diff = v.subtract(mean);
+            variance = variance.add(diff.multiply(diff));
+        }
+        variance = variance.divide(new BigDecimal(values.size() - 1), 10, RoundingMode.HALF_UP);
+        BigDecimal stdDev = sqrt(variance).setScale(6, RoundingMode.HALF_UP);
+
+        // 鐩存柟鍥惧垎缁�
+        int binCount = dto.getBinCount() != null ? dto.getBinCount() : 10;
+        BigDecimal range = max.subtract(min);
+        BigDecimal binWidth = range.divide(new BigDecimal(binCount), 10, RoundingMode.HALF_UP);
+
+        // 璁$畻鍒嗙粍杈圭晫
+        List<BigDecimal> binEdges = new ArrayList<>();
+        BigDecimal edge = min;
+        for (int i = 0; i <= binCount; i++) {
+            binEdges.add(edge.setScale(4, RoundingMode.HALF_UP));
+            edge = edge.add(binWidth);
+        }
+
+        // 璁$畻棰戞暟
+        List<Integer> frequencies = new ArrayList<>();
+        for (int i = 0; i < binCount; i++) {
+            BigDecimal lower = binEdges.get(i);
+            BigDecimal upper = binEdges.get(i + 1);
+            int count = 0;
+            for (BigDecimal v : values) {
+                if (v.compareTo(lower) >= 0 && (i == binCount - 1 ? v.compareTo(upper) <= 0 : v.compareTo(upper) < 0)) {
+                    count++;
+                }
+            }
+            frequencies.add(count);
+        }
+
+        // 姝f�佸垎甯冩洸绾�
+        List<BigDecimal> normalX = new ArrayList<>();
+        List<BigDecimal> normalY = new ArrayList<>();
+
+        // 鐢熸垚鏇茬嚎鐐�
+        BigDecimal step = range.divide(new BigDecimal(100), 10, RoundingMode.HALF_UP);
+        BigDecimal x = min;
+        for (int i = 0; i <= 100; i++) {
+            normalX.add(x.setScale(4, RoundingMode.HALF_UP));
+
+            // 姝f�佸垎甯冨叕寮�: f(x) = (1/(蟽鈭�(2蟺))) * e^(-(x-渭)^2/(2蟽^2))
+            BigDecimal exponent = x.subtract(mean).pow(2)
+                    .divide(stdDev.pow(2).multiply(new BigDecimal(2)), 10, RoundingMode.HALF_UP);
+            BigDecimal expValue = BigDecimal.valueOf(Math.exp(-exponent.doubleValue()));
+            BigDecimal coefficient = BigDecimal.ONE.divide(
+                    stdDev.multiply(BigDecimal.valueOf(Math.sqrt(2 * Math.PI))),
+                    10, RoundingMode.HALF_UP);
+            BigDecimal y = coefficient.multiply(expValue)
+                    .multiply(new BigDecimal(values.size()))
+                    .multiply(binWidth)
+                    .setScale(4, RoundingMode.HALF_UP);
+            normalY.add(y);
+
+            x = x.add(step);
+        }
+
+        // 鏋勫缓缁撴灉
+        NormalDistributionVo vo = new NormalDistributionVo();
+        vo.setBinEdges(binEdges);
+        vo.setFrequencies(frequencies);
+        vo.setNormalX(normalX);
+        vo.setNormalY(normalY);
+        vo.setMean(mean.setScale(4, RoundingMode.HALF_UP));
+        vo.setStdDev(stdDev.setScale(4, RoundingMode.HALF_UP));
+        vo.setMin(min.setScale(4, RoundingMode.HALF_UP));
+        vo.setMax(max.setScale(4, RoundingMode.HALF_UP));
+        vo.setSampleSize(values.size());
+
+        return vo;
+    }
+
+    @Override
+    public void export(NormalDistributionDto dto, HttpServletResponse response) {
+        List<Map<String, Object>> itemData = normalDistributionMapper.getItemData(dto);
+
+        if (CollectionUtil.isEmpty(itemData)) {
+            return;
+        }
+
+        try {
+            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+            response.setCharacterEncoding("utf-8");
+            String fileName = "姝f�佸垎甯冨垎鏋愭暟鎹甠" + DateUtil.format(new Date(), "yyyyMMddHHmmss");
+            response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
+
+            // 鏋勫缓瀵煎嚭鏁版嵁
+            List<Map<String, Object>> exportData = new ArrayList<>();
+            for (Map<String, Object> item : itemData) {
+                Map<String, Object> row = new HashMap<>();
+                row.put("sampleCode", item.get("sampleCode"));
+                row.put("sampleName", item.get("sampleName"));
+                row.put("itemName", item.get("itemName"));
+                row.put("lastValue", item.get("lastValue"));
+                row.put("insTime", item.get("insTime"));
+                exportData.add(row);
+            }
+
+            EasyExcel.write(response.getOutputStream())
+                    .sheet("姝f�佸垎甯冨垎鏋愭暟鎹�")
+                    .doWrite(exportData);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public List<String> getItemNames(NormalDistributionDto dto) {
+        return normalDistributionMapper.getItemNames(dto);
+    }
+
+    @Override
+    public List<String> getSampleNames(NormalDistributionDto dto) {
+        return normalDistributionMapper.getSampleNames(dto);
+    }
+
+    /**
+     * 骞虫柟鏍硅绠�
+     */
+    private BigDecimal sqrt(BigDecimal value) {
+        BigDecimal x = value;
+        BigDecimal tolerance = new BigDecimal("1E-10");
+        BigDecimal guess = value.divide(BigDecimal.valueOf(2), MathContext.DECIMAL128);
+
+        while (x.subtract(guess).abs().compareTo(tolerance) > 0) {
+            x = guess;
+            guess = x.add(value.divide(x, MathContext.DECIMAL128)).divide(new BigDecimal("2"), MathContext.DECIMAL128);
+        }
+
+        return guess;
+    }
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/service/impl/PassRateServiceImpl.java b/report-server/src/main/java/com/ruoyi/report/service/impl/PassRateServiceImpl.java
new file mode 100644
index 0000000..2015f9c
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/service/impl/PassRateServiceImpl.java
@@ -0,0 +1,122 @@
+package com.ruoyi.report.service.impl;
+
+import cn.hutool.core.date.DateUtil;
+import com.ruoyi.report.dto.PassRateDto;
+import com.ruoyi.report.mapper.PassRateMapper;
+import com.ruoyi.report.service.PassRateService;
+import com.ruoyi.report.vo.ParetoVo;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.*;
+
+/**
+ * 鍚堟牸鐜囩粺璁℃湇鍔″疄鐜�
+ */
+@Service
+@AllArgsConstructor
+public class PassRateServiceImpl implements PassRateService {
+
+    private PassRateMapper passRateMapper;
+
+    @Override
+    public List<Map<String, Object>> getRawMaterialPassRate(PassRateDto dto) {
+        processDateType(dto);
+        return passRateMapper.getRawMaterialPassRate(dto);
+    }
+
+    @Override
+    public List<Map<String, Object>> getSupplierUnqualified(PassRateDto dto) {
+        processDateType(dto);
+        return passRateMapper.getSupplierUnqualified(dto);
+    }
+
+    @Override
+    public ParetoVo getPareto(PassRateDto dto) {
+        processDateType(dto);
+
+        List<Map<String, Object>> stats = passRateMapper.getUnqualifiedItemStats(dto);
+
+        ParetoVo vo = new ParetoVo();
+        List<String> categories = new ArrayList<>();
+        List<Integer> values = new ArrayList<>();
+        List<Double> cumulativePercent = new ArrayList<>();
+
+        if (stats.isEmpty()) {
+            vo.setCategories(categories);
+            vo.setValues(values);
+            vo.setCumulativePercent(cumulativePercent);
+            return vo;
+        }
+
+        // 璁$畻鎬绘暟
+        int total = stats.stream()
+                .mapToInt(m -> ((Number) m.get("unqualifiedCount")).intValue())
+                .sum();
+
+        // 璁$畻绱鐧惧垎姣�
+        BigDecimal cumulative = BigDecimal.ZERO;
+        for (Map<String, Object> stat : stats) {
+            categories.add((String) stat.get("itemName"));
+            int count = ((Number) stat.get("unqualifiedCount")).intValue();
+            values.add(count);
+
+            cumulative = cumulative.add(new BigDecimal(count));
+            double percent = cumulative.divide(new BigDecimal(total), 4, RoundingMode.HALF_UP)
+                    .multiply(new BigDecimal(100))
+                    .setScale(2, RoundingMode.HALF_UP)
+                    .doubleValue();
+            cumulativePercent.add(percent);
+        }
+
+        vo.setCategories(categories);
+        vo.setValues(values);
+        vo.setCumulativePercent(cumulativePercent);
+        return vo;
+    }
+
+    @Override
+    public List<Map<String, Object>> getProcessPassRate(PassRateDto dto) {
+        processDateType(dto);
+        return passRateMapper.getProcessPassRate(dto);
+    }
+
+    @Override
+    public List<Map<String, Object>> getMachineUnqualified(PassRateDto dto) {
+        processDateType(dto);
+        return passRateMapper.getMachineUnqualified(dto);
+    }
+
+    /**
+     * 澶勭悊鏃堕棿绫诲瀷
+     */
+    private void processDateType(PassRateDto dto) {
+        if (dto.getStartTime() != null && dto.getEndTime() != null) {
+            return;
+        }
+
+        String dateType = dto.getDateType();
+        if (dateType == null || dateType.isEmpty()) {
+            dateType = "2"; // 榛樿鏈湀
+        }
+
+        Date now = new Date();
+        switch (dateType) {
+            case "1": // 鏈懆
+                dto.setStartTime(DateUtil.format(DateUtil.beginOfWeek(now), "yyyy-MM-dd HH:mm:ss"));
+                dto.setEndTime(DateUtil.format(DateUtil.endOfWeek(now), "yyyy-MM-dd HH:mm:ss"));
+                break;
+            case "2": // 鏈湀
+                dto.setStartTime(DateUtil.format(DateUtil.beginOfMonth(now), "yyyy-MM-dd HH:mm:ss"));
+                dto.setEndTime(DateUtil.format(DateUtil.endOfMonth(now), "yyyy-MM-dd HH:mm:ss"));
+                break;
+            case "3": // 鏈勾
+                dto.setStartTime(DateUtil.format(DateUtil.beginOfYear(now), "yyyy-MM-dd HH:mm:ss"));
+                dto.setEndTime(DateUtil.format(DateUtil.endOfYear(now), "yyyy-MM-dd HH:mm:ss"));
+                break;
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/service/impl/ReportDeviceRecordServiceImpl.java b/report-server/src/main/java/com/ruoyi/report/service/impl/ReportDeviceRecordServiceImpl.java
new file mode 100644
index 0000000..9e8249c
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/service/impl/ReportDeviceRecordServiceImpl.java
@@ -0,0 +1,61 @@
+package com.ruoyi.report.service.impl;
+
+import cn.hutool.core.date.DateUtil;
+import com.alibaba.excel.EasyExcel;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.report.dto.DeviceRecordDto;
+import com.ruoyi.report.mapper.ReportDeviceRecordMapper;
+import com.ruoyi.report.service.ReportDeviceRecordService;
+import com.ruoyi.report.vo.DeviceRecordVo;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * 璁惧浣跨敤璁板綍鎶ヨ〃鏈嶅姟瀹炵幇
+ */
+@Service
+@AllArgsConstructor
+public class ReportDeviceRecordServiceImpl implements ReportDeviceRecordService {
+
+    private ReportDeviceRecordMapper reportDeviceRecordMapper;
+
+    @Override
+    public Page<DeviceRecordVo> pageDeviceRecord(Page page, DeviceRecordDto dto) {
+        return reportDeviceRecordMapper.pageDeviceRecord(page, dto);
+    }
+
+    @Override
+    public List<Map<String, Object>> getStatistics(DeviceRecordDto dto) {
+        return reportDeviceRecordMapper.getStatistics(dto);
+    }
+
+    @Override
+    public void exportDeviceRecord(DeviceRecordDto dto, HttpServletResponse response) {
+        try {
+            // 鏌ヨ鍏ㄩ儴鏁版嵁
+            Page<DeviceRecordVo> page = new Page<>();
+            page.setSize(Long.MAX_VALUE);
+            Page<DeviceRecordVo> result = reportDeviceRecordMapper.pageDeviceRecord(page, dto);
+
+            List<DeviceRecordVo> records = result.getRecords();
+
+            // 璁剧疆鍝嶅簲澶�
+            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+            response.setCharacterEncoding("utf-8");
+            String fileName = "璁惧浣跨敤璁板綍_" + DateUtil.format(new Date(), "yyyyMMddHHmmss");
+            response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
+
+            // 浣跨敤EasyExcel瀵煎嚭
+            EasyExcel.write(response.getOutputStream(), DeviceRecordVo.class)
+                    .sheet("璁惧浣跨敤璁板綍")
+                    .doWrite(records);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/service/impl/SampleProgressServiceImpl.java b/report-server/src/main/java/com/ruoyi/report/service/impl/SampleProgressServiceImpl.java
new file mode 100644
index 0000000..d1c9d37
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/service/impl/SampleProgressServiceImpl.java
@@ -0,0 +1,125 @@
+package com.ruoyi.report.service.impl;
+
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.StrUtil;
+import com.alibaba.excel.EasyExcel;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.report.dto.SampleProgressDto;
+import com.ruoyi.report.mapper.SampleProgressMapper;
+import com.ruoyi.report.service.SampleProgressService;
+import com.ruoyi.report.vo.SampleProgressVo;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 鏍峰搧杩涘害鎶ヨ〃鏈嶅姟瀹炵幇
+ */
+@Service
+@AllArgsConstructor
+public class SampleProgressServiceImpl implements SampleProgressService {
+
+    private SampleProgressMapper sampleProgressMapper;
+
+    @Override
+    public Page<SampleProgressVo> pageSampleProgress(Page page, SampleProgressDto dto) {
+        Page<SampleProgressVo> result = sampleProgressMapper.pageSampleProgress(page, dto);
+        // 澶勭悊鐘舵�佸悕绉板拰杩涘害鐧惧垎姣�
+        List<SampleProgressVo> records = result.getRecords();
+        for (SampleProgressVo vo : records) {
+            vo.setInsStateName(formatInsState(vo.getInsState()));
+            if (vo.getTotalItems() != null && vo.getTotalItems() > 0) {
+                int finished = vo.getFinishedItems() != null ? vo.getFinishedItems() : 0;
+                vo.setProgressPercent((finished * 100.0) / vo.getTotalItems());
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public Map<String, Object> getStatistics(SampleProgressDto dto) {
+        Map<String, Object> statistics = sampleProgressMapper.getStatistics(dto);
+        if (statistics == null) {
+            statistics = new HashMap<>();
+            statistics.put("waitInspection", 0);
+            statistics.put("inspecting", 0);
+            statistics.put("waitAudit", 0);
+            statistics.put("finished", 0);
+        }
+        return statistics;
+    }
+
+    @Override
+    public void exportSampleProgress(SampleProgressDto dto, HttpServletResponse response) {
+        try {
+            // 鏌ヨ鍏ㄩ儴鏁版嵁
+            Page<SampleProgressVo> page = new Page<>();
+            page.setSize(Long.MAX_VALUE);
+            Page<SampleProgressVo> result = sampleProgressMapper.pageSampleProgress(page, dto);
+
+            // 澶勭悊鏁版嵁
+            List<SampleProgressVo> records = result.getRecords();
+            for (SampleProgressVo vo : records) {
+                vo.setInsStateName(formatInsState(vo.getInsState()));
+                if (vo.getTotalItems() != null && vo.getTotalItems() > 0) {
+                    int finished = vo.getFinishedItems() != null ? vo.getFinishedItems() : 0;
+                    vo.setProgressPercent((finished * 100.0) / vo.getTotalItems());
+                }
+            }
+
+            // 璁剧疆鍝嶅簲澶�
+            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+            response.setCharacterEncoding("utf-8");
+            String fileName = "鏍峰搧杩涘害鎶ヨ〃_" + DateUtil.format(new Date(), "yyyyMMddHHmmss");
+            response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
+
+            // 浣跨敤EasyExcel瀵煎嚭
+            EasyExcel.write(response.getOutputStream(), SampleProgressVo.class)
+                    .sheet("鏍峰搧杩涘害")
+                    .doWrite(records);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public Map<String, Object> getChartData(SampleProgressDto dto) {
+        List<Map<String, Object>> chartData = sampleProgressMapper.getChartData(dto);
+
+        Map<String, Object> result = new HashMap<>();
+        List<String> dates = chartData.stream()
+                .map(m -> (String) m.get("date"))
+                .collect(Collectors.toList());
+        List<Integer> totalCounts = chartData.stream()
+                .map(m -> ((Number) m.get("totalCount")).intValue())
+                .collect(Collectors.toList());
+        List<Integer> finishedCounts = chartData.stream()
+                .map(m -> ((Number) m.get("finishedCount")).intValue())
+                .collect(Collectors.toList());
+
+        result.put("dates", dates);
+        result.put("totalCounts", totalCounts);
+        result.put("finishedCounts", finishedCounts);
+        return result;
+    }
+
+    /**
+     * 鏍煎紡鍖栨娴嬬姸鎬�
+     */
+    private String formatInsState(Integer val) {
+        if (val == null) return "";
+        Map<Integer, String> map = new HashMap<>();
+        map.put(0, "寰呮");
+        map.put(1, "妫�楠屼腑");
+        map.put(2, "宸叉楠�");
+        map.put(3, "寰呭鏍�");
+        map.put(4, "瀹℃牳鏈�氳繃");
+        map.put(5, "瀹℃牳閫氳繃");
+        return map.getOrDefault(val, "");
+    }
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/service/impl/SampleRecordServiceImpl.java b/report-server/src/main/java/com/ruoyi/report/service/impl/SampleRecordServiceImpl.java
new file mode 100644
index 0000000..0a77cc8
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/service/impl/SampleRecordServiceImpl.java
@@ -0,0 +1,89 @@
+package com.ruoyi.report.service.impl;
+
+import cn.hutool.core.date.DateUtil;
+import com.alibaba.excel.EasyExcel;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.report.dto.SampleRecordDto;
+import com.ruoyi.report.mapper.SampleRecordMapper;
+import com.ruoyi.report.service.SampleRecordService;
+import com.ruoyi.report.vo.SampleRecordVo;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * 鏍峰搧棰嗘牱璁板綍鏈嶅姟瀹炵幇
+ */
+@Service
+@AllArgsConstructor
+public class SampleRecordServiceImpl implements SampleRecordService {
+
+    private SampleRecordMapper sampleRecordMapper;
+
+    @Override
+    public Page<SampleRecordVo> pageSampleRecord(Page page, SampleRecordDto dto) {
+        Page<SampleRecordVo> result = sampleRecordMapper.pageSampleRecord(page, dto);
+        // 澶勭悊鎿嶄綔绫诲瀷鍚嶇О
+        List<SampleRecordVo> records = result.getRecords();
+        for (SampleRecordVo vo : records) {
+            vo.setOperateTypeName(formatOperateType(vo.getOperateType()));
+        }
+        return result;
+    }
+
+    @Override
+    public List<SampleRecordVo> getFlowRecord(Long sampleId) {
+        List<SampleRecordVo> list = sampleRecordMapper.getFlowRecord(sampleId);
+        for (SampleRecordVo vo : list) {
+            vo.setOperateTypeName(formatOperateType(vo.getOperateType()));
+        }
+        return list;
+    }
+
+    @Override
+    public void exportSampleRecord(SampleRecordDto dto, HttpServletResponse response) {
+        try {
+            // 鏌ヨ鍏ㄩ儴鏁版嵁
+            Page<SampleRecordVo> page = new Page<>();
+            page.setSize(Long.MAX_VALUE);
+            Page<SampleRecordVo> result = sampleRecordMapper.pageSampleRecord(page, dto);
+
+            // 澶勭悊鏁版嵁
+            List<SampleRecordVo> records = result.getRecords();
+            for (SampleRecordVo vo : records) {
+                vo.setOperateTypeName(formatOperateType(vo.getOperateType()));
+            }
+
+            // 璁剧疆鍝嶅簲澶�
+            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+            response.setCharacterEncoding("utf-8");
+            String fileName = "鏍峰搧棰嗘牱璁板綍_" + DateUtil.format(new Date(), "yyyyMMddHHmmss");
+            response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
+
+            // 浣跨敤EasyExcel瀵煎嚭
+            EasyExcel.write(response.getOutputStream(), SampleRecordVo.class)
+                    .sheet("鏍峰搧棰嗘牱璁板綍")
+                    .doWrite(records);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 鏍煎紡鍖栨搷浣滅被鍨�
+     */
+    private String formatOperateType(String val) {
+        if (val == null) return "";
+        Map<String, String> map = new HashMap<>();
+        map.put("in", "鍏ュ簱");
+        map.put("out", "鍑哄簱");
+        map.put("move", "绉诲簱");
+        map.put("receive", "棰嗙敤");
+        map.put("return", "褰掕繕");
+        return map.getOrDefault(val, val);
+    }
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/service/impl/SpcChartServiceImpl.java b/report-server/src/main/java/com/ruoyi/report/service/impl/SpcChartServiceImpl.java
new file mode 100644
index 0000000..f7e16f3
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/service/impl/SpcChartServiceImpl.java
@@ -0,0 +1,267 @@
+package com.ruoyi.report.service.impl;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.date.DateUtil;
+import com.alibaba.excel.EasyExcel;
+import com.ruoyi.framework.exception.ErrorException;
+import com.ruoyi.report.dto.SpcChartDto;
+import com.ruoyi.report.mapper.SpcChartMapper;
+import com.ruoyi.report.service.SpcChartService;
+import com.ruoyi.report.vo.SpcResultVo;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.MathContext;
+import java.math.RoundingMode;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * SPC鎺у埗鍥炬湇鍔″疄鐜�
+ */
+@Service
+@AllArgsConstructor
+public class SpcChartServiceImpl implements SpcChartService {
+
+    private SpcChartMapper spcChartMapper;
+
+    @Override
+    public SpcResultVo analyze(SpcChartDto dto) {
+        // 鏌ヨ鏁版嵁
+        List<Map<String, Object>> itemData = spcChartMapper.getItemData(dto);
+
+        if (CollectionUtil.isEmpty(itemData)) {
+            return null;
+        }
+
+        // 鑾峰彇鏁板�煎垪琛�
+        List<BigDecimal> values = itemData.stream()
+                .map(m -> new BigDecimal((String) m.get("lastValue")))
+                .collect(Collectors.toList());
+
+        // 瀛愮粍澶у皬锛岄粯璁や负5
+        int subgroupSize = dto.getSubgroupSize() != null ? dto.getSubgroupSize() : 5;
+
+        // 鍒嗙粍
+        List<List<BigDecimal>> subgroups = new ArrayList<>();
+        for (int i = 0; i < values.size(); i += subgroupSize) {
+            int end = Math.min(i + subgroupSize, values.size());
+            if (end - i >= 2) { // 鑷冲皯闇�瑕�2涓暟鎹偣
+                subgroups.add(values.subList(i, end));
+            }
+        }
+
+        if (subgroups.isEmpty()) {
+            throw new ErrorException("鏁版嵁涓嶈冻浠ヨ繘琛孲PC鍒嗘瀽");
+        }
+
+        // 璁$畻X-bar鍜孯
+        List<BigDecimal> xBarData = new ArrayList<>();
+        List<BigDecimal> rData = new ArrayList<>();
+        List<String> sampleLabels = new ArrayList<>();
+
+        for (int i = 0; i < subgroups.size(); i++) {
+            List<BigDecimal> subgroup = subgroups.get(i);
+            BigDecimal sum = subgroup.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
+            BigDecimal mean = sum.divide(new BigDecimal(subgroup.size()), 10, RoundingMode.HALF_UP);
+            BigDecimal max = subgroup.stream().max(BigDecimal::compareTo).orElse(BigDecimal.ZERO);
+            BigDecimal min = subgroup.stream().min(BigDecimal::compareTo).orElse(BigDecimal.ZERO);
+            BigDecimal range = max.subtract(min);
+
+            xBarData.add(mean.setScale(4, RoundingMode.HALF_UP));
+            rData.add(range.setScale(4, RoundingMode.HALF_UP));
+            sampleLabels.add("缁�" + (i + 1));
+        }
+
+        // 璁$畻鎺у埗闄�
+        BigDecimal xBarMean = xBarData.stream().reduce(BigDecimal.ZERO, BigDecimal::add)
+                .divide(new BigDecimal(xBarData.size()), 10, RoundingMode.HALF_UP);
+        BigDecimal rMean = rData.stream().reduce(BigDecimal.ZERO, BigDecimal::add)
+                .divide(new BigDecimal(rData.size()), 10, RoundingMode.HALF_UP);
+
+        // A2, D3, D4 甯告暟 (閽堝瀛愮粍澶у皬)
+        BigDecimal A2 = getA2(subgroupSize);
+        BigDecimal D3 = getD3(subgroupSize);
+        BigDecimal D4 = getD4(subgroupSize);
+
+        // X-bar 鎺у埗闄�
+        BigDecimal xBarUcl = dto.getUcl() != null ? dto.getUcl() :
+                xBarMean.add(A2.multiply(rMean)).setScale(4, RoundingMode.HALF_UP);
+        BigDecimal xBarLcl = dto.getLcl() != null ? dto.getLcl() :
+                xBarMean.subtract(A2.multiply(rMean)).setScale(4, RoundingMode.HALF_UP);
+
+        // R 鎺у埗闄�
+        BigDecimal rUcl = D4.multiply(rMean).setScale(4, RoundingMode.HALF_UP);
+        BigDecimal rLcl = D3.multiply(rMean).setScale(4, RoundingMode.HALF_UP);
+
+        // 鏋勫缓缁撴灉
+        SpcResultVo result = new SpcResultVo();
+
+        SpcResultVo.ChartData xBarChart = new SpcResultVo.ChartData();
+        xBarChart.setData(xBarData);
+        xBarChart.setUcl(xBarUcl);
+        xBarChart.setLcl(xBarLcl);
+        xBarChart.setCl(xBarMean.setScale(4, RoundingMode.HALF_UP));
+        xBarChart.setSampleLabels(sampleLabels);
+        result.setXBar(xBarChart);
+
+        SpcResultVo.ChartData rChart = new SpcResultVo.ChartData();
+        rChart.setData(rData);
+        rChart.setUcl(rUcl);
+        rChart.setLcl(rLcl);
+        rChart.setCl(rMean.setScale(4, RoundingMode.HALF_UP));
+        rChart.setSampleLabels(sampleLabels);
+        result.setRChart(rChart);
+
+        // 鍒剁▼鑳藉姏
+        SpcResultVo.Capability capability = calculateCapability(values, dto.getUcl(), dto.getLcl());
+        result.setCapability(capability);
+
+        return result;
+    }
+
+    @Override
+    public SpcResultVo.Capability getCapability(SpcChartDto dto) {
+        List<Map<String, Object>> itemData = spcChartMapper.getItemData(dto);
+
+        if (CollectionUtil.isEmpty(itemData)) {
+            return null;
+        }
+
+        List<BigDecimal> values = itemData.stream()
+                .map(m -> new BigDecimal((String) m.get("lastValue")))
+                .collect(Collectors.toList());
+
+        return calculateCapability(values, dto.getUcl(), dto.getLcl());
+    }
+
+    @Override
+    public void export(SpcChartDto dto, HttpServletResponse response) {
+        SpcResultVo result = analyze(dto);
+
+        if (result == null) {
+            return;
+        }
+
+        try {
+            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+            response.setCharacterEncoding("utf-8");
+            String fileName = "SPC鍒嗘瀽鏁版嵁_" + DateUtil.format(new Date(), "yyyyMMddHHmmss");
+            response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
+
+            // 鏋勫缓瀵煎嚭鏁版嵁
+            List<Map<String, Object>> exportData = new ArrayList<>();
+            List<String> labels = result.getXBar().getSampleLabels();
+            List<BigDecimal> xBarData = result.getXBar().getData();
+            List<BigDecimal> rData = result.getRChart().getData();
+
+            for (int i = 0; i < labels.size(); i++) {
+                Map<String, Object> row = new HashMap<>();
+                row.put("sampleLabel", labels.get(i));
+                row.put("xBar", xBarData.get(i));
+                row.put("r", rData.get(i));
+                exportData.add(row);
+            }
+
+            EasyExcel.write(response.getOutputStream())
+                    .sheet("SPC鍒嗘瀽鏁版嵁")
+                    .doWrite(exportData);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public List<String> getItemNames(SpcChartDto dto) {
+        return spcChartMapper.getItemNames(dto);
+    }
+
+    @Override
+    public List<String> getSampleNames(SpcChartDto dto) {
+        return spcChartMapper.getSampleNames(dto);
+    }
+
+    /**
+     * 璁$畻鍒剁▼鑳藉姏
+     */
+    private SpcResultVo.Capability calculateCapability(List<BigDecimal> values, BigDecimal ucl, BigDecimal lcl) {
+        if (ucl == null || lcl == null) {
+            return null;
+        }
+
+        SpcResultVo.Capability capability = new SpcResultVo.Capability();
+
+        // 璁$畻鍧囧�煎拰鏍囧噯宸�
+        BigDecimal sum = values.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
+        BigDecimal mean = sum.divide(new BigDecimal(values.size()), 10, RoundingMode.HALF_UP);
+
+        BigDecimal variance = BigDecimal.ZERO;
+        for (BigDecimal v : values) {
+            BigDecimal diff = v.subtract(mean);
+            variance = variance.add(diff.multiply(diff));
+        }
+        variance = variance.divide(new BigDecimal(values.size() - 1), 10, RoundingMode.HALF_UP);
+        BigDecimal stdDev = sqrt(variance);
+
+        // 璁$畻Cp, Cpk
+        BigDecimal USL = ucl;
+        BigDecimal LSL = lcl;
+        BigDecimal tolerance = USL.subtract(LSL);
+
+        BigDecimal cp = tolerance.divide(stdDev.multiply(new BigDecimal(6)), 4, RoundingMode.HALF_UP);
+        BigDecimal cpu = USL.subtract(mean).divide(stdDev.multiply(new BigDecimal(3)), 4, RoundingMode.HALF_UP);
+        BigDecimal cpl = mean.subtract(LSL).divide(stdDev.multiply(new BigDecimal(3)), 4, RoundingMode.HALF_UP);
+        BigDecimal cpk = cpu.min(cpl);
+
+        capability.setCp(cp);
+        capability.setCpk(cpk);
+        capability.setPp(cp); // 绠�鍖栧鐞�
+        capability.setPpk(cpk);
+
+        return capability;
+    }
+
+    /**
+     * 鑾峰彇A2甯告暟
+     */
+    private BigDecimal getA2(int n) {
+        double[] a2Values = {0, 1.880, 1.023, 0.729, 0.577, 0.483, 0.419, 0.373, 0.337, 0.308};
+        return new BigDecimal(a2Values[Math.min(n, 9)]);
+    }
+
+    /**
+     * 鑾峰彇D3甯告暟
+     */
+    private BigDecimal getD3(int n) {
+        double[] d3Values = {0, 0, 0, 0, 0, 0, 0.076, 0.136, 0.184, 0.223};
+        return new BigDecimal(d3Values[Math.min(n, 9)]);
+    }
+
+    /**
+     * 鑾峰彇D4甯告暟
+     */
+    private BigDecimal getD4(int n) {
+        double[] d4Values = {0, 3.267, 2.574, 2.282, 2.114, 2.004, 1.924, 1.864, 1.816, 1.777};
+        return new BigDecimal(d4Values[Math.min(n, 9)]);
+    }
+
+    /**
+     * 骞虫柟鏍硅绠�
+     */
+    private BigDecimal sqrt(BigDecimal value) {
+        BigDecimal x = value;
+        BigDecimal tolerance = new BigDecimal("1E-10");
+        BigDecimal guess = value.divide(BigDecimal.valueOf(2), MathContext.DECIMAL128);
+
+        while (x.subtract(guess).abs().compareTo(tolerance) > 0) {
+            x = guess;
+            guess = x.add(value.divide(x, MathContext.DECIMAL128)).divide(new BigDecimal("2"), MathContext.DECIMAL128);
+        }
+
+        return guess;
+    }
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/service/impl/TestItemDataServiceImpl.java b/report-server/src/main/java/com/ruoyi/report/service/impl/TestItemDataServiceImpl.java
new file mode 100644
index 0000000..5be9afe
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/service/impl/TestItemDataServiceImpl.java
@@ -0,0 +1,144 @@
+package com.ruoyi.report.service.impl;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.date.DateUtil;
+import com.alibaba.excel.EasyExcel;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.report.dto.TestItemDataDto;
+import com.ruoyi.report.mapper.TestItemDataMapper;
+import com.ruoyi.report.service.TestItemDataService;
+import com.ruoyi.report.vo.TestItemDataVo;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 妫�娴嬮」鐩暟鎹湇鍔″疄鐜�
+ */
+@Service
+@AllArgsConstructor
+public class TestItemDataServiceImpl implements TestItemDataService {
+
+    private TestItemDataMapper testItemDataMapper;
+
+    @Override
+    public Page<TestItemDataVo> pageTestItemData(Page page, TestItemDataDto dto) {
+        Page<TestItemDataVo> result = testItemDataMapper.pageTestItemData(page, dto);
+        // 澶勭悊妫�娴嬬粨鏋滃悕绉�
+        List<TestItemDataVo> records = result.getRecords();
+        for (TestItemDataVo vo : records) {
+            vo.setInsResultName(formatInsResult(vo.getInsResult()));
+        }
+        return result;
+    }
+
+    @Override
+    public List<TestItemDataVo> getDetail(Long sampleId) {
+        List<TestItemDataVo> list = testItemDataMapper.getDetail(sampleId);
+        for (TestItemDataVo vo : list) {
+            vo.setInsResultName(formatInsResult(vo.getInsResult()));
+        }
+        return list;
+    }
+
+    @Override
+    public Map<String, Object> compare(TestItemDataDto dto) {
+        if (CollectionUtils.isEmpty(dto.getSampleIds())) {
+            return new HashMap<>();
+        }
+
+        // 鏌ヨ鏁版嵁
+        List<TestItemDataVo> dataList = testItemDataMapper.listBySampleIds(dto.getSampleIds());
+
+        // 鑾峰彇鎵�鏈夋娴嬮」鍚嶇О
+        List<String> itemNames = dataList.stream()
+                .map(TestItemDataVo::getItemName)
+                .distinct()
+                .sorted()
+                .collect(Collectors.toList());
+
+        // 鎸夋牱鍝佸垎缁�
+        Map<Long, List<TestItemDataVo>> sampleMap = dataList.stream()
+                .collect(Collectors.groupingBy(TestItemDataVo::getSampleId));
+
+        // 鏋勫缓姣旇緝鏁版嵁
+        List<Map<String, Object>> compareList = new ArrayList<>();
+        for (Long sampleId : dto.getSampleIds()) {
+            Map<String, Object> row = new HashMap<>();
+            List<TestItemDataVo> sampleData = sampleMap.getOrDefault(sampleId, new ArrayList<>());
+
+            // 鑾峰彇鏍峰搧淇℃伅
+            if (CollectionUtil.isNotEmpty(sampleData)) {
+                TestItemDataVo first = sampleData.get(0);
+                row.put("sampleCode", first.getSampleCode());
+                row.put("sampleName", first.getSampleName());
+                row.put("batchNo", first.getBatchNo());
+            }
+
+            // 濉厖妫�娴嬮」鍊�
+            for (String itemName : itemNames) {
+                Optional<TestItemDataVo> match = sampleData.stream()
+                        .filter(v -> v.getItemName().equals(itemName))
+                        .findFirst();
+                row.put(itemName, match.map(TestItemDataVo::getLastValue).orElse(null));
+            }
+
+            compareList.add(row);
+        }
+
+        Map<String, Object> result = new HashMap<>();
+        result.put("itemNames", itemNames);
+        result.put("compareList", compareList);
+        return result;
+    }
+
+    @Override
+    public void exportTestItemData(TestItemDataDto dto, HttpServletResponse response) {
+        try {
+            // 鏌ヨ鍏ㄩ儴鏁版嵁
+            Page<TestItemDataVo> page = new Page<>();
+            page.setSize(Long.MAX_VALUE);
+            Page<TestItemDataVo> result = testItemDataMapper.pageTestItemData(page, dto);
+
+            // 澶勭悊鏁版嵁
+            List<TestItemDataVo> records = result.getRecords();
+            for (TestItemDataVo vo : records) {
+                vo.setInsResultName(formatInsResult(vo.getInsResult()));
+            }
+
+            // 璁剧疆鍝嶅簲澶�
+            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+            response.setCharacterEncoding("utf-8");
+            String fileName = "妫�娴嬮」鐩暟鎹甠" + DateUtil.format(new Date(), "yyyyMMddHHmmss");
+            response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
+
+            // 浣跨敤EasyExcel瀵煎嚭
+            EasyExcel.write(response.getOutputStream(), TestItemDataVo.class)
+                    .sheet("妫�娴嬮」鐩暟鎹�")
+                    .doWrite(records);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public List<String> getItemNames(TestItemDataDto dto) {
+        return testItemDataMapper.getItemNames(dto);
+    }
+
+    /**
+     * 鏍煎紡鍖栨娴嬬粨鏋�
+     */
+    private String formatInsResult(Integer val) {
+        if (val == null) return "";
+        if (val == 1) return "鍚堟牸";
+        if (val == 0) return "涓嶅悎鏍�";
+        return "";
+    }
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/service/impl/WorkStatisticsServiceImpl.java b/report-server/src/main/java/com/ruoyi/report/service/impl/WorkStatisticsServiceImpl.java
new file mode 100644
index 0000000..578fed3
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/service/impl/WorkStatisticsServiceImpl.java
@@ -0,0 +1,103 @@
+package com.ruoyi.report.service.impl;
+
+import cn.hutool.core.date.DateUtil;
+import com.ruoyi.report.dto.WorkStatisticsDto;
+import com.ruoyi.report.mapper.WorkStatisticsMapper;
+import com.ruoyi.report.service.WorkStatisticsService;
+import com.ruoyi.report.vo.WorkStatisticsVo;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+
+/**
+ * 宸ヤ綔缁熻鏈嶅姟瀹炵幇
+ */
+@Service
+@AllArgsConstructor
+public class WorkStatisticsServiceImpl implements WorkStatisticsService {
+
+    private WorkStatisticsMapper workStatisticsMapper;
+
+    @Override
+    public List<WorkStatisticsVo> getByUser(WorkStatisticsDto dto) {
+        // 澶勭悊鏃堕棿绫诲瀷
+        processDateType(dto);
+
+        List<WorkStatisticsVo> list = workStatisticsMapper.getByUser(dto);
+
+        // 璁$畻鍙婃椂鐜�
+        for (WorkStatisticsVo vo : list) {
+            int timely = vo.getTimelyCount() != null ? vo.getTimelyCount() : 0;
+            int overdue = vo.getOverdueCount() != null ? vo.getOverdueCount() : 0;
+            int total = timely + overdue;
+            if (total > 0) {
+                vo.setTimelyRate((timely * 100.0) / total);
+            } else {
+                vo.setTimelyRate(0.0);
+            }
+        }
+
+        return list;
+    }
+
+    @Override
+    public List<Map<String, Object>> getTimelyRate(WorkStatisticsDto dto) {
+        processDateType(dto);
+        return workStatisticsMapper.getTimelyRate(dto);
+    }
+
+    @Override
+    public Map<String, Object> getTrend(WorkStatisticsDto dto) {
+        processDateType(dto);
+
+        List<Map<String, Object>> trendData = workStatisticsMapper.getTrend(dto);
+
+        Map<String, Object> result = new HashMap<>();
+        List<String> dates = new ArrayList<>();
+        List<Integer> sampleCounts = new ArrayList<>();
+        List<Integer> itemCounts = new ArrayList<>();
+
+        for (Map<String, Object> item : trendData) {
+            dates.add((String) item.get("date"));
+            sampleCounts.add(((Number) item.get("sampleCount")).intValue());
+            itemCounts.add(((Number) item.get("itemCount")).intValue());
+        }
+
+        result.put("dates", dates);
+        result.put("sampleCounts", sampleCounts);
+        result.put("itemCounts", itemCounts);
+        return result;
+    }
+
+    /**
+     * 澶勭悊鏃堕棿绫诲瀷
+     */
+    private void processDateType(WorkStatisticsDto dto) {
+        if (dto.getStartTime() != null && dto.getEndTime() != null) {
+            return;
+        }
+
+        String dateType = dto.getDateType();
+        if (dateType == null || dateType.isEmpty()) {
+            dateType = "2"; // 榛樿鏈湀
+        }
+
+        Date now = new Date();
+        switch (dateType) {
+            case "1": // 鏈懆
+                dto.setStartTime(DateUtil.format(DateUtil.beginOfWeek(now), "yyyy-MM-dd HH:mm:ss"));
+                dto.setEndTime(DateUtil.format(DateUtil.endOfWeek(now), "yyyy-MM-dd HH:mm:ss"));
+                break;
+            case "2": // 鏈湀
+                dto.setStartTime(DateUtil.format(DateUtil.beginOfMonth(now), "yyyy-MM-dd HH:mm:ss"));
+                dto.setEndTime(DateUtil.format(DateUtil.endOfMonth(now), "yyyy-MM-dd HH:mm:ss"));
+                break;
+            case "3": // 鏈勾
+                dto.setStartTime(DateUtil.format(DateUtil.beginOfYear(now), "yyyy-MM-dd HH:mm:ss"));
+                dto.setEndTime(DateUtil.format(DateUtil.endOfYear(now), "yyyy-MM-dd HH:mm:ss"));
+                break;
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/vo/DashboardOverviewVo.java b/report-server/src/main/java/com/ruoyi/report/vo/DashboardOverviewVo.java
new file mode 100644
index 0000000..d414324
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/vo/DashboardOverviewVo.java
@@ -0,0 +1,42 @@
+package com.ruoyi.report.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 鐪嬫澘姒傝VO
+ */
+@Data
+public class DashboardOverviewVo {
+
+    @ApiModelProperty("寰呴鏍峰搧鏁�")
+    private Integer waitReceive;
+
+    @ApiModelProperty("寰呮鏍峰搧鏁�")
+    private Integer waitInspection;
+
+    @ApiModelProperty("寰呭鏍告牱鍝佹暟")
+    private Integer waitAudit;
+
+    @ApiModelProperty("寰呯紪鍒舵姤鍛婃暟")
+    private Integer waitReport;
+
+    @ApiModelProperty("浠婃棩鏂板鏍峰搧")
+    private Integer todayNewSample;
+
+    @ApiModelProperty("浠婃棩瀹屾垚鏍峰搧")
+    private Integer todayFinished;
+
+    @ApiModelProperty("杩�30澶╁師鏉愭枡妫�楠岀粨鏋�")
+    private List<Map<String, Object>> rawMaterialResult;
+
+    @ApiModelProperty("杩�30澶╁崐鎴愬搧妫�楠岀粨鏋�")
+    private List<Map<String, Object>> semiFinishedResult;
+
+    @ApiModelProperty("杩�30澶╂垚鍝佹楠岀粨鏋�")
+    private List<Map<String, Object>> finishedProductResult;
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/vo/DeviceRecordVo.java b/report-server/src/main/java/com/ruoyi/report/vo/DeviceRecordVo.java
new file mode 100644
index 0000000..27ff307
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/vo/DeviceRecordVo.java
@@ -0,0 +1,54 @@
+package com.ruoyi.report.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 璁惧浣跨敤璁板綍VO
+ */
+@Data
+public class DeviceRecordVo {
+
+    @ApiModelProperty("璁板綍ID")
+    private Long id;
+
+    @ApiModelProperty("璁惧ID")
+    private Long deviceId;
+
+    @ApiModelProperty("璁惧缂栧彿")
+    private String deviceCode;
+
+    @ApiModelProperty("璁惧鍚嶇О")
+    private String deviceName;
+
+    @ApiModelProperty("鏍峰搧缂栧彿")
+    private String sampleCode;
+
+    @ApiModelProperty("浣跨敤浜�")
+    private String useUser;
+
+    @ApiModelProperty("寮�濮嬫椂闂�")
+    private String startTime;
+
+    @ApiModelProperty("缁撴潫鏃堕棿")
+    private String endTime;
+
+    @ApiModelProperty("娓╁害")
+    private String temperature;
+
+    @ApiModelProperty("婀垮害")
+    private String humidity;
+
+    @ApiModelProperty("浣跨敤鍓嶇姸鎬�(0寮傚父/1鑹ソ)")
+    private Integer useBefore;
+
+    @ApiModelProperty("浣跨敤鍚庣姸鎬�(0寮傚父/1鑹ソ)")
+    private Integer useAfter;
+
+    @ApiModelProperty("寮傚父鎯呭喌")
+    private String abnormal;
+
+    @ApiModelProperty("澶囨敞")
+    private String remark;
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/vo/NormalDistributionVo.java b/report-server/src/main/java/com/ruoyi/report/vo/NormalDistributionVo.java
new file mode 100644
index 0000000..37cf20b
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/vo/NormalDistributionVo.java
@@ -0,0 +1,42 @@
+package com.ruoyi.report.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * 姝f�佸垎甯冨垎鏋愮粨鏋淰O
+ */
+@Data
+public class NormalDistributionVo {
+
+    @ApiModelProperty("鐩存柟鍥惧垎缁勮竟鐣�")
+    private List<BigDecimal> binEdges;
+
+    @ApiModelProperty("鐩存柟鍥鹃鏁�")
+    private List<Integer> frequencies;
+
+    @ApiModelProperty("姝f�佸垎甯冩洸绾縓杞�")
+    private List<BigDecimal> normalX;
+
+    @ApiModelProperty("姝f�佸垎甯冩洸绾縔杞�")
+    private List<BigDecimal> normalY;
+
+    @ApiModelProperty("鍧囧��")
+    private BigDecimal mean;
+
+    @ApiModelProperty("鏍囧噯宸�")
+    private BigDecimal stdDev;
+
+    @ApiModelProperty("鏈�灏忓��")
+    private BigDecimal min;
+
+    @ApiModelProperty("鏈�澶у��")
+    private BigDecimal max;
+
+    @ApiModelProperty("鏍锋湰鏁�")
+    private Integer sampleSize;
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/vo/ParetoVo.java b/report-server/src/main/java/com/ruoyi/report/vo/ParetoVo.java
new file mode 100644
index 0000000..8f20a37
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/vo/ParetoVo.java
@@ -0,0 +1,23 @@
+package com.ruoyi.report.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 甯曠疮鎵樺浘VO
+ */
+@Data
+public class ParetoVo {
+
+    @ApiModelProperty("涓嶅悎鏍奸」鐩�")
+    private List<String> categories;
+
+    @ApiModelProperty("涓嶅悎鏍兼鏁�")
+    private List<Integer> values;
+
+    @ApiModelProperty("绱鐧惧垎姣�")
+    private List<Double> cumulativePercent;
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/vo/RankingVo.java b/report-server/src/main/java/com/ruoyi/report/vo/RankingVo.java
new file mode 100644
index 0000000..61597a2
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/vo/RankingVo.java
@@ -0,0 +1,30 @@
+package com.ruoyi.report.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 鎺掕VO
+ */
+@Data
+public class RankingVo {
+
+    @ApiModelProperty("鎺掑悕")
+    private Integer rank;
+
+    @ApiModelProperty("鐢ㄦ埛ID")
+    private Long userId;
+
+    @ApiModelProperty("鐢ㄦ埛鍚嶇О")
+    private String userName;
+
+    @ApiModelProperty("閮ㄩ棬鍚嶇О")
+    private String deptName;
+
+    @ApiModelProperty("鎻愪氦鏁伴噺")
+    private Integer submitCount;
+
+    @ApiModelProperty("瀹屾垚鏁伴噺")
+    private Integer finishCount;
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/vo/SampleProgressVo.java b/report-server/src/main/java/com/ruoyi/report/vo/SampleProgressVo.java
new file mode 100644
index 0000000..abd33c4
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/vo/SampleProgressVo.java
@@ -0,0 +1,57 @@
+package com.ruoyi.report.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 鏍峰搧杩涘害VO
+ */
+@Data
+public class SampleProgressVo {
+
+    @ApiModelProperty("鏍峰搧ID")
+    private Long sampleId;
+
+    @ApiModelProperty("濮旀墭缂栧彿")
+    private String entrustCode;
+
+    @ApiModelProperty("鏍峰搧缂栧彿")
+    private String sampleCode;
+
+    @ApiModelProperty("鏍峰搧鍚嶇О")
+    private String sampleName;
+
+    @ApiModelProperty("鎶ュ憡缂栧彿")
+    private String reportCode;
+
+    @ApiModelProperty("妫�娴嬬姸鎬�(0寰呮/1妫�楠屼腑/2宸叉楠�/3寰呭鏍�/4瀹℃牳鏈�氳繃/5瀹℃牳閫氳繃)")
+    private Integer insState;
+
+    @ApiModelProperty("妫�娴嬬姸鎬佸悕绉�")
+    private String insStateName;
+
+    @ApiModelProperty("杩涘害鐧惧垎姣�")
+    private Double progressPercent;
+
+    @ApiModelProperty("宸插畬鎴愰」鐩暟")
+    private Integer finishedItems;
+
+    @ApiModelProperty("鎬婚」鐩暟")
+    private Integer totalItems;
+
+    @ApiModelProperty("璁″垝瀹屾垚鏃堕棿")
+    private String planFinishTime;
+
+    @ApiModelProperty("瀹為檯瀹屾垚鏃堕棿")
+    private String actualFinishTime;
+
+    @ApiModelProperty("璐熻矗浜�")
+    private String chargeUser;
+
+    @ApiModelProperty("瀹㈡埛鍚嶇О")
+    private String custom;
+
+    @ApiModelProperty("鍒涘缓鏃堕棿")
+    private String createTime;
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/vo/SampleRecordVo.java b/report-server/src/main/java/com/ruoyi/report/vo/SampleRecordVo.java
new file mode 100644
index 0000000..dbdf066
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/vo/SampleRecordVo.java
@@ -0,0 +1,48 @@
+package com.ruoyi.report.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 鏍峰搧棰嗘牱璁板綍VO
+ */
+@Data
+public class SampleRecordVo {
+
+    @ApiModelProperty("璁板綍ID")
+    private Long id;
+
+    @ApiModelProperty("鏍峰搧ID")
+    private Long sampleId;
+
+    @ApiModelProperty("鏍峰搧缂栧彿")
+    private String sampleCode;
+
+    @ApiModelProperty("鏍峰搧鍚嶇О")
+    private String sampleName;
+
+    @ApiModelProperty("瀹㈡埛鍚嶇О")
+    private String custom;
+
+    @ApiModelProperty("鎿嶄綔绫诲瀷(棰嗙敤/褰掕繕/杞Щ)")
+    private String operateType;
+
+    @ApiModelProperty("鎿嶄綔绫诲瀷鍚嶇О")
+    private String operateTypeName;
+
+    @ApiModelProperty("鎿嶄綔浜�")
+    private String operateUser;
+
+    @ApiModelProperty("鎿嶄綔鏃堕棿")
+    private String operateTime;
+
+    @ApiModelProperty("鍘熷簱浣�")
+    private String fromCell;
+
+    @ApiModelProperty("鐩爣搴撲綅")
+    private String toCell;
+
+    @ApiModelProperty("澶囨敞")
+    private String remark;
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/vo/SpcResultVo.java b/report-server/src/main/java/com/ruoyi/report/vo/SpcResultVo.java
new file mode 100644
index 0000000..1097b13
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/vo/SpcResultVo.java
@@ -0,0 +1,58 @@
+package com.ruoyi.report.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * SPC鍒嗘瀽缁撴灉VO
+ */
+@Data
+public class SpcResultVo {
+
+    @ApiModelProperty("X-bar鍥炬暟鎹�")
+    private ChartData xBar;
+
+    @ApiModelProperty("R鍥炬暟鎹�")
+    private ChartData rChart;
+
+    @ApiModelProperty("鍒剁▼鑳藉姏")
+    private Capability capability;
+
+    @Data
+    public static class ChartData {
+        @ApiModelProperty("鏁版嵁鐐�")
+        private List<BigDecimal> data;
+
+        @ApiModelProperty("鎺у埗涓婇檺")
+        private BigDecimal ucl;
+
+        @ApiModelProperty("鎺у埗涓嬮檺")
+        private BigDecimal lcl;
+
+        @ApiModelProperty("涓績绾�")
+        private BigDecimal cl;
+
+        @ApiModelProperty("鏍锋湰缂栧彿")
+        private List<String> sampleLabels;
+    }
+
+    @Data
+    public static class Capability {
+        @ApiModelProperty("鍒剁▼鑳藉姏鎸囨暟Cp")
+        private BigDecimal cp;
+
+        @ApiModelProperty("鍒剁▼鑳藉姏鎸囨暟Cpk")
+        private BigDecimal cpk;
+
+        @ApiModelProperty("鍒剁▼鎬ц兘鎸囨暟Pp")
+        private BigDecimal pp;
+
+        @ApiModelProperty("鍒剁▼鎬ц兘鎸囨暟Ppk")
+        private BigDecimal ppk;
+    }
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/vo/TaskCalendarVo.java b/report-server/src/main/java/com/ruoyi/report/vo/TaskCalendarVo.java
new file mode 100644
index 0000000..72ca7ea
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/vo/TaskCalendarVo.java
@@ -0,0 +1,27 @@
+package com.ruoyi.report.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 浠诲姟鏃ュ巻VO
+ */
+@Data
+public class TaskCalendarVo {
+
+    @ApiModelProperty("鏃ユ湡")
+    private String date;
+
+    @ApiModelProperty("鏍峰搧鏁伴噺")
+    private Integer sampleCount;
+
+    @ApiModelProperty("瀹屾垚鏁伴噺")
+    private Integer finishedCount;
+
+    @ApiModelProperty("寰呭鐞嗘暟閲�")
+    private Integer pendingCount;
+
+    @ApiModelProperty("瓒呮湡鏁伴噺")
+    private Integer overdueCount;
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/vo/TestItemDataVo.java b/report-server/src/main/java/com/ruoyi/report/vo/TestItemDataVo.java
new file mode 100644
index 0000000..3c7eeef
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/vo/TestItemDataVo.java
@@ -0,0 +1,57 @@
+package com.ruoyi.report.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 妫�娴嬮」鐩暟鎹甐O
+ */
+@Data
+public class TestItemDataVo {
+
+    @ApiModelProperty("妫�娴嬮」ID")
+    private Long productId;
+
+    @ApiModelProperty("鏍峰搧ID")
+    private Long sampleId;
+
+    @ApiModelProperty("鏍峰搧缂栧彿")
+    private String sampleCode;
+
+    @ApiModelProperty("鏍峰搧鍚嶇О")
+    private String sampleName;
+
+    @ApiModelProperty("鐢熶骇璁㈠崟")
+    private String productionOrder;
+
+    @ApiModelProperty("鎵规鍙�")
+    private String batchNo;
+
+    @ApiModelProperty("妫�娴嬮」鍚嶇О")
+    private String itemName;
+
+    @ApiModelProperty("妫�娴嬪��")
+    private String lastValue;
+
+    @ApiModelProperty("鏍囧噯鍊�")
+    private String standardValue;
+
+    @ApiModelProperty("妫�娴嬬粨鏋�(1鍚堟牸/0涓嶅悎鏍�)")
+    private Integer insResult;
+
+    @ApiModelProperty("妫�娴嬬粨鏋滃悕绉�")
+    private String insResultName;
+
+    @ApiModelProperty("鍗曚綅")
+    private String unit;
+
+    @ApiModelProperty("妫�娴嬩汉")
+    private String insUser;
+
+    @ApiModelProperty("妫�娴嬫椂闂�")
+    private String insTime;
+
+    @ApiModelProperty("鎶ュ憡缂栧彿")
+    private String reportCode;
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/java/com/ruoyi/report/vo/WorkStatisticsVo.java b/report-server/src/main/java/com/ruoyi/report/vo/WorkStatisticsVo.java
new file mode 100644
index 0000000..80d1afc
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/vo/WorkStatisticsVo.java
@@ -0,0 +1,36 @@
+package com.ruoyi.report.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 宸ヤ綔缁熻VO
+ */
+@Data
+public class WorkStatisticsVo {
+
+    @ApiModelProperty("鐢ㄦ埛ID")
+    private Long userId;
+
+    @ApiModelProperty("鐢ㄦ埛鍚嶇О")
+    private String userName;
+
+    @ApiModelProperty("閮ㄩ棬鍚嶇О")
+    private String deptName;
+
+    @ApiModelProperty("妫�娴嬫牱鍝佹暟")
+    private Integer sampleCount;
+
+    @ApiModelProperty("妫�娴嬮」鐩暟")
+    private Integer itemCount;
+
+    @ApiModelProperty("鍙婃椂鐜�(%)")
+    private Double timelyRate;
+
+    @ApiModelProperty("鎸夋椂瀹屾垚鏁�")
+    private Integer timelyCount;
+
+    @ApiModelProperty("瓒呮湡瀹屾垚鏁�")
+    private Integer overdueCount;
+
+}
\ No newline at end of file
diff --git a/report-server/src/main/resources/mapper/DashboardMapper.xml b/report-server/src/main/resources/mapper/DashboardMapper.xml
new file mode 100644
index 0000000..93e7eef
--- /dev/null
+++ b/report-server/src/main/resources/mapper/DashboardMapper.xml
@@ -0,0 +1,164 @@
+<?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.report.mapper.DashboardMapper">
+
+    <!-- 鑾峰彇寰呴鏍峰搧鏁� -->
+    <select id="getWaitReceive" resultType="java.lang.Integer">
+        SELECT COUNT(*) FROM ins_sample WHERE ins_state = 0
+    </select>
+
+    <!-- 鑾峰彇寰呮鏍峰搧鏁� -->
+    <select id="getWaitInspection" resultType="java.lang.Integer">
+        SELECT COUNT(*) FROM ins_order WHERE ins_state = 0
+    </select>
+
+    <!-- 鑾峰彇寰呭鏍告牱鍝佹暟 -->
+    <select id="getWaitAudit" resultType="java.lang.Integer">
+        SELECT COUNT(*) FROM ins_order WHERE ins_state = 3
+    </select>
+
+    <!-- 鑾峰彇寰呯紪鍒舵姤鍛婃暟 -->
+    <select id="getWaitReport" resultType="java.lang.Integer">
+        SELECT COUNT(*) FROM ins_report WHERE state = 0
+    </select>
+
+    <!-- 浠婃棩鏂板鏍峰搧 -->
+    <select id="getTodayNewSample" resultType="java.lang.Integer">
+        SELECT COUNT(*) FROM ins_sample
+        WHERE DATE(create_time) = CURDATE()
+    </select>
+
+    <!-- 浠婃棩瀹屾垚鏍峰搧 -->
+    <select id="getTodayFinished" resultType="java.lang.Integer">
+        SELECT COUNT(*) FROM ins_order
+        WHERE ins_state = 5 AND DATE(ins_time) = CURDATE()
+    </select>
+
+    <!-- 鍘嗗彶15澶╂暟鎹� -->
+    <select id="getHistoryDays" resultType="com.ruoyi.report.vo.TaskCalendarVo">
+        SELECT
+            DATE_FORMAT(DATE_SUB(CURDATE(), INTERVAL n DAY), '%Y-%m-%d') AS date,
+            COUNT(DISTINCT s.id) AS sampleCount,
+            SUM(CASE WHEN o.ins_state = 5 THEN 1 ELSE 0 END) AS finishedCount,
+            SUM(CASE WHEN o.ins_state IN (0,1,2,3) THEN 1 ELSE 0 END) AS pendingCount,
+            SUM(CASE WHEN o.ins_state IN (0,1,2,3) AND o.appointed &lt; NOW() THEN 1 ELSE 0 END) AS overdueCount
+        FROM (
+            SELECT 0 AS n UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4
+            UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
+            UNION SELECT 10 UNION SELECT 11 UNION SELECT 12 UNION SELECT 13 UNION SELECT 14
+        ) days
+        LEFT JOIN ins_order o ON DATE(o.create_time) = DATE_SUB(CURDATE(), INTERVAL n DAY)
+        LEFT JOIN ins_sample s ON o.id = s.ins_order_id
+        GROUP BY date
+        ORDER BY date ASC
+    </select>
+
+    <!-- 鏈潵15澶╀换鍔� -->
+    <select id="getFutureDays" resultType="com.ruoyi.report.vo.TaskCalendarVo">
+        SELECT
+            DATE_FORMAT(DATE_ADD(CURDATE(), INTERVAL n DAY), '%Y-%m-%d') AS date,
+            COUNT(DISTINCT s.id) AS sampleCount,
+            0 AS finishedCount,
+            SUM(CASE WHEN o.ins_state IN (0,1,2,3) THEN 1 ELSE 0 END) AS pendingCount,
+            0 AS overdueCount
+        FROM (
+            SELECT 0 AS n UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4
+            UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
+            UNION SELECT 10 UNION SELECT 11 UNION SELECT 12 UNION SELECT 13 UNION SELECT 14
+        ) days
+        LEFT JOIN ins_order o ON DATE(o.appointed) = DATE_ADD(CURDATE(), INTERVAL n DAY) AND o.ins_state IN (0,1,2,3)
+        LEFT JOIN ins_sample s ON o.id = s.ins_order_id
+        GROUP BY date
+        ORDER BY date ASC
+    </select>
+
+    <!-- 鎻愪氦鎺掕(鍘熷璁板綍) -->
+    <select id="getOriginalRecordRanking" resultType="com.ruoyi.report.vo.RankingVo">
+        SELECT
+            u.id AS userId,
+            u.name AS userName,
+            d.dept_name AS deptName,
+            COUNT(*) AS submitCount
+        FROM ins_product p
+        LEFT JOIN ins_product_user pu ON p.id = pu.ins_product_id
+        LEFT JOIN user u ON pu.create_user = u.id
+        LEFT JOIN sys_dept d ON u.dept_id = d.dept_id
+        LEFT JOIN ins_sample s ON p.ins_sample_id = s.id
+        LEFT JOIN ins_order o ON s.ins_order_id = o.id
+        WHERE o.ins_time IS NOT NULL
+        <if test="dto.startTime != null and dto.startTime != ''">
+            AND o.ins_time >= #{dto.startTime}
+        </if>
+        <if test="dto.endTime != null and dto.endTime != ''">
+            AND o.ins_time &lt;= #{dto.endTime}
+        </if>
+        GROUP BY u.id, u.name, d.dept_name
+        ORDER BY submitCount DESC
+        LIMIT 10
+    </select>
+
+    <!-- 鎻愪氦鎺掕(鎶ュ憡) -->
+    <select id="getReportRanking" resultType="com.ruoyi.report.vo.RankingVo">
+        SELECT
+            u.id AS userId,
+            u.name AS userName,
+            d.dept_name AS deptName,
+            COUNT(*) AS submitCount
+        FROM ins_report r
+        LEFT JOIN user u ON r.write_user_id = u.id
+        LEFT JOIN sys_dept d ON u.dept_id = d.dept_id
+        WHERE r.state >= 1
+        <if test="dto.startTime != null and dto.startTime != ''">
+            AND r.create_time >= #{dto.startTime}
+        </if>
+        <if test="dto.endTime != null and dto.endTime != ''">
+            AND r.create_time &lt;= #{dto.endTime}
+        </if>
+        GROUP BY u.id, u.name, d.dept_name
+        ORDER BY submitCount DESC
+        LIMIT 10
+    </select>
+
+    <!-- 杩�30澶╂楠岀粨鏋� -->
+    <select id="getInsResultByDays" resultType="java.util.Map">
+        SELECT
+            DATE_FORMAT(DATE_SUB(CURDATE(), INTERVAL n DAY), '%Y-%m-%d') AS date,
+            SUM(CASE WHEN p.ins_result = 1 THEN 1 ELSE 0 END) AS qualified,
+            SUM(CASE WHEN p.ins_result = 0 THEN 1 ELSE 0 END) AS unqualified,
+            COUNT(*) AS total
+        FROM (
+            SELECT 0 AS n UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4
+            UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
+            UNION SELECT 10 UNION SELECT 11 UNION SELECT 12 UNION SELECT 13 UNION SELECT 14
+            UNION SELECT 15 UNION SELECT 16 UNION SELECT 17 UNION SELECT 18 UNION SELECT 19
+            UNION SELECT 20 UNION SELECT 21 UNION SELECT 22 UNION SELECT 23 UNION SELECT 24
+            UNION SELECT 25 UNION SELECT 26 UNION SELECT 27 UNION SELECT 28 UNION SELECT 29
+        ) days
+        LEFT JOIN ins_product p ON DATE(p.create_time) = DATE_SUB(CURDATE(), INTERVAL n DAY)
+        LEFT JOIN ins_sample s ON p.ins_sample_id = s.id
+        LEFT JOIN ins_order o ON s.ins_order_id = o.id
+        WHERE p.ins_result IS NOT NULL
+        <if test="orderType != null and orderType != ''">
+            AND o.order_type = #{orderType}
+        </if>
+        GROUP BY date
+        ORDER BY date ASC
+    </select>
+
+    <!-- 鑾峰彇璇煶鎾姤闃熷垪 -->
+    <select id="getVoiceQueue" resultType="java.util.Map">
+        SELECT
+            id,
+            event_type AS eventType,
+            event_name AS eventName,
+            details,
+            voice_text AS voiceText,
+            priority,
+            create_time AS createTime
+        FROM voice_queue
+        WHERE status = 0
+        ORDER BY priority DESC, create_time ASC
+        LIMIT 10
+    </select>
+
+</mapper>
\ No newline at end of file
diff --git a/report-server/src/main/resources/mapper/NormalDistributionMapper.xml b/report-server/src/main/resources/mapper/NormalDistributionMapper.xml
new file mode 100644
index 0000000..63b1ce5
--- /dev/null
+++ b/report-server/src/main/resources/mapper/NormalDistributionMapper.xml
@@ -0,0 +1,57 @@
+<?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.report.mapper.NormalDistributionMapper">
+
+    <!-- 鏌ヨ妫�娴嬮」鏁版嵁 -->
+    <select id="getItemData" resultType="java.util.Map">
+        SELECT
+            s.sample_code AS sampleCode,
+            s.sample AS sampleName,
+            p.inspection_item AS itemName,
+            p.last_value AS lastValue,
+            o.ins_time AS insTime
+        FROM ins_product p
+        LEFT JOIN ins_sample s ON p.ins_sample_id = s.id
+        LEFT JOIN ins_order o ON s.ins_order_id = o.id
+        WHERE p.ins_result IS NOT NULL AND p.last_value IS NOT NULL
+        <if test="dto.itemName != null and dto.itemName != ''">
+            AND p.inspection_item = #{dto.itemName}
+        </if>
+        <if test="dto.sampleName != null and dto.sampleName != ''">
+            AND s.sample LIKE CONCAT('%', #{dto.sampleName}, '%')
+        </if>
+        <if test="dto.startDate != null and dto.startDate != ''">
+            AND o.ins_time >= #{dto.startDate}
+        </if>
+        <if test="dto.endDate != null and dto.endDate != ''">
+            AND o.ins_time &lt;= #{dto.endDate}
+        </if>
+        ORDER BY o.ins_time ASC
+    </select>
+
+    <!-- 鏌ヨ鍙�夋娴嬮」 -->
+    <select id="getItemNames" resultType="java.lang.String">
+        SELECT DISTINCT p.inspection_item
+        FROM ins_product p
+        LEFT JOIN ins_sample s ON p.ins_sample_id = s.id
+        WHERE p.ins_result IS NOT NULL AND p.last_value IS NOT NULL
+        <if test="dto.sampleName != null and dto.sampleName != ''">
+            AND s.sample LIKE CONCAT('%', #{dto.sampleName}, '%')
+        </if>
+        ORDER BY p.inspection_item ASC
+    </select>
+
+    <!-- 鏌ヨ鍙�夋牱鍝佸悕绉板垪琛� -->
+    <select id="getSampleNames" resultType="java.lang.String">
+        SELECT DISTINCT s.sample
+        FROM ins_product p
+        LEFT JOIN ins_sample s ON p.ins_sample_id = s.id
+        LEFT JOIN ins_order o ON s.ins_order_id = o.id
+        WHERE p.ins_result IS NOT NULL AND p.last_value IS NOT NULL
+        <if test="dto.itemName != null and dto.itemName != ''">
+            AND p.inspection_item = #{dto.itemName}
+        </if>
+        ORDER BY s.sample ASC
+    </select>
+
+</mapper>
\ No newline at end of file
diff --git a/report-server/src/main/resources/mapper/PassRateMapper.xml b/report-server/src/main/resources/mapper/PassRateMapper.xml
new file mode 100644
index 0000000..b41c4e8
--- /dev/null
+++ b/report-server/src/main/resources/mapper/PassRateMapper.xml
@@ -0,0 +1,126 @@
+<?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.report.mapper.PassRateMapper">
+
+    <!-- 鍘熸潗鏂欏悎鏍肩巼 -->
+    <select id="getRawMaterialPassRate" resultType="java.util.Map">
+        SELECT
+            s.sample AS sampleName,
+            o.entrust_code AS batchNo,
+            COUNT(*) AS totalCount,
+            SUM(CASE WHEN p.ins_result = 1 THEN 1 ELSE 0 END) AS qualifiedCount,
+            SUM(CASE WHEN p.ins_result = 0 THEN 1 ELSE 0 END) AS unqualifiedCount,
+            ROUND(SUM(CASE WHEN p.ins_result = 1 THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 2) AS passRate
+        FROM ins_product p
+        LEFT JOIN ins_sample s ON p.ins_sample_id = s.id
+        LEFT JOIN ins_order o ON s.ins_order_id = o.id
+        WHERE p.ins_result IS NOT NULL AND o.order_type = '1'
+        <if test="dto.startTime != null and dto.startTime != ''">
+            AND o.ins_time >= #{dto.startTime}
+        </if>
+        <if test="dto.endTime != null and dto.endTime != ''">
+            AND o.ins_time &lt;= #{dto.endTime}
+        </if>
+        <if test="dto.sampleName != null and dto.sampleName != ''">
+            AND s.sample LIKE CONCAT('%', #{dto.sampleName}, '%')
+        </if>
+        <if test="dto.supplierName != null and dto.supplierName != ''">
+            AND o.custom LIKE CONCAT('%', #{dto.supplierName}, '%')
+        </if>
+        GROUP BY s.sample, o.entrust_code
+        ORDER BY passRate ASC
+    </select>
+
+    <!-- 渚涘簲鍟嗕笉鍚堟牸缁熻 -->
+    <select id="getSupplierUnqualified" resultType="java.util.Map">
+        SELECT
+            o.custom AS supplierName,
+            COUNT(*) AS totalCount,
+            SUM(CASE WHEN p.ins_result = 0 THEN 1 ELSE 0 END) AS unqualifiedCount
+        FROM ins_product p
+        LEFT JOIN ins_sample s ON p.ins_sample_id = s.id
+        LEFT JOIN ins_order o ON s.ins_order_id = o.id
+        WHERE p.ins_result IS NOT NULL AND o.order_type = '1'
+        <if test="dto.startTime != null and dto.startTime != ''">
+            AND o.ins_time >= #{dto.startTime}
+        </if>
+        <if test="dto.endTime != null and dto.endTime != ''">
+            AND o.ins_time &lt;= #{dto.endTime}
+        </if>
+        GROUP BY o.custom
+        HAVING unqualifiedCount > 0
+        ORDER BY unqualifiedCount DESC
+    </select>
+
+    <!-- 涓嶅悎鏍奸」鐩粺璁�(鐢ㄤ簬甯曠疮鎵樺浘) -->
+    <select id="getUnqualifiedItemStats" resultType="java.util.Map">
+        SELECT
+            p.inspection_item AS itemName,
+            COUNT(*) AS unqualifiedCount
+        FROM ins_product p
+        LEFT JOIN ins_sample s ON p.ins_sample_id = s.id
+        LEFT JOIN ins_order o ON s.ins_order_id = o.id
+        WHERE p.ins_result = 0
+        <if test="dto.startTime != null and dto.startTime != ''">
+            AND o.ins_time >= #{dto.startTime}
+        </if>
+        <if test="dto.endTime != null and dto.endTime != ''">
+            AND o.ins_time &lt;= #{dto.endTime}
+        </if>
+        <if test="dto.orderType != null and dto.orderType != ''">
+            AND o.order_type = #{dto.orderType}
+        </if>
+        GROUP BY p.inspection_item
+        ORDER BY unqualifiedCount DESC
+    </select>
+
+    <!-- 宸ュ簭鍚堟牸鐜� -->
+    <select id="getProcessPassRate" resultType="java.util.Map">
+        SELECT
+            o.laboratory AS processName,
+            COUNT(*) AS totalCount,
+            SUM(CASE WHEN p.ins_result = 1 THEN 1 ELSE 0 END) AS qualifiedCount,
+            SUM(CASE WHEN p.ins_result = 0 THEN 1 ELSE 0 END) AS unqualifiedCount,
+            ROUND(SUM(CASE WHEN p.ins_result = 1 THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 2) AS passRate
+        FROM ins_product p
+        LEFT JOIN ins_sample s ON p.ins_sample_id = s.id
+        LEFT JOIN ins_order o ON s.ins_order_id = o.id
+        WHERE p.ins_result IS NOT NULL
+        <if test="dto.startTime != null and dto.startTime != ''">
+            AND o.ins_time >= #{dto.startTime}
+        </if>
+        <if test="dto.endTime != null and dto.endTime != ''">
+            AND o.ins_time &lt;= #{dto.endTime}
+        </if>
+        <if test="dto.orderType != null and dto.orderType != ''">
+            AND o.order_type = #{dto.orderType}
+        </if>
+        GROUP BY o.laboratory
+        ORDER BY passRate ASC
+    </select>
+
+    <!-- 鏈哄彴涓嶅悎鏍肩粺璁� -->
+    <select id="getMachineUnqualified" resultType="java.util.Map">
+        SELECT
+            p.factory AS machineNo,
+            COUNT(*) AS totalCount,
+            SUM(CASE WHEN p.ins_result = 0 THEN 1 ELSE 0 END) AS unqualifiedCount
+        FROM ins_product p
+        LEFT JOIN ins_sample s ON p.ins_sample_id = s.id
+        LEFT JOIN ins_order o ON s.ins_order_id = o.id
+        WHERE p.ins_result IS NOT NULL
+        <if test="dto.startTime != null and dto.startTime != ''">
+            AND o.ins_time >= #{dto.startTime}
+        </if>
+        <if test="dto.endTime != null and dto.endTime != ''">
+            AND o.ins_time &lt;= #{dto.endTime}
+        </if>
+        <if test="dto.orderType != null and dto.orderType != ''">
+            AND o.order_type = #{dto.orderType}
+        </if>
+        GROUP BY p.factory
+        HAVING unqualifiedCount > 0
+        ORDER BY unqualifiedCount DESC
+    </select>
+
+</mapper>
diff --git a/report-server/src/main/resources/mapper/ReportDeviceRecordMapper.xml b/report-server/src/main/resources/mapper/ReportDeviceRecordMapper.xml
new file mode 100644
index 0000000..312c187
--- /dev/null
+++ b/report-server/src/main/resources/mapper/ReportDeviceRecordMapper.xml
@@ -0,0 +1,73 @@
+<?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.report.mapper.ReportDeviceRecordMapper">
+
+    <!-- 鍒嗛〉鏌ヨ璁惧浣跨敤璁板綍 -->
+    <select id="pageDeviceRecord" resultType="com.ruoyi.report.vo.DeviceRecordVo">
+        SELECT
+            r.id AS id,
+            d.id AS deviceId,
+            d.management_number AS deviceCode,
+            d.device_name AS deviceName,
+            r.sample_code AS sampleCode,
+            r.use_person AS useUser,
+            r.use_start_date AS startTime,
+            r.use_end_date AS endTime,
+            r.temperature,
+            r.humidity,
+            r.use_before AS useBefore,
+            r.use_after AS useAfter,
+            r.abnormal,
+            r.remark
+        FROM device_record r
+        LEFT JOIN device d ON r.device_id = d.id
+        WHERE 1=1
+        <if test="dto.deviceCode != null and dto.deviceCode != ''">
+            AND d.management_number LIKE CONCAT('%', #{dto.deviceCode}, '%')
+        </if>
+        <if test="dto.deviceName != null and dto.deviceName != ''">
+            AND d.device_name LIKE CONCAT('%', #{dto.deviceName}, '%')
+        </if>
+        <if test="dto.useUser != null and dto.useUser != ''">
+            AND r.use_person LIKE CONCAT('%', #{dto.useUser}, '%')
+        </if>
+        <if test="dto.sampleCode != null and dto.sampleCode != ''">
+            AND r.sample_code LIKE CONCAT('%', #{dto.sampleCode}, '%')
+        </if>
+        <if test="dto.startTime != null and dto.startTime != ''">
+            AND r.use_start_date >= #{dto.startTime}
+        </if>
+        <if test="dto.endTime != null and dto.endTime != ''">
+            AND r.use_end_date &lt;= #{dto.endTime}
+        </if>
+        ORDER BY r.use_start_date DESC
+    </select>
+
+    <!-- 璁惧浣跨敤缁熻 -->
+    <select id="getStatistics" resultType="java.util.Map">
+        SELECT
+            d.device_name AS deviceName,
+            d.management_number AS deviceCode,
+            COUNT(*) AS useCount,
+            COUNT(DISTINCT r.sample_code) AS sampleCount,
+            COUNT(DISTINCT r.use_person_id) AS userCount
+        FROM device_record r
+        LEFT JOIN device d ON r.device_id = d.id
+        WHERE 1=1
+        <if test="dto.deviceCode != null and dto.deviceCode != ''">
+            AND d.management_number LIKE CONCAT('%', #{dto.deviceCode}, '%')
+        </if>
+        <if test="dto.deviceName != null and dto.deviceName != ''">
+            AND d.device_name LIKE CONCAT('%', #{dto.deviceName}, '%')
+        </if>
+        <if test="dto.startTime != null and dto.startTime != ''">
+            AND r.use_start_date >= #{dto.startTime}
+        </if>
+        <if test="dto.endTime != null and dto.endTime != ''">
+            AND r.use_end_date &lt;= #{dto.endTime}
+        </if>
+        GROUP BY d.id
+        ORDER BY useCount DESC
+    </select>
+
+</mapper>
\ No newline at end of file
diff --git a/report-server/src/main/resources/mapper/SampleProgressMapper.xml b/report-server/src/main/resources/mapper/SampleProgressMapper.xml
new file mode 100644
index 0000000..ac9dfd1
--- /dev/null
+++ b/report-server/src/main/resources/mapper/SampleProgressMapper.xml
@@ -0,0 +1,98 @@
+<?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.report.mapper.SampleProgressMapper">
+
+    <!-- 鍒嗛〉鏌ヨ鏍峰搧杩涘害 -->
+    <select id="pageSampleProgress" resultType="com.ruoyi.report.vo.SampleProgressVo">
+        SELECT
+            s.id AS sampleId,
+            o.entrust_code AS entrustCode,
+            s.sample_code AS sampleCode,
+            s.sample AS sampleName,
+            r.code AS reportCode,
+            o.ins_state AS insState,
+            COUNT(p.id) AS totalItems,
+            SUM(CASE WHEN p.ins_result IS NOT NULL THEN 1 ELSE 0 END) AS finishedItems,
+            o.appointed AS planFinishTime,
+            o.ins_time AS actualFinishTime,
+            u.name AS chargeUser,
+            o.custom AS custom,
+            s.create_time AS createTime
+        FROM ins_sample s
+        LEFT JOIN ins_order o ON s.ins_order_id = o.id
+        LEFT JOIN ins_product p ON s.id = p.ins_sample_id
+        LEFT JOIN ins_report r ON o.id = r.ins_order_id
+        LEFT JOIN user u ON o.prepare_user_id = u.id
+        WHERE 1=1
+        <if test="dto.entrustCode != null and dto.entrustCode != ''">
+            AND o.entrust_code LIKE CONCAT('%', #{dto.entrustCode}, '%')
+        </if>
+        <if test="dto.sampleCode != null and dto.sampleCode != ''">
+            AND s.sample_code LIKE CONCAT('%', #{dto.sampleCode}, '%')
+        </if>
+        <if test="dto.sampleName != null and dto.sampleName != ''">
+            AND s.sample LIKE CONCAT('%', #{dto.sampleName}, '%')
+        </if>
+        <if test="dto.reportCode != null and dto.reportCode != ''">
+            AND r.code LIKE CONCAT('%', #{dto.reportCode}, '%')
+        </if>
+        <if test="dto.insState != null">
+            AND o.ins_state = #{dto.insState}
+        </if>
+        <if test="dto.custom != null and dto.custom != ''">
+            AND o.custom LIKE CONCAT('%', #{dto.custom}, '%')
+        </if>
+        <if test="dto.startTime != null and dto.startTime != ''">
+            AND s.create_time >= #{dto.startTime}
+        </if>
+        <if test="dto.endTime != null and dto.endTime != ''">
+            AND s.create_time &lt;= #{dto.endTime}
+        </if>
+        GROUP BY s.id
+        ORDER BY s.create_time DESC
+    </select>
+
+    <!-- 鑾峰彇缁熻鏁版嵁 -->
+    <select id="getStatistics" resultType="java.util.Map">
+        SELECT
+            SUM(CASE WHEN o.ins_state = 0 THEN 1 ELSE 0 END) AS waitInspection,
+            SUM(CASE WHEN o.ins_state = 1 THEN 1 ELSE 0 END) AS inspecting,
+            SUM(CASE WHEN o.ins_state = 3 THEN 1 ELSE 0 END) AS waitAudit,
+            SUM(CASE WHEN o.ins_state = 5 THEN 1 ELSE 0 END) AS finished
+        FROM ins_sample s
+        LEFT JOIN ins_order o ON s.ins_order_id = o.id
+        WHERE 1=1
+        <if test="dto.custom != null and dto.custom != ''">
+            AND o.custom LIKE CONCAT('%', #{dto.custom}, '%')
+        </if>
+        <if test="dto.startTime != null and dto.startTime != ''">
+            AND s.create_time >= #{dto.startTime}
+        </if>
+        <if test="dto.endTime != null and dto.endTime != ''">
+            AND s.create_time &lt;= #{dto.endTime}
+        </if>
+    </select>
+
+    <!-- 鑾峰彇鍥捐〃鏁版嵁 -->
+    <select id="getChartData" resultType="java.util.Map">
+        SELECT
+            DATE_FORMAT(s.create_time, '%Y-%m-%d') AS date,
+            COUNT(*) AS totalCount,
+            SUM(CASE WHEN o.ins_state = 5 THEN 1 ELSE 0 END) AS finishedCount
+        FROM ins_sample s
+        LEFT JOIN ins_order o ON s.ins_order_id = o.id
+        WHERE 1=1
+        <if test="dto.custom != null and dto.custom != ''">
+            AND o.custom LIKE CONCAT('%', #{dto.custom}, '%')
+        </if>
+        <if test="dto.startTime != null and dto.startTime != ''">
+            AND s.create_time >= #{dto.startTime}
+        </if>
+        <if test="dto.endTime != null and dto.endTime != ''">
+            AND s.create_time &lt;= #{dto.endTime}
+        </if>
+        GROUP BY DATE_FORMAT(s.create_time, '%Y-%m-%d')
+        ORDER BY date ASC
+    </select>
+
+</mapper>
\ No newline at end of file
diff --git a/report-server/src/main/resources/mapper/SampleRecordMapper.xml b/report-server/src/main/resources/mapper/SampleRecordMapper.xml
new file mode 100644
index 0000000..d48f083
--- /dev/null
+++ b/report-server/src/main/resources/mapper/SampleRecordMapper.xml
@@ -0,0 +1,67 @@
+<?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.report.mapper.SampleRecordMapper">
+
+    <!-- 鍒嗛〉鏌ヨ棰嗘牱璁板綍 -->
+    <select id="pageSampleRecord" resultType="com.ruoyi.report.vo.SampleRecordVo">
+        SELECT
+            h.id AS id,
+            s.id AS sampleId,
+            s.sample_code AS sampleCode,
+            s.sample_name AS sampleName,
+            o.custom AS custom,
+            h.operate_type AS operateType,
+            u.name AS operateUser,
+            h.operate_time AS operateTime,
+            h.from_cell AS fromCell,
+            h.to_cell AS toCell,
+            h.remark AS remark
+        FROM warehouse_history h
+        LEFT JOIN ins_sample s ON h.sample_id = s.id
+        LEFT JOIN ins_order o ON s.ins_order_id = o.id
+        LEFT JOIN user u ON h.operate_user_id = u.id
+        WHERE 1=1
+        <if test="dto.sampleCode != null and dto.sampleCode != ''">
+            AND s.sample_code LIKE CONCAT('%', #{dto.sampleCode}, '%')
+        </if>
+        <if test="dto.sampleName != null and dto.sampleName != ''">
+            AND s.sample_name LIKE CONCAT('%', #{dto.sampleName}, '%')
+        </if>
+        <if test="dto.custom != null and dto.custom != ''">
+            AND o.custom LIKE CONCAT('%', #{dto.custom}, '%')
+        </if>
+        <if test="dto.receiveUser != null and dto.receiveUser != ''">
+            AND u.name LIKE CONCAT('%', #{dto.receiveUser}, '%')
+        </if>
+        <if test="dto.startTime != null and dto.startTime != ''">
+            AND h.operate_time >= #{dto.startTime}
+        </if>
+        <if test="dto.endTime != null and dto.endTime != ''">
+            AND h.operate_time &lt;= #{dto.endTime}
+        </if>
+        ORDER BY h.operate_time DESC
+    </select>
+
+    <!-- 鏌ヨ鏍峰搧娴佽浆璁板綍 -->
+    <select id="getFlowRecord" resultType="com.ruoyi.report.vo.SampleRecordVo">
+        SELECT
+            h.id AS id,
+            s.id AS sampleId,
+            s.sample_code AS sampleCode,
+            s.sample_name AS sampleName,
+            o.custom AS custom,
+            h.operate_type AS operateType,
+            u.name AS operateUser,
+            h.operate_time AS operateTime,
+            h.from_cell AS fromCell,
+            h.to_cell AS toCell,
+            h.remark AS remark
+        FROM warehouse_history h
+        LEFT JOIN ins_sample s ON h.sample_id = s.id
+        LEFT JOIN ins_order o ON s.ins_order_id = o.id
+        LEFT JOIN user u ON h.operate_user_id = u.id
+        WHERE s.id = #{sampleId}
+        ORDER BY h.operate_time ASC
+    </select>
+
+</mapper>
\ No newline at end of file
diff --git a/report-server/src/main/resources/mapper/SpcChartMapper.xml b/report-server/src/main/resources/mapper/SpcChartMapper.xml
new file mode 100644
index 0000000..82ae654
--- /dev/null
+++ b/report-server/src/main/resources/mapper/SpcChartMapper.xml
@@ -0,0 +1,57 @@
+<?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.report.mapper.SpcChartMapper">
+
+    <!-- 鏌ヨ妫�娴嬮」鏁版嵁 -->
+    <select id="getItemData" resultType="java.util.Map">
+        SELECT
+            s.sample_code AS sampleCode,
+            s.sample AS sampleName,
+            p.inspection_item AS itemName,
+            p.last_value AS lastValue,
+            o.ins_time AS insTime
+        FROM ins_product p
+        LEFT JOIN ins_sample s ON p.ins_sample_id = s.id
+        LEFT JOIN ins_order o ON s.ins_order_id = o.id
+        WHERE p.ins_result IS NOT NULL AND p.last_value IS NOT NULL
+        <if test="dto.itemName != null and dto.itemName != ''">
+            AND p.inspection_item = #{dto.itemName}
+        </if>
+        <if test="dto.sampleName != null and dto.sampleName != ''">
+            AND s.sample LIKE CONCAT('%', #{dto.sampleName}, '%')
+        </if>
+        <if test="dto.startDate != null and dto.startDate != ''">
+            AND o.ins_time >= #{dto.startDate}
+        </if>
+        <if test="dto.endDate != null and dto.endDate != ''">
+            AND o.ins_time &lt;= #{dto.endDate}
+        </if>
+        ORDER BY o.ins_time ASC
+    </select>
+
+    <!-- 鏌ヨ鍙�夋娴嬮」 -->
+    <select id="getItemNames" resultType="java.lang.String">
+        SELECT DISTINCT p.inspection_item
+        FROM ins_product p
+        LEFT JOIN ins_sample s ON p.ins_sample_id = s.id
+        WHERE p.ins_result IS NOT NULL AND p.last_value IS NOT NULL
+        <if test="dto.sampleName != null and dto.sampleName != ''">
+            AND s.sample LIKE CONCAT('%', #{dto.sampleName}, '%')
+        </if>
+        ORDER BY p.inspection_item ASC
+    </select>
+
+    <!-- 鏌ヨ鍙�夋牱鍝佸悕绉板垪琛� -->
+    <select id="getSampleNames" resultType="java.lang.String">
+        SELECT DISTINCT s.sample
+        FROM ins_product p
+        LEFT JOIN ins_sample s ON p.ins_sample_id = s.id
+        LEFT JOIN ins_order o ON s.ins_order_id = o.id
+        WHERE p.ins_result IS NOT NULL AND p.last_value IS NOT NULL
+        <if test="dto.itemName != null and dto.itemName != ''">
+            AND p.inspection_item = #{dto.itemName}
+        </if>
+        ORDER BY s.sample ASC
+    </select>
+
+</mapper>
\ No newline at end of file
diff --git a/report-server/src/main/resources/mapper/TestItemDataMapper.xml b/report-server/src/main/resources/mapper/TestItemDataMapper.xml
new file mode 100644
index 0000000..aa05fea
--- /dev/null
+++ b/report-server/src/main/resources/mapper/TestItemDataMapper.xml
@@ -0,0 +1,132 @@
+<?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.report.mapper.TestItemDataMapper">
+
+    <!-- 鍒嗛〉鏌ヨ妫�娴嬮」鐩暟鎹� -->
+    <select id="pageTestItemData" resultType="com.ruoyi.report.vo.TestItemDataVo">
+        SELECT
+            p.id AS productId,
+            s.id AS sampleId,
+            s.sample_code AS sampleCode,
+            s.sample AS sampleName,
+            o.production AS productionOrder,
+            o.entrust_code AS batchNo,
+            p.inspection_item AS itemName,
+            p.last_value AS lastValue,
+            p.ask AS standardValue,
+            p.ins_result AS insResult,
+            p.unit AS unit,
+            u.name AS insUser,
+            o.ins_time AS insTime,
+            r.code AS reportCode
+        FROM ins_product p
+        LEFT JOIN ins_sample s ON p.ins_sample_id = s.id
+        LEFT JOIN ins_order o ON s.ins_order_id = o.id
+        LEFT JOIN ins_report r ON o.id = r.ins_order_id
+        LEFT JOIN ins_product_user pu ON p.id = pu.ins_product_id
+        LEFT JOIN user u ON pu.create_user = u.id
+        WHERE 1=1
+        <if test="dto.productionOrder != null and dto.productionOrder != ''">
+            AND o.production LIKE CONCAT('%', #{dto.productionOrder}, '%')
+        </if>
+        <if test="dto.batchNo != null and dto.batchNo != ''">
+            AND o.entrust_code LIKE CONCAT('%', #{dto.batchNo}, '%')
+        </if>
+        <if test="dto.sampleCode != null and dto.sampleCode != ''">
+            AND s.sample_code LIKE CONCAT('%', #{dto.sampleCode}, '%')
+        </if>
+        <if test="dto.sampleName != null and dto.sampleName != ''">
+            AND s.sample LIKE CONCAT('%', #{dto.sampleName}, '%')
+        </if>
+        <if test="dto.itemName != null and dto.itemName != ''">
+            AND p.inspection_item LIKE CONCAT('%', #{dto.itemName}, '%')
+        </if>
+        <if test="dto.insState != null">
+            AND o.ins_state = #{dto.insState}
+        </if>
+        <if test="dto.startTime != null and dto.startTime != ''">
+            AND o.ins_time >= #{dto.startTime}
+        </if>
+        <if test="dto.endTime != null and dto.endTime != ''">
+            AND o.ins_time &lt;= #{dto.endTime}
+        </if>
+        ORDER BY o.ins_time DESC
+    </select>
+
+    <!-- 鏌ヨ妫�娴嬮」鐩鎯� -->
+    <select id="getDetail" resultType="com.ruoyi.report.vo.TestItemDataVo">
+        SELECT
+            p.id AS productId,
+            s.id AS sampleId,
+            s.sample_code AS sampleCode,
+            s.sample AS sampleName,
+            o.production AS productionOrder,
+            o.entrust_code AS batchNo,
+            p.inspection_item AS itemName,
+            p.last_value AS lastValue,
+            p.ask AS standardValue,
+            p.ins_result AS insResult,
+            p.unit AS unit,
+            u.name AS insUser,
+            o.ins_time AS insTime,
+            r.code AS reportCode
+        FROM ins_product p
+        LEFT JOIN ins_sample s ON p.ins_sample_id = s.id
+        LEFT JOIN ins_order o ON s.ins_order_id = o.id
+        LEFT JOIN ins_report r ON o.id = r.ins_order_id
+        LEFT JOIN ins_product_user pu ON p.id = pu.ins_product_id
+        LEFT JOIN user u ON pu.create_user = u.id
+        WHERE s.id = #{sampleId}
+        ORDER BY p.inspection_item ASC
+    </select>
+
+    <!-- 鏌ヨ妫�娴嬮」鍚嶇О鍒楄〃 -->
+    <select id="getItemNames" resultType="java.lang.String">
+        SELECT DISTINCT p.inspection_item
+        FROM ins_product p
+        LEFT JOIN ins_sample s ON p.ins_sample_id = s.id
+        LEFT JOIN ins_order o ON s.ins_order_id = o.id
+        WHERE 1=1
+        <if test="dto.sampleName != null and dto.sampleName != ''">
+            AND s.sample LIKE CONCAT('%', #{dto.sampleName}, '%')
+        </if>
+        <if test="dto.startTime != null and dto.startTime != ''">
+            AND o.ins_time >= #{dto.startTime}
+        </if>
+        <if test="dto.endTime != null and dto.endTime != ''">
+            AND o.ins_time &lt;= #{dto.endTime}
+        </if>
+        ORDER BY p.inspection_item ASC
+    </select>
+
+    <!-- 鏍规嵁鏍峰搧ID鍒楄〃鏌ヨ妫�娴嬫暟鎹� -->
+    <select id="listBySampleIds" resultType="com.ruoyi.report.vo.TestItemDataVo">
+        SELECT
+            p.id AS productId,
+            s.id AS sampleId,
+            s.sample_code AS sampleCode,
+            s.sample AS sampleName,
+            o.production AS productionOrder,
+            o.entrust_code AS batchNo,
+            p.inspection_item AS itemName,
+            p.last_value AS lastValue,
+            p.ask AS standardValue,
+            p.ins_result AS insResult,
+            p.unit AS unit,
+            u.name AS insUser,
+            o.ins_time AS insTime,
+            r.code AS reportCode
+        FROM ins_product p
+        LEFT JOIN ins_sample s ON p.ins_sample_id = s.id
+        LEFT JOIN ins_order o ON s.ins_order_id = o.id
+        LEFT JOIN ins_report r ON o.id = r.ins_order_id
+        LEFT JOIN ins_product_user pu ON p.id = pu.ins_product_id
+        LEFT JOIN user u ON pu.create_user = u.id
+        WHERE s.id IN
+        <foreach collection="sampleIds" item="id" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+        ORDER BY s.sample_code ASC, p.inspection_item ASC
+    </select>
+
+</mapper>
diff --git a/report-server/src/main/resources/mapper/WorkStatisticsMapper.xml b/report-server/src/main/resources/mapper/WorkStatisticsMapper.xml
new file mode 100644
index 0000000..80acaae
--- /dev/null
+++ b/report-server/src/main/resources/mapper/WorkStatisticsMapper.xml
@@ -0,0 +1,87 @@
+<?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.report.mapper.WorkStatisticsMapper">
+
+    <!-- 鎸変汉鍛樼粺璁� -->
+    <select id="getByUser" resultType="com.ruoyi.report.vo.WorkStatisticsVo">
+        SELECT
+            u.id AS userId,
+            u.name AS userName,
+            d.dept_name AS deptName,
+            COUNT(DISTINCT s.id) AS sampleCount,
+            COUNT(p.id) AS itemCount,
+            SUM(CASE WHEN o.ins_time IS NOT NULL AND o.ins_time &lt;= o.appointed THEN 1 ELSE 0 END) AS timelyCount,
+            SUM(CASE WHEN o.ins_time IS NOT NULL AND o.ins_time > o.appointed THEN 1 ELSE 0 END) AS overdueCount
+        FROM ins_product p
+        LEFT JOIN ins_sample s ON p.ins_sample_id = s.id
+        LEFT JOIN ins_order o ON s.ins_order_id = o.id
+        LEFT JOIN ins_product_user pu ON p.id = pu.ins_product_id
+        LEFT JOIN user u ON pu.create_user = u.id
+        LEFT JOIN sys_dept d ON u.dept_id = d.dept_id
+        WHERE p.ins_result IS NOT NULL
+        <if test="dto.startTime != null and dto.startTime != ''">
+            AND o.ins_time >= #{dto.startTime}
+        </if>
+        <if test="dto.endTime != null and dto.endTime != ''">
+            AND o.ins_time &lt;= #{dto.endTime}
+        </if>
+        <if test="dto.userId != null">
+            AND pu.create_user = #{dto.userId}
+        </if>
+        <if test="dto.deptId != null">
+            AND u.dept_id = #{dto.deptId}
+        </if>
+        GROUP BY u.id
+        ORDER BY sampleCount DESC
+    </select>
+
+    <!-- 鍙婃椂鐜囩粺璁� -->
+    <select id="getTimelyRate" resultType="java.util.Map">
+        SELECT
+            u.name AS userName,
+            COUNT(*) AS totalCount,
+            SUM(CASE WHEN o.ins_time IS NOT NULL AND o.ins_time &lt;= o.appointed THEN 1 ELSE 0 END) AS timelyCount,
+            ROUND(SUM(CASE WHEN o.ins_time IS NOT NULL AND o.ins_time &lt;= o.appointed THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 2) AS timelyRate
+        FROM ins_order o
+        LEFT JOIN ins_sample s ON o.id = s.ins_order_id
+        LEFT JOIN ins_product p ON s.id = p.ins_sample_id
+        LEFT JOIN user u ON o.prepare_user_id = u.id
+        WHERE o.ins_state = 5
+        <if test="dto.startTime != null and dto.startTime != ''">
+            AND o.ins_time >= #{dto.startTime}
+        </if>
+        <if test="dto.endTime != null and dto.endTime != ''">
+            AND o.ins_time &lt;= #{dto.endTime}
+        </if>
+        <if test="dto.deptId != null">
+            AND u.dept_id = #{dto.deptId}
+        </if>
+        GROUP BY u.id
+        ORDER BY timelyRate DESC
+    </select>
+
+    <!-- 宸ヤ綔瓒嬪娍鍥� -->
+    <select id="getTrend" resultType="java.util.Map">
+        SELECT
+            DATE_FORMAT(o.ins_time, '%Y-%m-%d') AS date,
+            COUNT(DISTINCT s.id) AS sampleCount,
+            COUNT(p.id) AS itemCount
+        FROM ins_product p
+        LEFT JOIN ins_sample s ON p.ins_sample_id = s.id
+        LEFT JOIN ins_order o ON s.ins_order_id = o.id
+        LEFT JOIN ins_product_user pu ON p.id = pu.ins_product_id
+        WHERE p.ins_result IS NOT NULL
+        <if test="dto.startTime != null and dto.startTime != ''">
+            AND o.ins_time >= #{dto.startTime}
+        </if>
+        <if test="dto.endTime != null and dto.endTime != ''">
+            AND o.ins_time &lt;= #{dto.endTime}
+        </if>
+        <if test="dto.userId != null">
+            AND pu.create_user = #{dto.userId}
+        </if>
+        GROUP BY DATE_FORMAT(o.ins_time, '%Y-%m-%d')
+        ORDER BY date ASC
+    </select>
+
+</mapper>
\ No newline at end of file
diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml
index 6c8e9a9..6261fe2 100644
--- a/ruoyi-admin/pom.xml
+++ b/ruoyi-admin/pom.xml
@@ -103,6 +103,12 @@
             <artifactId>cnas-personnel</artifactId>
         </dependency>
 
+        <!--鎶ヨ〃鍥捐〃妯″潡-->
+        <dependency>
+            <groupId>com.ruoyi</groupId>
+            <artifactId>report-server</artifactId>
+        </dependency>
+
 
     </dependencies>
 
diff --git a/ruoyi-admin/src/main/resources/application-druid.yml b/ruoyi-admin/src/main/resources/application-druid.yml
index 530a2b5..1c10da3 100644
--- a/ruoyi-admin/src/main/resources/application-druid.yml
+++ b/ruoyi-admin/src/main/resources/application-druid.yml
@@ -65,13 +65,13 @@
   # redis 閰嶇疆
   redis:
     # 鍦板潃
-    host: localhost
+    host: 47.114.74.44
     # 绔彛锛岄粯璁や负6379
-    port: 6379
+    port: 6399
     # 鏁版嵁搴撶储寮�
-    database: 0
+    database: 9
     #    # 瀵嗙爜
-    password: 123456
+    password:
     # 杩炴帴瓒呮椂鏃堕棿
     timeout: 10s
     lettuce:
diff --git a/ruoyi-admin/src/main/resources/report_chart_sql.sql b/ruoyi-admin/src/main/resources/report_chart_sql.sql
new file mode 100644
index 0000000..90bdbc6
--- /dev/null
+++ b/ruoyi-admin/src/main/resources/report_chart_sql.sql
@@ -0,0 +1,177 @@
+-- =====================================================
+-- 鎶ヨ〃鍥捐〃绠$悊妯″潡鏁版嵁搴撹剼鏈�
+-- 鎵ц鍓嶈澶囦唤鏁版嵁搴�
+-- 鍒涘缓鏃堕棿: 2026-06-04
+-- =====================================================
+
+-- =============================================
+-- 1. 璇煶鎾姤闃熷垪琛� (voice_queue) - 鏂板
+-- =============================================
+DROP TABLE IF EXISTS `voice_queue`;
+CREATE TABLE `voice_queue` (
+  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '涓婚敭ID',
+  `event_type` varchar(50) NOT NULL COMMENT '浜嬩欢绫诲瀷(sample_receive/task_assign/report_submit/emergency)',
+  `event_name` varchar(100) NOT NULL COMMENT '浜嬩欢鍚嶇О',
+  `details` varchar(500) DEFAULT '' COMMENT '浜嬩欢璇︽儏',
+  `voice_text` varchar(500) NOT NULL COMMENT '璇煶鎾姤鏂囨湰',
+  `priority` int DEFAULT 0 COMMENT '浼樺厛绾�(0鏅��/1閲嶈/2绱ф��)',
+  `status` tinyint DEFAULT 0 COMMENT '鐘舵��(0寰呮挱鎶�/1宸叉挱鎶�/2宸插彇娑�)',
+  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿',
+  PRIMARY KEY (`id`),
+  KEY `idx_status_priority` (`status`, `priority`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='璇煶鎾姤闃熷垪琛�';
+
+-- =============================================
+-- 2. 鎶ヨ〃閰嶇疆琛�
+-- =============================================
+DROP TABLE IF EXISTS `report_config`;
+CREATE TABLE `report_config` (
+  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '涓婚敭ID',
+  `report_name` varchar(100) NOT NULL COMMENT '鎶ヨ〃鍚嶇О',
+  `report_code` varchar(50) NOT NULL COMMENT '鎶ヨ〃缂栫爜',
+  `report_type` varchar(20) NOT NULL COMMENT '鎶ヨ〃绫诲瀷(sample_progress/test_item/sample_record/device_record/dashboard)',
+  `query_config` text COMMENT '鏌ヨ鏉′欢閰嶇疆JSON',
+  `column_config` text COMMENT '鍒楅厤缃甁SON',
+  `chart_config` text COMMENT '鍥捐〃閰嶇疆JSON',
+  `status` tinyint DEFAULT 1 COMMENT '鐘舵��(1鍚敤/0鍋滅敤)',
+  `create_by` varchar(64) DEFAULT '' COMMENT '鍒涘缓鑰�',
+  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+  `update_by` varchar(64) DEFAULT '' COMMENT '鏇存柊鑰�',
+  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿',
+  `remark` varchar(500) DEFAULT '' COMMENT '澶囨敞',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `uk_report_code` (`report_code`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='鎶ヨ〃閰嶇疆琛�';
+
+-- =============================================
+-- 3. 鍥捐〃閰嶇疆琛�
+-- =============================================
+DROP TABLE IF EXISTS `chart_config`;
+CREATE TABLE `chart_config` (
+  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '涓婚敭ID',
+  `chart_name` varchar(100) NOT NULL COMMENT '鍥捐〃鍚嶇О',
+  `chart_code` varchar(50) NOT NULL COMMENT '鍥捐〃缂栫爜',
+  `chart_type` varchar(20) NOT NULL COMMENT '鍥捐〃绫诲瀷(bar/line/pie/radar/spc/normal)',
+  `data_source` varchar(100) NOT NULL COMMENT '鏁版嵁婧怱QL鎴栨帴鍙f爣璇�',
+  `x_axis_field` varchar(50) COMMENT 'X杞村瓧娈�',
+  `y_axis_field` varchar(100) COMMENT 'Y杞村瓧娈�(JSON鏁扮粍)',
+  `query_params` text COMMENT '鏌ヨ鍙傛暟閰嶇疆JSON',
+  `chart_options` text COMMENT 'ECharts閰嶇疆JSON',
+  `status` tinyint DEFAULT 1 COMMENT '鐘舵��(1鍚敤/0鍋滅敤)',
+  `create_by` varchar(64) DEFAULT '' COMMENT '鍒涘缓鑰�',
+  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+  `update_by` varchar(64) DEFAULT '' COMMENT '鏇存柊鑰�',
+  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿',
+  `remark` varchar(500) DEFAULT '' COMMENT '澶囨敞',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `uk_chart_code` (`chart_code`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='鍥捐〃閰嶇疆琛�';
+
+-- =============================================
+-- 4. 鐪嬫澘閰嶇疆琛�
+-- =============================================
+DROP TABLE IF EXISTS `dashboard_config`;
+CREATE TABLE `dashboard_config` (
+  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '涓婚敭ID',
+  `dashboard_name` varchar(100) NOT NULL COMMENT '鐪嬫澘鍚嶇О',
+  `dashboard_code` varchar(50) NOT NULL COMMENT '鐪嬫澘缂栫爜',
+  `layout_config` text COMMENT '甯冨眬閰嶇疆JSON',
+  `components` text COMMENT '缁勪欢閰嶇疆JSON',
+  `voice_config` text COMMENT '璇煶鎾姤閰嶇疆JSON',
+  `refresh_interval` int DEFAULT 30 COMMENT '鍒锋柊闂撮殧(绉�)',
+  `status` tinyint DEFAULT 1 COMMENT '鐘舵��(1鍚敤/0鍋滅敤)',
+  `create_by` varchar(64) DEFAULT '' COMMENT '鍒涘缓鑰�',
+  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+  `update_by` varchar(64) DEFAULT '' COMMENT '鏇存柊鑰�',
+  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿',
+  `remark` varchar(500) DEFAULT '' COMMENT '澶囨敞',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `uk_dashboard_code` (`dashboard_code`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='鐪嬫澘閰嶇疆琛�';
+
+-- =============================================
+-- 5. 鑿滃崟鏉冮檺閰嶇疆
+-- =============================================
+-- 鎶ヨ〃鍥捐〃绠$悊锛堜竴绾х洰褰曪級- 鎺掑湪鏈�鍚庯紝order_num = 21
+INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `route_name`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`, `is_rersonal_button`) VALUES
+(3000, '鎶ヨ〃鍥捐〃绠$悊', 0, 21, 'reportChart', '', NULL, '', 1, 0, 'M', '0', '0', '', 'chart', 'admin', NOW(), '', NULL, '鎶ヨ〃鍥捐〃绠$悊鐩綍', 0),
+
+-- 鏁板瓧鍖栬闊崇湅鏉匡紙浜岀骇鐩綍锛�
+(3001, '鏁板瓧鍖栬闊崇湅鏉�', 3000, 1, 'dashboard', '', NULL, '', 1, 0, 'M', '0', '0', '', 'monitor', 'admin', NOW(), '', NULL, '鏁板瓧鍖栬闊崇湅鏉跨洰褰�', 0),
+
+-- 璇曢獙澶у巺
+(3002, '璇曢獙澶у巺', 3001, 1, 'testHall', 'report/dashboard/index', NULL, '', 1, 0, 'C', '0', '0', 'report:dashboard:list', 'monitor', 'admin', NOW(), '', NULL, '璇曢獙澶у巺鑿滃崟', 0),
+
+-- 鎶ヨ〃绠$悊锛堜簩绾х洰褰曪級
+(3010, '鎶ヨ〃绠$悊', 3000, 2, 'report', '', NULL, '', 1, 0, 'M', '0', '0', '', 'form', 'admin', NOW(), '', NULL, '鎶ヨ〃绠$悊鐩綍', 0),
+
+-- 鏍峰搧杩涘害鎶ヨ〃
+(3011, '鏍峰搧杩涘害鎶ヨ〃', 3010, 1, 'sampleProgress', 'report/sampleProgress/index', NULL, '', 1, 0, 'C', '0', '0', 'report:sampleProgress:list', 'table', 'admin', NOW(), '', NULL, '鏍峰搧杩涘害鎶ヨ〃鑿滃崟', 0),
+
+-- 妫�娴嬮」鐩暟鎹�
+(3012, '妫�娴嬮」鐩暟鎹�', 3010, 2, 'testItemData', 'report/testItemData/index', NULL, '', 1, 0, 'C', '0', '0', 'report:testItemData:list', 'table', 'admin', NOW(), '', NULL, '妫�娴嬮」鐩暟鎹彍鍗�', 0),
+
+-- 鏍峰搧棰嗘牱璁板綍
+(3013, '鏍峰搧棰嗘牱璁板綍', 3010, 3, 'sampleRecord', 'report/sampleRecord/index', NULL, '', 1, 0, 'C', '0', '0', 'report:sampleRecord:list', 'table', 'admin', NOW(), '', NULL, '鏍峰搧棰嗘牱璁板綍鑿滃崟', 0),
+
+-- 璁惧浣跨敤璁板綍
+(3014, '璁惧浣跨敤璁板綍', 3010, 4, 'deviceRecord', 'report/deviceRecord/index', NULL, '', 1, 0, 'C', '0', '0', 'report:deviceRecord:list', 'table', 'admin', NOW(), '', NULL, '璁惧浣跨敤璁板綍鑿滃崟', 0),
+
+-- 鏅鸿兘鍥捐〃锛堜簩绾х洰褰曪級
+(3020, '鏅鸿兘鍥捐〃', 3000, 3, 'chart', '', NULL, '', 1, 0, 'M', '0', '0', '', 'chart', 'admin', NOW(), '', NULL, '鏅鸿兘鍥捐〃鐩綍', 0),
+
+-- 宸ヤ綔缁熻
+(3021, '宸ヤ綔缁熻', 3020, 1, 'workStatistics', 'report/workStatistics/index', NULL, '', 1, 0, 'C', '0', '0', 'chart:workStatistics:list', 'peoples', 'admin', NOW(), '', NULL, '宸ヤ綔缁熻鑿滃崟', 0),
+
+-- 鍚堟牸鐜囩粺璁�
+(3022, '鍚堟牸鐜囩粺璁�', 3020, 2, 'passRate', 'report/passRate/index', NULL, '', 1, 0, 'C', '0', '0', 'chart:passRate:list', 'chart', 'admin', NOW(), '', NULL, '鍚堟牸鐜囩粺璁¤彍鍗�', 0),
+
+-- SPC鎺у埗鍥�
+(3023, 'SPC鎺у埗鍥�', 3020, 3, 'spcChart', 'report/spcChart/index', NULL, '', 1, 0, 'C', '0', '0', 'chart:spcChart:list', 'chart', 'admin', NOW(), '', NULL, 'SPC鎺у埗鍥捐彍鍗�', 0),
+
+-- 姝f�佸垎甯冨浘
+(3024, '姝f�佸垎甯冨浘', 3020, 4, 'normalDistribution', 'report/normalDistribution/index', NULL, '', 1, 0, 'C', '0', '0', 'chart:normalDistribution:list', 'chart', 'admin', NOW(), '', NULL, '姝f�佸垎甯冨浘鑿滃崟', 0);
+
+-- =============================================
+-- 6. 鍒濆鍖栫湅鏉块厤缃暟鎹�
+-- =============================================
+INSERT INTO `dashboard_config` (`dashboard_name`, `dashboard_code`, `layout_config`, `components`, `voice_config`, `refresh_interval`, `status`, `create_by`) VALUES
+('璇曢獙澶у巺鐪嬫澘', 'test_hall',
+'[{"i":"history","x":0,"y":0,"w":8,"h":4},{"i":"future","x":8,"y":0,"w":4,"h":4},{"i":"ranking","x":0,"y":4,"w":6,"h":3},{"i":"status","x":6,"y":4,"w":6,"h":3},{"i":"result","x":0,"y":7,"w":12,"h":3}]',
+'[{"id":"history","type":"calendar","title":"鍘嗗彶15澶╂娴嬩换鍔�","api":"/report/dashboard/history15Days"},{"id":"future","type":"calendar","title":"鏈潵15澶╂娴嬩换鍔�","api":"/report/dashboard/future15Days"},{"id":"ranking","type":"ranking","title":"鎻愪氦鎺掕","api":"/report/dashboard/ranking"},{"id":"status","type":"stat","title":"寰呭鐞嗙粺璁�","api":"/report/dashboard/overview"},{"id":"result","type":"chart","title":"妫�楠岀粨鏋滅粺璁�","api":"/report/dashboard/insResult"}]',
+'{"enabled":true,"events":["sample_receive","task_assign","report_submit","emergency"],"template":"绱ф�ラ�氱煡锛歿eventName}锛寋details}"}',
+30, 1, 'admin');
+
+-- =============================================
+-- 7. 鍒濆鍖栨姤琛ㄩ厤缃暟鎹�
+-- =============================================
+INSERT INTO `report_config` (`report_name`, `report_code`, `report_type`, `query_config`, `status`, `create_by`) VALUES
+('鏍峰搧杩涘害鎶ヨ〃', 'sample_progress', 'sample_progress', '[{"field":"entrustCode","label":"濮旀墭缂栧彿","type":"input"},{"field":"sampleCode","label":"鏍峰搧缂栧彿","type":"input"},{"field":"sampleName","label":"鏍峰搧鍚嶇О","type":"input"},{"field":"insState","label":"妫�娴嬬姸鎬�","type":"select","options":[{"value":0,"label":"寰呮"},{"value":1,"label":"妫�楠屼腑"},{"value":5,"label":"宸插畬鎴�"}]}]', 1, 'admin'),
+('妫�娴嬮」鐩暟鎹�', 'test_item_data', 'test_item', '[{"field":"productionOrder","label":"鐢熶骇璁㈠崟","type":"input"},{"field":"batchNo","label":"鎵规鍙�","type":"input"},{"field":"sampleCode","label":"鏍峰搧缂栧彿","type":"input"},{"field":"itemName","label":"妫�娴嬮」鐩�","type":"input"}]', 1, 'admin'),
+('鏍峰搧棰嗘牱璁板綍', 'sample_record', 'sample_record', '[{"field":"sampleCode","label":"鏍峰搧缂栧彿","type":"input"},{"field":"operateUser","label":"鎿嶄綔浜�","type":"input"},{"field":"startTime","label":"寮�濮嬫椂闂�","type":"date"},{"field":"endTime","label":"缁撴潫鏃堕棿","type":"date"}]', 1, 'admin'),
+('璁惧浣跨敤璁板綍', 'device_record', 'device_record', '[{"field":"deviceCode","label":"璁惧缂栧彿","type":"input"},{"field":"deviceName","label":"璁惧鍚嶇О","type":"input"},{"field":"useUser","label":"浣跨敤浜�","type":"input"}]', 1, 'admin');
+
+-- =============================================
+-- 8. 鍒濆鍖栧浘琛ㄩ厤缃暟鎹�
+-- =============================================
+INSERT INTO `chart_config` (`chart_name`, `chart_code`, `chart_type`, `data_source`, `x_axis_field`, `y_axis_field`, `status`, `create_by`) VALUES
+('宸ヤ綔缁熻鍥�', 'work_statistics', 'bar', '/chart/workStatistics/byUser', 'userName', '[{"field":"sampleCount","name":"鏍峰搧鏁伴噺"},{"field":"itemCount","name":"椤圭洰鏁伴噺"}]', 1, 'admin'),
+('鍙婃椂鐜囩粺璁�', 'timely_rate', 'line', '/chart/workStatistics/timelyRate', 'userName', '[{"field":"timelyRate","name":"鍙婃椂鐜�(%)"}]', 1, 'admin'),
+('鍘熸潗鏂欏悎鏍肩巼', 'raw_material_pass_rate', 'bar', '/chart/passRate/rawMaterial', 'sampleName', '[{"field":"passRate","name":"鍚堟牸鐜�(%)"}]', 1, 'admin'),
+('甯曠疮鎵樺浘', 'pareto', 'bar', '/chart/passRate/pareto', 'itemName', '[{"field":"unqualifiedCount","name":"涓嶅悎鏍兼暟"},{"field":"cumulativePercent","name":"绱鐧惧垎姣�"}]', 1, 'admin'),
+('宸ュ簭鍚堟牸鐜�', 'process_pass_rate', 'pie', '/chart/passRate/process', 'processName', '[{"field":"passRate","name":"鍚堟牸鐜�(%)"}]', 1, 'admin'),
+('SPC鎺у埗鍥�', 'spc_chart', 'line', '/chart/spc/analyze', 'subgroup', '[{"field":"xBar","name":"X-Bar"},{"field":"ucl","name":"UCL"},{"field":"lcl","name":"LCL"}]', 1, 'admin'),
+('姝f�佸垎甯冨浘', 'normal_distribution', 'line', '/chart/normalDistribution/analyze', 'value', '[{"field":"frequency","name":"棰戠巼"},{"field":"normalCurve","name":"姝f�佹洸绾�"}]', 1, 'admin');
+
+-- =============================================
+-- 9. 鍒濆鍖栬闊虫挱鎶ユ祴璇曟暟鎹�
+-- =============================================
+INSERT INTO `voice_queue` (`event_type`, `event_name`, `details`, `voice_text`, `priority`, `status`) VALUES
+('sample_receive', '鏍峰搧鎺ユ敹', '鏍峰搧A001宸插埌杈惧疄楠屽', '鏍峰搧A001宸插埌杈惧疄楠屽锛岃鍙婃椂棰嗗彇', 0, 0),
+('task_assign', '浠诲姟鍒嗛厤', '妫�娴嬩换鍔″凡鍒嗛厤缁欏紶涓�', '妫�娴嬩换鍔″凡鍒嗛厤缁欏紶涓夛紝璇峰敖蹇紑濮嬫娴�', 1, 0),
+('emergency', '绱ф�ラ�氱煡', '璁惧鏁呴殰闇�瑕佺淮淇�', '绱ф�ラ�氱煡锛氬叧閿娴嬭澶囧嚭鐜版晠闅滐紝璇风珛鍗宠仈绯荤淮淇汉鍛�', 2, 0);
+
+-- =====================================================
+-- 鎵ц瀹屾垚
+-- =====================================================
\ No newline at end of file

--
Gitblit v1.9.3