From 06ed168bbe9bd100c460ae21a499dbaee04e5abb Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期五, 22 五月 2026 10:52:04 +0800
Subject: [PATCH] feat(lims): 新增数据分析与展示功能
---
src/main/java/com/ruoyi/lims/dto/LimsAnalysisOverviewDto.java | 34 ++
src/main/java/com/ruoyi/lims/dto/LimsTrendPointDto.java | 28 +
src/main/java/com/ruoyi/lims/controller/LimsDataAnalysisController.java | 51 +++
src/main/java/com/ruoyi/lims/service/LimsDataAnalysisService.java | 19 +
src/main/java/com/ruoyi/lims/dto/LimsComparisonItemDto.java | 28 +
src/main/java/com/ruoyi/lims/dto/LimsDataAnalysisQueryDto.java | 37 ++
src/main/java/com/ruoyi/lims/dto/LimsDataAnalysisDashboardDto.java | 38 ++
src/main/resources/application-dev.yml | 222 +++++++++++++
doc/lims-data-analysis-front-integration.md | 150 ++++++++
src/main/java/com/ruoyi/lims/dto/LimsQualityDistributionItemDto.java | 22 +
src/main/java/com/ruoyi/lims/service/impl/LimsDataAnalysisServiceImpl.java | 387 ++++++++++++++++++++++
11 files changed, 1,016 insertions(+), 0 deletions(-)
diff --git a/doc/lims-data-analysis-front-integration.md b/doc/lims-data-analysis-front-integration.md
new file mode 100644
index 0000000..d87214e
--- /dev/null
+++ b/doc/lims-data-analysis-front-integration.md
@@ -0,0 +1,150 @@
+# LIMS 鏁版嵁鍒嗘瀽涓庡睍绀� - 鍓嶇鑱旇皟鏂囨。
+
+## 1. 妯″潡璇存槑
+
+鏈鍦� `lims` 妯″潡鏂板浜嗏�滄暟鎹垎鏋愪笌灞曠ず鈥濊兘鍔涳紝瑕嗙洊锛�
+
+- 鏁版嵁瓒嬪娍鍒嗘瀽锛堟寜澶�/鎸夊皬鏃讹級
+- 鏁版嵁姣旇緝鍒嗘瀽锛堟寜鏁版嵁绫诲瀷/璁惧锛�
+- 鏁版嵁璐ㄩ噺鍒嗗竷缁熻
+- 姒傝鎸囨爣鐪嬫澘锛堟�婚噺銆佸紓甯搞�佸悎鏍肩巼銆侀璀︾瓑锛�
+
+鎺ュ彛缁熶竴鍓嶇紑锛歚/lims/dataAnalysis`
+
+---
+
+## 2. 閴存潈涓庨�氱敤绾﹀畾
+
+1. 閴存潈鏂瑰紡锛氭部鐢ㄧ郴缁熺幇鏈� Token锛坄Authorization` 璇锋眰澶达級銆�
+2. 杩斿洖鏍煎紡锛歚AjaxResult`
+
+```json
+{
+ "code": 200,
+ "msg": "鎿嶄綔鎴愬姛",
+ "data": {}
+}
+```
+
+3. 鏃堕棿鍙傛暟鏍煎紡锛歚yyyy-MM-dd HH:mm:ss`
+4. 榛樿鏃堕棿鑼冨洿锛氳嫢 `startTime/endTime` 閮戒笉浼狅紝鍒欓粯璁ゆ渶杩� 7 澶┿��
+
+---
+
+## 3. 鏌ヨ鍙傛暟锛堥�氱敤锛�
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|---|---|---|---|
+| startTime | string | 鍚� | 寮�濮嬫椂闂达紝鏍煎紡锛歚yyyy-MM-dd HH:mm:ss` |
+| endTime | string | 鍚� | 缁撴潫鏃堕棿锛屾牸寮忥細`yyyy-MM-dd HH:mm:ss` |
+| dataType | string | 鍚� | 鏁版嵁绫诲瀷锛歚temperature/humidity/pressure/flow/concentration` |
+| deviceCode | string | 鍚� | 璁惧缂栧彿 |
+| granularity | string | 鍚� | 瓒嬪娍绮掑害锛歚day/hour`锛岄粯璁� `day` |
+| dimension | string | 鍚� | 姣旇緝缁村害锛歚dataType/deviceName/deviceCode`锛岄粯璁� `dataType` |
+| topN | number | 鍚� | 姣旇緝鍒嗘瀽杩斿洖鏉℃暟锛岄粯璁� `10`锛屾渶澶� `50` |
+
+---
+
+## 4. 鎺ュ彛娓呭崟
+
+| 鎺ュ彛 | 鏂规硶 | 璇存槑 |
+|---|---|---|
+| `/lims/dataAnalysis/dashboard` | GET | 涓�娆¤繑鍥炵湅鏉垮叏閮ㄦ暟鎹紙鎺ㄨ崘锛� |
+| `/lims/dataAnalysis/overview` | GET | 浠呰繑鍥炴瑙堟寚鏍� |
+| `/lims/dataAnalysis/trend` | GET | 浠呰繑鍥炶秼鍔挎暟鎹� |
+| `/lims/dataAnalysis/comparison` | GET | 浠呰繑鍥炴瘮杈冨垎鏋愭暟鎹� |
+| `/lims/dataAnalysis/qualityDistribution` | GET | 浠呰繑鍥炶川閲忓垎甯冩暟鎹� |
+
+---
+
+## 5. 璇︾粏杩斿洖缁撴瀯
+
+### 5.1 `GET /lims/dataAnalysis/dashboard`
+
+`data` 缁撴瀯锛�
+
+```json
+{
+ "startTime": "2026-05-16 00:00:00",
+ "endTime": "2026-05-22 23:59:59",
+ "granularity": "day",
+ "dimension": "dataType",
+ "overview": {
+ "totalCollections": 1200,
+ "todayCollections": 210,
+ "abnormalCollections": 38,
+ "qualifiedRate": 94.17,
+ "inProgressExperiments": 6,
+ "warningMonitors": 3,
+ "inStockSamples": 168
+ },
+ "trend": [
+ {
+ "time": "2026-05-16",
+ "pointCount": 145,
+ "avgValue": 23.65,
+ "maxValue": 29.40,
+ "minValue": 20.10
+ }
+ ],
+ "comparison": [
+ {
+ "dimensionValue": "temperature",
+ "pointCount": 560,
+ "avgValue": 24.31,
+ "maxValue": 33.20,
+ "minValue": 18.90
+ }
+ ],
+ "qualityDistribution": [
+ {
+ "category": "qualified",
+ "pointCount": 1130,
+ "ratio": 94.17
+ },
+ {
+ "category": "abnormal",
+ "pointCount": 38,
+ "ratio": 3.17
+ },
+ {
+ "category": "pending",
+ "pointCount": 32,
+ "ratio": 2.67
+ }
+ ]
+}
+```
+
+瀛楁瑙i噴锛�
+
+- `overview.totalCollections`锛氭椂闂磋寖鍥村唴閲囬泦鎬绘潯鏁�
+- `overview.todayCollections`锛氭渶杩� 24 灏忔椂閲囬泦鏉℃暟
+- `overview.qualifiedRate`锛氬悎鏍肩巼锛堢櫨鍒嗘瘮锛�
+- `trend`锛氭姌绾垮浘鏁版嵁婧�
+- `comparison`锛氭煴鐘跺浘/妯悜鏉″舰鍥炬暟鎹簮
+- `qualityDistribution`锛氶ゼ鍥炬暟鎹簮
+
+---
+
+## 6. 鍓嶇璋冪敤寤鸿
+
+1. 椤甸潰鍒濆鍖栦紭鍏堣皟鐢� `dashboard`锛屽噺灏戣姹傛鏁般��
+2. 鐢ㄦ埛鍒囨崲绛涢�夋潯浠讹紙鏃堕棿銆佽澶囥�佹暟鎹被鍨嬶級鍚庯紝閲嶆柊璇锋眰 `dashboard`銆�
+3. 鑻ラ〉闈㈠垎鍖哄姞杞斤紝鍙垎鍒皟 `overview/trend/comparison/qualityDistribution`銆�
+4. `trend` 宸茶ˉ榻愭椂闂磋酱绌烘。浣嶏紙鏃犳暟鎹繑鍥� 0锛夛紝鍙洿鎺ョ粯鍒惰繛缁姌绾裤��
+
+---
+
+## 7. 璋冪敤绀轰緥
+
+```bash
+curl -G 'http://localhost:8080/lims/dataAnalysis/dashboard' \
+ --data-urlencode 'startTime=2026-05-16 00:00:00' \
+ --data-urlencode 'endTime=2026-05-22 23:59:59' \
+ --data-urlencode 'granularity=day' \
+ --data-urlencode 'dimension=dataType' \
+ --data-urlencode 'topN=10' \
+ -H 'Authorization: Bearer <token>'
+```
+
diff --git a/src/main/java/com/ruoyi/lims/controller/LimsDataAnalysisController.java b/src/main/java/com/ruoyi/lims/controller/LimsDataAnalysisController.java
new file mode 100644
index 0000000..31575f3
--- /dev/null
+++ b/src/main/java/com/ruoyi/lims/controller/LimsDataAnalysisController.java
@@ -0,0 +1,51 @@
+package com.ruoyi.lims.controller;
+
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.lims.dto.LimsDataAnalysisQueryDto;
+import com.ruoyi.lims.service.LimsDataAnalysisService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@AllArgsConstructor
+@RequestMapping("/lims/dataAnalysis")
+@Api(value = "LimsDataAnalysis", tags = "鏁版嵁鍒嗘瀽涓庡睍绀�")
+public class LimsDataAnalysisController {
+
+ private final LimsDataAnalysisService limsDataAnalysisService;
+
+ @GetMapping("/dashboard")
+ @ApiOperation("鏁版嵁鍒嗘瀽鐪嬫澘")
+ public AjaxResult dashboard(LimsDataAnalysisQueryDto queryDto) {
+ return AjaxResult.success(limsDataAnalysisService.dashboard(queryDto));
+ }
+
+ @GetMapping("/overview")
+ @ApiOperation("鏁版嵁鍒嗘瀽姒傝")
+ public AjaxResult overview(LimsDataAnalysisQueryDto queryDto) {
+ return AjaxResult.success(limsDataAnalysisService.overview(queryDto));
+ }
+
+ @GetMapping("/trend")
+ @ApiOperation("鏁版嵁瓒嬪娍鍒嗘瀽")
+ public AjaxResult trend(LimsDataAnalysisQueryDto queryDto) {
+ return AjaxResult.success(limsDataAnalysisService.trend(queryDto));
+ }
+
+ @GetMapping("/comparison")
+ @ApiOperation("鏁版嵁姣旇緝鍒嗘瀽")
+ public AjaxResult comparison(LimsDataAnalysisQueryDto queryDto) {
+ return AjaxResult.success(limsDataAnalysisService.comparison(queryDto));
+ }
+
+ @GetMapping("/qualityDistribution")
+ @ApiOperation("鏁版嵁璐ㄩ噺鍒嗗竷")
+ public AjaxResult qualityDistribution(LimsDataAnalysisQueryDto queryDto) {
+ return AjaxResult.success(limsDataAnalysisService.qualityDistribution(queryDto));
+ }
+}
+
diff --git a/src/main/java/com/ruoyi/lims/dto/LimsAnalysisOverviewDto.java b/src/main/java/com/ruoyi/lims/dto/LimsAnalysisOverviewDto.java
new file mode 100644
index 0000000..ee7dff7
--- /dev/null
+++ b/src/main/java/com/ruoyi/lims/dto/LimsAnalysisOverviewDto.java
@@ -0,0 +1,34 @@
+package com.ruoyi.lims.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+@ApiModel(description = "LIMS鏁版嵁鍒嗘瀽姒傝")
+public class LimsAnalysisOverviewDto {
+
+ @ApiModelProperty(value = "閲囬泦鎬绘潯鏁�")
+ private Long totalCollections;
+
+ @ApiModelProperty(value = "鏈�杩�24灏忔椂閲囬泦鏉℃暟")
+ private Long todayCollections;
+
+ @ApiModelProperty(value = "寮傚父鏁版嵁鏉℃暟")
+ private Long abnormalCollections;
+
+ @ApiModelProperty(value = "鏁版嵁鍚堟牸鐜�(%)")
+ private BigDecimal qualifiedRate;
+
+ @ApiModelProperty(value = "杩涜涓疄楠屾暟")
+ private Long inProgressExperiments;
+
+ @ApiModelProperty(value = "棰勮鐩戞帶鏁�")
+ private Long warningMonitors;
+
+ @ApiModelProperty(value = "鍦ㄥ簱鏍峰搧鏁�")
+ private Long inStockSamples;
+}
+
diff --git a/src/main/java/com/ruoyi/lims/dto/LimsComparisonItemDto.java b/src/main/java/com/ruoyi/lims/dto/LimsComparisonItemDto.java
new file mode 100644
index 0000000..9b357dc
--- /dev/null
+++ b/src/main/java/com/ruoyi/lims/dto/LimsComparisonItemDto.java
@@ -0,0 +1,28 @@
+package com.ruoyi.lims.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+@ApiModel(description = "LIMS姣旇緝鍒嗘瀽椤�")
+public class LimsComparisonItemDto {
+
+ @ApiModelProperty(value = "缁村害鍊�")
+ private String dimensionValue;
+
+ @ApiModelProperty(value = "鏁版嵁鐐规暟閲�")
+ private Long pointCount;
+
+ @ApiModelProperty(value = "骞冲潎鍊�")
+ private BigDecimal avgValue;
+
+ @ApiModelProperty(value = "鏈�澶у��")
+ private BigDecimal maxValue;
+
+ @ApiModelProperty(value = "鏈�灏忓��")
+ private BigDecimal minValue;
+}
+
diff --git a/src/main/java/com/ruoyi/lims/dto/LimsDataAnalysisDashboardDto.java b/src/main/java/com/ruoyi/lims/dto/LimsDataAnalysisDashboardDto.java
new file mode 100644
index 0000000..59d9e32
--- /dev/null
+++ b/src/main/java/com/ruoyi/lims/dto/LimsDataAnalysisDashboardDto.java
@@ -0,0 +1,38 @@
+package com.ruoyi.lims.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Data
+@ApiModel(description = "LIMS鏁版嵁鍒嗘瀽鐪嬫澘")
+public class LimsDataAnalysisDashboardDto {
+
+ @ApiModelProperty(value = "鍒嗘瀽寮�濮嬫椂闂�")
+ private LocalDateTime startTime;
+
+ @ApiModelProperty(value = "鍒嗘瀽缁撴潫鏃堕棿")
+ private LocalDateTime endTime;
+
+ @ApiModelProperty(value = "瓒嬪娍绮掑害")
+ private String granularity;
+
+ @ApiModelProperty(value = "姣旇緝缁村害")
+ private String dimension;
+
+ @ApiModelProperty(value = "姒傝鏁版嵁")
+ private LimsAnalysisOverviewDto overview;
+
+ @ApiModelProperty(value = "瓒嬪娍鏁版嵁")
+ private List<LimsTrendPointDto> trend;
+
+ @ApiModelProperty(value = "姣旇緝鏁版嵁")
+ private List<LimsComparisonItemDto> comparison;
+
+ @ApiModelProperty(value = "璐ㄩ噺鍒嗗竷鏁版嵁")
+ private List<LimsQualityDistributionItemDto> qualityDistribution;
+}
+
diff --git a/src/main/java/com/ruoyi/lims/dto/LimsDataAnalysisQueryDto.java b/src/main/java/com/ruoyi/lims/dto/LimsDataAnalysisQueryDto.java
new file mode 100644
index 0000000..0e42185
--- /dev/null
+++ b/src/main/java/com/ruoyi/lims/dto/LimsDataAnalysisQueryDto.java
@@ -0,0 +1,37 @@
+package com.ruoyi.lims.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+@Data
+@ApiModel(description = "LIMS鏁版嵁鍒嗘瀽鏌ヨ鍙傛暟")
+public class LimsDataAnalysisQueryDto {
+
+ @ApiModelProperty(value = "寮�濮嬫椂闂达紝鏍煎紡锛歽yyy-MM-dd HH:mm:ss")
+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private LocalDateTime startTime;
+
+ @ApiModelProperty(value = "缁撴潫鏃堕棿锛屾牸寮忥細yyyy-MM-dd HH:mm:ss")
+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private LocalDateTime endTime;
+
+ @ApiModelProperty(value = "鏁版嵁绫诲瀷锛歵emperature/humidity/pressure/flow/concentration")
+ private String dataType;
+
+ @ApiModelProperty(value = "璁惧缂栧彿")
+ private String deviceCode;
+
+ @ApiModelProperty(value = "瓒嬪娍绮掑害锛歞ay/hour锛岄粯璁ay")
+ private String granularity;
+
+ @ApiModelProperty(value = "姣旇緝缁村害锛歞ataType/deviceName/deviceCode锛岄粯璁ataType")
+ private String dimension;
+
+ @ApiModelProperty(value = "姣旇緝鍒嗘瀽杩斿洖鏉℃暟锛岄粯璁�10锛屾渶澶�50")
+ private Integer topN;
+}
+
diff --git a/src/main/java/com/ruoyi/lims/dto/LimsQualityDistributionItemDto.java b/src/main/java/com/ruoyi/lims/dto/LimsQualityDistributionItemDto.java
new file mode 100644
index 0000000..1afe724
--- /dev/null
+++ b/src/main/java/com/ruoyi/lims/dto/LimsQualityDistributionItemDto.java
@@ -0,0 +1,22 @@
+package com.ruoyi.lims.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+@ApiModel(description = "LIMS璐ㄩ噺鍒嗗竷椤�")
+public class LimsQualityDistributionItemDto {
+
+ @ApiModelProperty(value = "鏁版嵁璐ㄩ噺鍒嗙被")
+ private String category;
+
+ @ApiModelProperty(value = "鏁伴噺")
+ private Long pointCount;
+
+ @ApiModelProperty(value = "鍗犳瘮(%)")
+ private BigDecimal ratio;
+}
+
diff --git a/src/main/java/com/ruoyi/lims/dto/LimsTrendPointDto.java b/src/main/java/com/ruoyi/lims/dto/LimsTrendPointDto.java
new file mode 100644
index 0000000..6cbab7b
--- /dev/null
+++ b/src/main/java/com/ruoyi/lims/dto/LimsTrendPointDto.java
@@ -0,0 +1,28 @@
+package com.ruoyi.lims.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+@ApiModel(description = "LIMS瓒嬪娍鍒嗘瀽鐐�")
+public class LimsTrendPointDto {
+
+ @ApiModelProperty(value = "鏃堕棿缁村害鍊�")
+ private String time;
+
+ @ApiModelProperty(value = "鏁版嵁鐐规暟閲�")
+ private Long pointCount;
+
+ @ApiModelProperty(value = "骞冲潎鍊�")
+ private BigDecimal avgValue;
+
+ @ApiModelProperty(value = "鏈�澶у��")
+ private BigDecimal maxValue;
+
+ @ApiModelProperty(value = "鏈�灏忓��")
+ private BigDecimal minValue;
+}
+
diff --git a/src/main/java/com/ruoyi/lims/service/LimsDataAnalysisService.java b/src/main/java/com/ruoyi/lims/service/LimsDataAnalysisService.java
new file mode 100644
index 0000000..efa38ce
--- /dev/null
+++ b/src/main/java/com/ruoyi/lims/service/LimsDataAnalysisService.java
@@ -0,0 +1,19 @@
+package com.ruoyi.lims.service;
+
+import com.ruoyi.lims.dto.*;
+
+import java.util.List;
+
+public interface LimsDataAnalysisService {
+
+ LimsDataAnalysisDashboardDto dashboard(LimsDataAnalysisQueryDto queryDto);
+
+ LimsAnalysisOverviewDto overview(LimsDataAnalysisQueryDto queryDto);
+
+ List<LimsTrendPointDto> trend(LimsDataAnalysisQueryDto queryDto);
+
+ List<LimsComparisonItemDto> comparison(LimsDataAnalysisQueryDto queryDto);
+
+ List<LimsQualityDistributionItemDto> qualityDistribution(LimsDataAnalysisQueryDto queryDto);
+}
+
diff --git a/src/main/java/com/ruoyi/lims/service/impl/LimsDataAnalysisServiceImpl.java b/src/main/java/com/ruoyi/lims/service/impl/LimsDataAnalysisServiceImpl.java
new file mode 100644
index 0000000..6789689
--- /dev/null
+++ b/src/main/java/com/ruoyi/lims/service/impl/LimsDataAnalysisServiceImpl.java
@@ -0,0 +1,387 @@
+package com.ruoyi.lims.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.ruoyi.lims.dto.*;
+import com.ruoyi.lims.mapper.DataCollectionMapper;
+import com.ruoyi.lims.mapper.ExperimentMapper;
+import com.ruoyi.lims.mapper.RealtimeMonitorMapper;
+import com.ruoyi.lims.mapper.SampleMapper;
+import com.ruoyi.lims.pojo.DataCollection;
+import com.ruoyi.lims.pojo.Experiment;
+import com.ruoyi.lims.pojo.RealtimeMonitor;
+import com.ruoyi.lims.pojo.Sample;
+import com.ruoyi.lims.service.LimsDataAnalysisService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Service
+@RequiredArgsConstructor
+@Transactional(rollbackFor = Exception.class)
+public class LimsDataAnalysisServiceImpl implements LimsDataAnalysisService {
+
+ private static final DateTimeFormatter DAY_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+ private static final DateTimeFormatter HOUR_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:00:00");
+
+ private final DataCollectionMapper dataCollectionMapper;
+ private final ExperimentMapper experimentMapper;
+ private final RealtimeMonitorMapper realtimeMonitorMapper;
+ private final SampleMapper sampleMapper;
+
+ @Override
+ public LimsDataAnalysisDashboardDto dashboard(LimsDataAnalysisQueryDto queryDto) {
+ LimsDataAnalysisQueryDto query = normalizeQuery(queryDto);
+ LimsDataAnalysisDashboardDto dashboardDto = new LimsDataAnalysisDashboardDto();
+ dashboardDto.setStartTime(query.getStartTime());
+ dashboardDto.setEndTime(query.getEndTime());
+ dashboardDto.setGranularity(query.getGranularity());
+ dashboardDto.setDimension(query.getDimension());
+ dashboardDto.setOverview(buildOverview(query));
+ dashboardDto.setTrend(buildTrend(query));
+ dashboardDto.setComparison(buildComparison(query));
+ dashboardDto.setQualityDistribution(buildQualityDistribution(query));
+ return dashboardDto;
+ }
+
+ @Override
+ public LimsAnalysisOverviewDto overview(LimsDataAnalysisQueryDto queryDto) {
+ return buildOverview(normalizeQuery(queryDto));
+ }
+
+ @Override
+ public List<LimsTrendPointDto> trend(LimsDataAnalysisQueryDto queryDto) {
+ return buildTrend(normalizeQuery(queryDto));
+ }
+
+ @Override
+ public List<LimsComparisonItemDto> comparison(LimsDataAnalysisQueryDto queryDto) {
+ return buildComparison(normalizeQuery(queryDto));
+ }
+
+ @Override
+ public List<LimsQualityDistributionItemDto> qualityDistribution(LimsDataAnalysisQueryDto queryDto) {
+ return buildQualityDistribution(normalizeQuery(queryDto));
+ }
+
+ private LimsAnalysisOverviewDto buildOverview(LimsDataAnalysisQueryDto query) {
+ QueryWrapper<DataCollection> totalWrapper = createDataCollectionFilter(query);
+ Long totalCollections = dataCollectionMapper.selectCount(totalWrapper);
+
+ QueryWrapper<DataCollection> abnormalWrapper = createDataCollectionFilter(query);
+ abnormalWrapper.eq("data_quality", "abnormal");
+ Long abnormalCollections = dataCollectionMapper.selectCount(abnormalWrapper);
+
+ QueryWrapper<DataCollection> qualifiedWrapper = createDataCollectionFilter(query);
+ qualifiedWrapper.eq("data_quality", "qualified");
+ Long qualifiedCollections = dataCollectionMapper.selectCount(qualifiedWrapper);
+
+ QueryWrapper<DataCollection> todayWrapper = new QueryWrapper<>();
+ todayWrapper.eq("del_flag", "0")
+ .ge("collection_time", LocalDateTime.now().minusHours(24))
+ .le("collection_time", LocalDateTime.now());
+ Long todayCollections = dataCollectionMapper.selectCount(todayWrapper);
+
+ Long inProgressExperiments = experimentMapper.selectCount(new QueryWrapper<Experiment>()
+ .eq("del_flag", "0")
+ .eq("experiment_status", "inProgress"));
+
+ Long warningMonitors = realtimeMonitorMapper.selectCount(new QueryWrapper<RealtimeMonitor>()
+ .eq("del_flag", "0")
+ .in("alert_status", Arrays.asList("warning", "alert")));
+
+ Long inStockSamples = sampleMapper.selectCount(new QueryWrapper<Sample>()
+ .eq("del_flag", "0")
+ .eq("sample_status", "inStock"));
+
+ LimsAnalysisOverviewDto dto = new LimsAnalysisOverviewDto();
+ dto.setTotalCollections(defaultLong(totalCollections));
+ dto.setTodayCollections(defaultLong(todayCollections));
+ dto.setAbnormalCollections(defaultLong(abnormalCollections));
+ dto.setInProgressExperiments(defaultLong(inProgressExperiments));
+ dto.setWarningMonitors(defaultLong(warningMonitors));
+ dto.setInStockSamples(defaultLong(inStockSamples));
+ dto.setQualifiedRate(calculateRate(qualifiedCollections, totalCollections));
+ return dto;
+ }
+
+ private List<LimsTrendPointDto> buildTrend(LimsDataAnalysisQueryDto query) {
+ String groupExpression = resolveTimeGroupExpression(query.getGranularity());
+ QueryWrapper<DataCollection> wrapper = createDataCollectionFilter(query);
+ wrapper.select(
+ groupExpression + " AS time_key",
+ "COUNT(1) AS point_count",
+ "AVG(collection_value) AS avg_value",
+ "MAX(collection_value) AS max_value",
+ "MIN(collection_value) AS min_value"
+ )
+ .groupBy(groupExpression)
+ .orderByAsc("time_key");
+
+ List<Map<String, Object>> rawRows = dataCollectionMapper.selectMaps(wrapper);
+ Map<String, LimsTrendPointDto> trendMap = rawRows.stream().map(this::mapToTrendPoint)
+ .collect(Collectors.toMap(LimsTrendPointDto::getTime, item -> item, (a, b) -> b));
+
+ List<String> axis = buildTimeAxis(query);
+ List<LimsTrendPointDto> result = new ArrayList<>();
+ for (String key : axis) {
+ LimsTrendPointDto point = trendMap.get(key);
+ if (point == null) {
+ point = new LimsTrendPointDto();
+ point.setTime(key);
+ point.setPointCount(0L);
+ point.setAvgValue(BigDecimal.ZERO);
+ point.setMaxValue(BigDecimal.ZERO);
+ point.setMinValue(BigDecimal.ZERO);
+ }
+ result.add(point);
+ }
+ return result;
+ }
+
+ private List<LimsComparisonItemDto> buildComparison(LimsDataAnalysisQueryDto query) {
+ String dimensionColumn = resolveDimensionColumn(query.getDimension());
+
+ QueryWrapper<DataCollection> wrapper = createDataCollectionFilter(query);
+ wrapper.isNotNull(dimensionColumn)
+ .ne(dimensionColumn, "")
+ .select(
+ dimensionColumn + " AS dimension_value",
+ "COUNT(1) AS point_count",
+ "AVG(collection_value) AS avg_value",
+ "MAX(collection_value) AS max_value",
+ "MIN(collection_value) AS min_value"
+ )
+ .groupBy(dimensionColumn);
+
+ List<LimsComparisonItemDto> result = dataCollectionMapper.selectMaps(wrapper).stream()
+ .map(this::mapToComparisonItem)
+ .sorted(Comparator.comparing(LimsComparisonItemDto::getPointCount, Comparator.nullsLast(Long::compareTo)).reversed())
+ .collect(Collectors.toList());
+
+ int limit = query.getTopN() == null ? 10 : query.getTopN();
+ if (result.size() > limit) {
+ return result.subList(0, limit);
+ }
+ return result;
+ }
+
+ private List<LimsQualityDistributionItemDto> buildQualityDistribution(LimsDataAnalysisQueryDto query) {
+ QueryWrapper<DataCollection> wrapper = createDataCollectionFilter(query);
+ wrapper.isNotNull("data_quality")
+ .ne("data_quality", "")
+ .select("data_quality AS category", "COUNT(1) AS point_count")
+ .groupBy("data_quality");
+
+ List<LimsQualityDistributionItemDto> result = dataCollectionMapper.selectMaps(wrapper).stream()
+ .map(this::mapToQualityDistributionItem)
+ .sorted(Comparator.comparing(LimsQualityDistributionItemDto::getPointCount, Comparator.nullsLast(Long::compareTo)).reversed())
+ .collect(Collectors.toList());
+
+ long total = result.stream().map(LimsQualityDistributionItemDto::getPointCount).filter(Objects::nonNull).mapToLong(Long::longValue).sum();
+ for (LimsQualityDistributionItemDto item : result) {
+ item.setRatio(calculateRate(item.getPointCount(), total));
+ }
+ return result;
+ }
+
+ private QueryWrapper<DataCollection> createDataCollectionFilter(LimsDataAnalysisQueryDto query) {
+ QueryWrapper<DataCollection> wrapper = new QueryWrapper<>();
+ wrapper.eq("del_flag", "0")
+ .ge("collection_time", query.getStartTime())
+ .le("collection_time", query.getEndTime());
+ if (StringUtils.hasText(query.getDataType())) {
+ wrapper.eq("data_type", query.getDataType().trim());
+ }
+ if (StringUtils.hasText(query.getDeviceCode())) {
+ wrapper.eq("device_code", query.getDeviceCode().trim());
+ }
+ return wrapper;
+ }
+
+ private String resolveTimeGroupExpression(String granularity) {
+ if ("hour".equalsIgnoreCase(granularity)) {
+ return "DATE_FORMAT(collection_time, '%Y-%m-%d %H:00:00')";
+ }
+ return "DATE_FORMAT(collection_time, '%Y-%m-%d')";
+ }
+
+ private String resolveDimensionColumn(String dimension) {
+ if ("deviceName".equalsIgnoreCase(dimension)) {
+ return "device_name";
+ }
+ if ("deviceCode".equalsIgnoreCase(dimension)) {
+ return "device_code";
+ }
+ return "data_type";
+ }
+
+ private List<String> buildTimeAxis(LimsDataAnalysisQueryDto query) {
+ List<String> axis = new ArrayList<>();
+ if ("hour".equalsIgnoreCase(query.getGranularity())) {
+ LocalDateTime cursor = query.getStartTime().withMinute(0).withSecond(0).withNano(0);
+ LocalDateTime end = query.getEndTime().withMinute(0).withSecond(0).withNano(0);
+ while (!cursor.isAfter(end)) {
+ axis.add(cursor.format(HOUR_FORMATTER));
+ cursor = cursor.plusHours(1);
+ }
+ return axis;
+ }
+ LocalDate cursor = query.getStartTime().toLocalDate();
+ LocalDate end = query.getEndTime().toLocalDate();
+ while (!cursor.isAfter(end)) {
+ axis.add(cursor.format(DAY_FORMATTER));
+ cursor = cursor.plusDays(1);
+ }
+ return axis;
+ }
+
+ private LimsTrendPointDto mapToTrendPoint(Map<String, Object> row) {
+ LimsTrendPointDto point = new LimsTrendPointDto();
+ point.setTime(asString(readIgnoreCase(row, "time_key")));
+ point.setPointCount(defaultLong(asLong(readIgnoreCase(row, "point_count"))));
+ point.setAvgValue(scale(asBigDecimal(readIgnoreCase(row, "avg_value"))));
+ point.setMaxValue(scale(asBigDecimal(readIgnoreCase(row, "max_value"))));
+ point.setMinValue(scale(asBigDecimal(readIgnoreCase(row, "min_value"))));
+ return point;
+ }
+
+ private LimsComparisonItemDto mapToComparisonItem(Map<String, Object> row) {
+ LimsComparisonItemDto item = new LimsComparisonItemDto();
+ item.setDimensionValue(asString(readIgnoreCase(row, "dimension_value")));
+ item.setPointCount(defaultLong(asLong(readIgnoreCase(row, "point_count"))));
+ item.setAvgValue(scale(asBigDecimal(readIgnoreCase(row, "avg_value"))));
+ item.setMaxValue(scale(asBigDecimal(readIgnoreCase(row, "max_value"))));
+ item.setMinValue(scale(asBigDecimal(readIgnoreCase(row, "min_value"))));
+ return item;
+ }
+
+ private LimsQualityDistributionItemDto mapToQualityDistributionItem(Map<String, Object> row) {
+ LimsQualityDistributionItemDto item = new LimsQualityDistributionItemDto();
+ item.setCategory(asString(readIgnoreCase(row, "category")));
+ item.setPointCount(defaultLong(asLong(readIgnoreCase(row, "point_count"))));
+ item.setRatio(BigDecimal.ZERO);
+ return item;
+ }
+
+ private Object readIgnoreCase(Map<String, Object> row, String key) {
+ if (row.containsKey(key)) {
+ return row.get(key);
+ }
+ for (Map.Entry<String, Object> entry : row.entrySet()) {
+ if (entry.getKey() != null && entry.getKey().equalsIgnoreCase(key)) {
+ return entry.getValue();
+ }
+ }
+ return null;
+ }
+
+ private BigDecimal calculateRate(Number part, Number total) {
+ if (part == null || total == null || total.longValue() == 0L) {
+ return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
+ }
+ return BigDecimal.valueOf(part.longValue())
+ .multiply(BigDecimal.valueOf(100))
+ .divide(BigDecimal.valueOf(total.longValue()), 2, RoundingMode.HALF_UP);
+ }
+
+ private BigDecimal scale(BigDecimal value) {
+ if (value == null) {
+ return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
+ }
+ return value.setScale(2, RoundingMode.HALF_UP);
+ }
+
+ private String asString(Object value) {
+ return value == null ? "" : String.valueOf(value);
+ }
+
+ private Long asLong(Object value) {
+ if (value == null) {
+ return 0L;
+ }
+ if (value instanceof Number) {
+ return ((Number) value).longValue();
+ }
+ return Long.parseLong(String.valueOf(value));
+ }
+
+ private BigDecimal asBigDecimal(Object value) {
+ if (value == null) {
+ return BigDecimal.ZERO;
+ }
+ if (value instanceof BigDecimal) {
+ return (BigDecimal) value;
+ }
+ if (value instanceof Number) {
+ return BigDecimal.valueOf(((Number) value).doubleValue());
+ }
+ return new BigDecimal(String.valueOf(value));
+ }
+
+ private Long defaultLong(Long value) {
+ return value == null ? 0L : value;
+ }
+
+ private LimsDataAnalysisQueryDto normalizeQuery(LimsDataAnalysisQueryDto source) {
+ LimsDataAnalysisQueryDto query = new LimsDataAnalysisQueryDto();
+ if (source != null) {
+ query.setStartTime(source.getStartTime());
+ query.setEndTime(source.getEndTime());
+ query.setDataType(source.getDataType());
+ query.setDeviceCode(source.getDeviceCode());
+ query.setGranularity(source.getGranularity());
+ query.setDimension(source.getDimension());
+ query.setTopN(source.getTopN());
+ }
+
+ LocalDateTime now = LocalDateTime.now().withSecond(0).withNano(0);
+ if (query.getStartTime() == null && query.getEndTime() == null) {
+ query.setEndTime(now);
+ query.setStartTime(now.minusDays(6).withHour(0).withMinute(0));
+ } else if (query.getStartTime() == null) {
+ query.setStartTime(query.getEndTime().minusDays(6).withHour(0).withMinute(0));
+ } else if (query.getEndTime() == null) {
+ query.setEndTime(query.getStartTime().plusDays(6).withHour(23).withMinute(59));
+ }
+
+ if (query.getStartTime().isAfter(query.getEndTime())) {
+ LocalDateTime temp = query.getStartTime();
+ query.setStartTime(query.getEndTime());
+ query.setEndTime(temp);
+ }
+
+ if (!"hour".equalsIgnoreCase(query.getGranularity()) && !"day".equalsIgnoreCase(query.getGranularity())) {
+ query.setGranularity("day");
+ } else {
+ query.setGranularity(query.getGranularity().toLowerCase(Locale.ROOT));
+ }
+
+ if (!"deviceName".equalsIgnoreCase(query.getDimension())
+ && !"deviceCode".equalsIgnoreCase(query.getDimension())
+ && !"dataType".equalsIgnoreCase(query.getDimension())) {
+ query.setDimension("dataType");
+ } else if ("devicename".equalsIgnoreCase(query.getDimension())) {
+ query.setDimension("deviceName");
+ } else if ("devicecode".equalsIgnoreCase(query.getDimension())) {
+ query.setDimension("deviceCode");
+ } else {
+ query.setDimension("dataType");
+ }
+
+ if (query.getTopN() == null || query.getTopN() <= 0) {
+ query.setTopN(10);
+ } else if (query.getTopN() > 50) {
+ query.setTopN(50);
+ }
+ return query;
+ }
+}
diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml
new file mode 100644
index 0000000..d2a0141
--- /dev/null
+++ b/src/main/resources/application-dev.yml
@@ -0,0 +1,222 @@
+# 鑺-浠撳偍鐗╂祦绯荤粺椤圭洰鐩稿叧閰嶇疆
+inspur:
+ appId: a3b0e3f1-7210-4ed0-8eed-c6c443e04e36
+ appSecret: 7aab6b10061962e861ab69596eec6c037daffd2b56cdf518df8554ff56daa9c8
+ruoyi:
+ # 鍚嶇О
+ name: RuoYi
+ # 鐗堟湰
+ version: 3.8.9
+ # 鐗堟潈骞翠唤
+ copyrightYear: 2025
+ # 鏂囦欢璺緞 绀轰緥锛� Windows閰嶇疆D:/ruoyi/uploadPath锛孡inux閰嶇疆 /home/ruoyi/uploadPath锛�
+ profile: /center-lims/mis/file
+
+ # 鑾峰彇ip鍦板潃寮�鍏�
+ addressEnabled: false
+ # 楠岃瘉鐮佺被鍨� math 鏁板瓧璁$畻 char 瀛楃楠岃瘉
+ captchaType: math
+
+# 寮�鍙戠幆澧冮厤缃�
+server:
+ # 鏈嶅姟鍣ㄧ殑HTTP绔彛锛岄粯璁や负8080
+ port: 8080
+ servlet:
+ # 搴旂敤鐨勮闂矾寰�
+ context-path: /
+ tomcat:
+ # tomcat鐨刄RI缂栫爜
+ uri-encoding: UTF-8
+ # 杩炴帴鏁版弧鍚庣殑鎺掗槦鏁帮紝榛樿涓�100
+ accept-count: 1000
+ threads:
+ # tomcat鏈�澶х嚎绋嬫暟锛岄粯璁や负200
+ max: 800
+ # Tomcat鍚姩鍒濆鍖栫殑绾跨▼鏁帮紝榛樿鍊�10
+ min-spare: 100
+
+# 鏃ュ織閰嶇疆
+logging:
+ level:
+ com.ruoyi: warn
+ org.springframework: warn
+
+minio:
+ endpoint: http://114.132.189.42/
+ port: 7019
+ secure: false
+ accessKey: admin
+ secretKey: 12345678
+ preview-expiry: 24 # 棰勮鍦板潃榛樿24灏忔椂
+ default-bucket: uploadPath
+# 鐢ㄦ埛閰嶇疆
+user:
+ password:
+ # 瀵嗙爜鏈�澶ч敊璇鏁�
+ maxRetryCount: 5
+ # 瀵嗙爜閿佸畾鏃堕棿锛堥粯璁�10鍒嗛挓锛�
+ lockTime: 10
+
+# Spring閰嶇疆
+spring:
+ datasource:
+ type: com.alibaba.druid.pool.DruidDataSource
+ driverClassName: com.mysql.cj.jdbc.Driver
+ druid:
+ # 涓诲簱鏁版嵁婧�
+ master:
+ url: jdbc:mysql://127.0.0.1:3306/lims-ruoyi?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+ username: root
+ password: 123456
+ # 浠庡簱鏁版嵁婧�
+ slave:
+ # 浠庢暟鎹簮寮�鍏�/榛樿鍏抽棴
+ enabled: false
+ url:
+ username:
+ password:
+ # 鍒濆杩炴帴鏁�
+ initialSize: 5
+ # 鏈�灏忚繛鎺ユ睜鏁伴噺
+ minIdle: 10
+ # 鏈�澶ц繛鎺ユ睜鏁伴噺
+ maxActive: 20
+ # 閰嶇疆鑾峰彇杩炴帴绛夊緟瓒呮椂鐨勬椂闂�
+ maxWait: 60000
+ # 閰嶇疆杩炴帴瓒呮椂鏃堕棿
+ connectTimeout: 30000
+ # 閰嶇疆缃戠粶瓒呮椂鏃堕棿
+ socketTimeout: 60000
+ # 閰嶇疆闂撮殧澶氫箙鎵嶈繘琛屼竴娆℃娴嬶紝妫�娴嬮渶瑕佸叧闂殑绌洪棽杩炴帴锛屽崟浣嶆槸姣
+ timeBetweenEvictionRunsMillis: 60000
+ # 閰嶇疆涓�涓繛鎺ュ湪姹犱腑鏈�灏忕敓瀛樼殑鏃堕棿锛屽崟浣嶆槸姣
+ minEvictableIdleTimeMillis: 300000
+ # 閰嶇疆涓�涓繛鎺ュ湪姹犱腑鏈�澶х敓瀛樼殑鏃堕棿锛屽崟浣嶆槸姣
+ maxEvictableIdleTimeMillis: 900000
+ # 閰嶇疆妫�娴嬭繛鎺ユ槸鍚︽湁鏁�
+ validationQuery: SELECT 1 FROM DUAL
+ testWhileIdle: true
+ testOnBorrow: false
+ testOnReturn: false
+ webStatFilter:
+ enabled: true
+ statViewServlet:
+ enabled: true
+ # 璁剧疆鐧藉悕鍗曪紝涓嶅~鍒欏厑璁告墍鏈夎闂�
+ allow:
+ url-pattern: /druid/*
+ # 鎺у埗鍙扮鐞嗙敤鎴峰悕鍜屽瘑鐮�
+ login-username: ruoyi
+ login-password: 123456
+ filter:
+ stat:
+ enabled: true
+ # 鎱QL璁板綍
+ log-slow-sql: true
+ slow-sql-millis: 1000
+ merge-sql: true
+ wall:
+ config:
+ multi-statement-allow: true
+ # 璧勬簮淇℃伅
+ messages:
+ # 鍥介檯鍖栬祫婧愭枃浠惰矾寰�
+ basename: i18n/messages
+ # 鏂囦欢涓婁紶
+ servlet:
+ multipart:
+ # 鍗曚釜鏂囦欢澶у皬
+ max-file-size: 1GB
+ # 璁剧疆鎬讳笂浼犵殑鏂囦欢澶у皬
+ max-request-size: 2GB
+ # 鏈嶅姟妯″潡
+ devtools:
+ restart:
+ # 鐑儴缃插紑鍏�
+ enabled: false
+ # redis 閰嶇疆
+ redis:
+ # 鍦板潃
+ host: 127.0.0.1
+# host: 172.17.0.1
+ # 绔彛锛岄粯璁や负6379
+ port: 6379
+ # 鏁版嵁搴撶储寮�
+ database: 0
+ # 瀵嗙爜
+ password:
+# password: 123456
+
+ # 杩炴帴瓒呮椂鏃堕棿
+ timeout: 10s
+ lettuce:
+ pool:
+ # 杩炴帴姹犱腑鐨勬渶灏忕┖闂茶繛鎺�
+ min-idle: 0
+ # 杩炴帴姹犱腑鐨勬渶澶х┖闂茶繛鎺�
+ max-idle: 8
+ # 杩炴帴姹犵殑鏈�澶ф暟鎹簱杩炴帴鏁�
+ max-active: 8
+ # #杩炴帴姹犳渶澶ч樆濉炵瓑寰呮椂闂达紙浣跨敤璐熷�艰〃绀烘病鏈夐檺鍒讹級
+ max-wait: -1ms
+
+# token閰嶇疆
+token:
+ # 浠ょ墝鑷畾涔夋爣璇�
+ header: Authorization
+ # 浠ょ墝瀵嗛挜
+ secret: abcdefghijklmnopqrstuvwxyz
+ # 浠ょ墝鏈夋晥鏈燂紙榛樿30鍒嗛挓锛�
+ expireTime: 450
+
+# MyBatis Plus閰嶇疆
+mybatis-plus:
+ # 鎼滅储鎸囧畾鍖呭埆鍚� 鏍规嵁鑷繁鐨勯」鐩潵
+ typeAliasesPackage: com.ruoyi.**.pojo
+ # 閰嶇疆mapper鐨勬壂鎻忥紝鎵惧埌鎵�鏈夌殑mapper.xml鏄犲皠鏂囦欢
+ mapperLocations: classpath*:mapper/**/*Mapper.xml
+ # 鍔犺浇鍏ㄥ眬鐨勯厤缃枃浠�
+ configLocation: classpath:mybatis/mybatis-config.xml
+ global-config:
+ enable-sql-runner: true
+ db-config:
+ id-type: auto
+
+# PageHelper鍒嗛〉鎻掍欢
+pagehelper:
+ helperDialect: mysql
+ supportMethodsArguments: true
+ params: count=countSql
+
+# Swagger閰嶇疆
+swagger:
+ # 鏄惁寮�鍚痵wagger
+ enabled: false
+ # 璇锋眰鍓嶇紑
+ pathMapping: /dev-api
+
+# 闃叉XSS鏀诲嚮
+xss:
+ # 杩囨护寮�鍏�
+ enabled: true
+ # 鎺掗櫎閾炬帴锛堝涓敤閫楀彿鍒嗛殧锛�
+ excludes: /system/notice
+ # 鍖归厤閾炬帴
+ urlPatterns: /system/*,/monitor/*,/tool/*
+
+# 浠g爜鐢熸垚
+gen:
+ # 浣滆��
+ author: ruoyi
+ # 榛樿鐢熸垚鍖呰矾寰� system 闇�鏀规垚鑷繁鐨勬ā鍧楀悕绉� 濡� system monitor tool
+ packageName: com.ruoyi.project.system
+ # 鑷姩鍘婚櫎琛ㄥ墠缂�锛岄粯璁ゆ槸true
+ autoRemovePre: false
+ # 琛ㄥ墠缂�锛堢敓鎴愮被鍚嶄笉浼氬寘鍚〃鍓嶇紑锛屽涓敤閫楀彿鍒嗛殧锛�
+ tablePrefix: sys_
+ # 鏄惁鍏佽鐢熸垚鏂囦欢瑕嗙洊鍒版湰鍦帮紙鑷畾涔夎矾寰勶級锛岄粯璁や笉鍏佽
+ allowOverwrite: false
+
+file:
+ temp-dir: /center-lims/mis/file/temp/uploads
+ upload-dir: /center-lims/mis/file/prod/uploads
\ No newline at end of file
--
Gitblit v1.9.3