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 < 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 <= #{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 <= #{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 <= #{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 <= #{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 <= #{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 <= #{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 <= #{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 <= #{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 <= #{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 <= #{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 <= #{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 <= #{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 <= #{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 <= #{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 <= #{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 <= #{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 <= #{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 <= 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 <= #{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 <= o.appointed THEN 1 ELSE 0 END) AS timelyCount,
+ ROUND(SUM(CASE WHEN o.ins_time IS NOT NULL AND o.ins_time <= 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 <= #{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 <= #{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