liu
6 天以前 722f3d8cfd48e3db2836578b36cff648dee1f72a
feat: 设备遥测实时采集与报警推送

- 新增 PLC/边缘网关遥测上报接口与 SSE 实时报警推送
- 遥测定时采集、阈值判定、远程启停网关对接
- 补充无人值守数据库升级脚本(含车辆通行授权表)
- 新增 dev 环境配置

Co-Authored-By: Claude <noreply@anthropic.com>
已添加3个文件
已修改6个文件
560 ■■■■■ 文件已修改
doc/device_monitor_integration.md 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/unattended_upgrade.sql 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/unattended/telemetry/controller/DeviceTelemetryController.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/unattended/telemetry/service/DeviceAlarmPushService.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/unattended/telemetry/service/DeviceTelemetryService.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/unattended/telemetry/service/impl/DeviceTelemetryServiceImpl.java 160 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-dev.yml 222 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-tide.yml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application.yml 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/device_monitor_integration.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,55 @@
# è®¾å¤‡è¿œç¨‹ç›‘控接入说明
## é¥æµ‹æ•°æ®ä¸ŠæŠ¥
PLC æˆ–边缘网关使用登录后的 Bearer Token è°ƒç”¨ï¼š
`POST /environmentAccess/deviceMonitor/report`
```json
{
  "deviceLedgerId": 1,
  "temperature": 72.5,
  "pressure": 0.86,
  "vibration": 3.2,
  "energy": 12.6,
  "collectTime": "2026-11-03 10:30:00"
}
```
服务端会根据设备台账阈值判定 `normal`、`warning` æˆ– `alarm`,异常状态首次出现或升级时,通过 `/environmentAccess/deviceMonitor/alarm-stream` å®žæ—¶æŽ¨é€ç»™åœ¨çº¿é¡µé¢ã€‚
## è¿œç¨‹å¯åœç½‘å…³
在配置文件中增加统一控制网关地址;令牌建议从环境变量读取,不要直接写入版本库:
```yaml
unattended:
  device-control:
    gateway-url: http://127.0.0.1:9000/api/device/control
    auth-token: ${DEVICE_CONTROL_TOKEN:}
```
系统向网关发送:
```json
{
  "deviceId": 1,
  "deviceName": "空压机 1#",
  "action": "start",
  "commandTime": "2026-11-03T10:30:00"
}
```
网关返回任意 2xx çŠ¶æ€ç è§†ä¸ºè®¾å¤‡ç¡®è®¤ï¼ŒéšåŽç³»ç»Ÿæ›´æ–°è¿è¡ŒçŠ¶æ€ï¼›è¿žæŽ¥å¤±è´¥æˆ–éž 2xx æ—¶ä¸ä¼šä¿®æ”¹çŠ¶æ€ã€‚æœªé…ç½®ç½‘å…³åœ°å€æ—¶ä½¿ç”¨æœ¬åœ°è”è°ƒæ¨¡å¼ã€‚
## è‡ªåŠ¨æ¨¡æ‹Ÿé‡‡é›†
默认每 30 ç§’自动生成一组联调数据。接入真实 PLC/网关后应关闭模拟采集:
```yaml
unattended:
  telemetry:
    auto-collect-enabled: false
    collect-interval-ms: 30000
```
doc/unattended_upgrade.sql
@@ -99,6 +99,29 @@
  KEY `idx_access_plate` (`plate_number`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='门禁进出记录';
CREATE TABLE IF NOT EXISTS `access_vehicle` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `plate_number` varchar(32) NOT NULL COMMENT '车牌号',
  `plate_color` varchar(16) DEFAULT NULL COMMENT '车牌颜色',
  `rfid_tag` varchar(64) DEFAULT NULL COMMENT 'RFID标签',
  `carrier_name` varchar(120) DEFAULT NULL COMMENT '承运商',
  `vehicle_type` varchar(64) DEFAULT NULL COMMENT '车辆类型',
  `driver_name` varchar(64) DEFAULT NULL COMMENT '司机姓名',
  `driver_phone` varchar(32) DEFAULT NULL COMMENT '司机电话',
  `valid_start` date DEFAULT NULL COMMENT '有效期开始',
  `valid_end` date DEFAULT NULL COMMENT '有效期结束',
  `status` varchar(16) DEFAULT 'valid' COMMENT '状态 valid/disabled',
  `remark` varchar(255) DEFAULT NULL,
  `create_user` bigint DEFAULT NULL,
  `create_time` datetime DEFAULT NULL,
  `update_user` bigint DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  `tenant_id` bigint DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_access_vehicle_plate` (`plate_number`),
  KEY `idx_access_vehicle_rfid` (`rfid_tag`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='车辆通行授权';
-- ------------------------------------------------------------
-- 3. æ— äººå€¼å®ˆåœ°ç£…:扩展字段
-- ------------------------------------------------------------
src/main/java/com/ruoyi/unattended/telemetry/controller/DeviceTelemetryController.java
@@ -6,6 +6,9 @@
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import com.ruoyi.unattended.telemetry.service.DeviceAlarmPushService;
import javax.annotation.Resource;
import java.util.List;
@@ -21,6 +24,9 @@
    @Resource
    private DeviceTelemetryService deviceTelemetryService;
    @Resource
    private DeviceAlarmPushService alarmPushService;
    @GetMapping("/list")
    @ApiOperation("实时监控列表")
    public AjaxResult list() {
@@ -31,6 +37,16 @@
    @ApiOperation("触发一次采集")
    public AjaxResult collect() {
        return AjaxResult.success(deviceTelemetryService.collectOnce());
    }
    @PostMapping("/report")
    @ApiOperation("PLC/边缘网关上报遥测数据")
    public AjaxResult report(@RequestBody DeviceTelemetry telemetry) {
        try {
            return AjaxResult.success(deviceTelemetryService.report(telemetry));
        } catch (IllegalArgumentException e) {
            return AjaxResult.error(e.getMessage());
        }
    }
    @GetMapping("/history")
@@ -47,6 +63,12 @@
        return AjaxResult.success(deviceTelemetryService.alarms());
    }
    @GetMapping(value = "/alarm-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    @ApiOperation("订阅实时报警推送")
    public SseEmitter alarmStream() {
        return alarmPushService.subscribe();
    }
    @PostMapping("/control")
    @ApiOperation("远程启停")
    public AjaxResult control(@RequestBody ControlRequest request) {
src/main/java/com/ruoyi/unattended/telemetry/service/DeviceAlarmPushService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,64 @@
package com.ruoyi.unattended.telemetry.service;
import com.ruoyi.unattended.telemetry.dto.DeviceMonitorVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
 * è®¾å¤‡æŠ¥è­¦ SSE æŽ¨é€ä¸­å¿ƒã€‚
 */
@Slf4j
@Component
public class DeviceAlarmPushService {
    private final List<SseEmitter> clients = new CopyOnWriteArrayList<>();
    public SseEmitter subscribe() {
        SseEmitter emitter = new SseEmitter(0L);
        clients.add(emitter);
        emitter.onCompletion(() -> clients.remove(emitter));
        emitter.onTimeout(() -> clients.remove(emitter));
        emitter.onError(error -> clients.remove(emitter));
        try {
            emitter.send(SseEmitter.event()
                    .name("connected")
                    .data(Collections.singletonMap("time", LocalDateTime.now().toString())));
        } catch (IOException e) {
            clients.remove(emitter);
        }
        return emitter;
    }
    public void publish(DeviceMonitorVo alarm) {
        for (SseEmitter client : clients) {
            try {
                String eventId = alarm.getDeviceId() + "-" + System.currentTimeMillis();
                client.send(SseEmitter.event().id(eventId).name("alarm").data(alarm));
            } catch (Exception e) {
                clients.remove(client);
            }
        }
    }
    /** ä¿æŒä»£ç†å’Œæµè§ˆå™¨ä¹‹é—´çš„长连接,及时清理已断开的客户端。 */
    @Scheduled(fixedRate = 25000L)
    public void heartbeat() {
        for (SseEmitter client : clients) {
            try {
                client.send(SseEmitter.event()
                        .name("heartbeat")
                        .data(Collections.singletonMap("time", LocalDateTime.now().toString())));
            } catch (Exception e) {
                clients.remove(client);
            }
        }
    }
}
src/main/java/com/ruoyi/unattended/telemetry/service/DeviceTelemetryService.java
@@ -15,6 +15,11 @@
    List<DeviceMonitorVo> collectOnce();
    /**
     * æŽ¥æ”¶ PLC/边缘网关上报的真实遥测数据,并立即做阈值判定与报警推送
     */
    DeviceMonitorVo report(DeviceTelemetry telemetry);
    /**
     * å½“前监控总览
     */
    List<DeviceMonitorVo> monitorList();
src/main/java/com/ruoyi/unattended/telemetry/service/impl/DeviceTelemetryServiceImpl.java
@@ -7,8 +7,16 @@
import com.ruoyi.unattended.telemetry.dto.DeviceMonitorVo;
import com.ruoyi.unattended.telemetry.mapper.DeviceTelemetryMapper;
import com.ruoyi.unattended.telemetry.pojo.DeviceTelemetry;
import com.ruoyi.unattended.telemetry.service.DeviceAlarmPushService;
import com.ruoyi.unattended.telemetry.service.DeviceTelemetryService;
import lombok.extern.slf4j.Slf4j;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@@ -16,7 +24,9 @@
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import com.alibaba.fastjson2.JSON;
/**
 * è®¾å¤‡é¥æµ‹æœåŠ¡ã€‚
@@ -36,6 +46,24 @@
    @Resource
    private DeviceTelemetryMapper telemetryMapper;
    @Resource
    private DeviceAlarmPushService alarmPushService;
    @Value("${unattended.device-control.gateway-url:}")
    private String gatewayUrl;
    @Value("${unattended.device-control.auth-token:}")
    private String gatewayToken;
    @Value("${unattended.telemetry.auto-collect-enabled:true}")
    private boolean autoCollectEnabled;
    private final OkHttpClient controlClient = new OkHttpClient.Builder()
            .connectTimeout(5, TimeUnit.SECONDS)
            .readTimeout(8, TimeUnit.SECONDS)
            .writeTimeout(8, TimeUnit.SECONDS)
            .build();
    private static final BigDecimal DEFAULT_TEMP_MIN = new BigDecimal("20");
    private static final BigDecimal DEFAULT_TEMP_MAX = new BigDecimal("80");
    private static final BigDecimal DEFAULT_PRESSURE_MIN = new BigDecimal("0.20");
@@ -53,6 +81,7 @@
        }
        LocalDateTime now = LocalDateTime.now();
        List<DeviceTelemetry> batch = new ArrayList<>();
        Set<Long> alarmDeviceIds = new HashSet<>();
        for (DeviceLedger device : devices) {
            DeviceTelemetry last = telemetryMapper.selectOne(
                    Wrappers.<DeviceTelemetry>lambdaQuery()
@@ -65,11 +94,87 @@
            telemetry.setDeviceName(device.getDeviceName());
            judge(device, telemetry);
            batch.add(telemetry);
            if (shouldPush(last, telemetry)) {
                alarmDeviceIds.add(device.getId());
            }
        }
        if (!batch.isEmpty()) {
            saveBatch(batch);
        }
        return monitorList();
        List<DeviceMonitorVo> result = monitorList();
        publishAlarms(result, alarmDeviceIds);
        return result;
    }
    /**
     * é»˜è®¤æ¯ 30 ç§’采集一次。现场接入真实网关后可将 auto-collect-enabled è®¾ä¸º false,
     * æ”¹ç”± /report æŽ¥å£æŽ¥æ”¶ PLC/边缘网关数据。
     */
    @Scheduled(fixedDelayString = "${unattended.telemetry.collect-interval-ms:30000}",
            initialDelayString = "${unattended.telemetry.initial-delay-ms:10000}")
    public void scheduledCollect() {
        if (!autoCollectEnabled) {
            return;
        }
        try {
            collectOnce();
        } catch (Exception e) {
            log.error("设备遥测定时采集失败", e);
        }
    }
    @Override
    public DeviceMonitorVo report(DeviceTelemetry telemetry) {
        if (telemetry == null || telemetry.getDeviceLedgerId() == null) {
            throw new IllegalArgumentException("设备id不能为空");
        }
        DeviceLedger device = deviceLedgerMapper.selectById(telemetry.getDeviceLedgerId());
        if (device == null) {
            throw new IllegalArgumentException("设备不存在");
        }
        DeviceTelemetry last = latestTelemetry(device.getId());
        telemetry.setId(null);
        telemetry.setDeviceName(device.getDeviceName());
        if (telemetry.getCollectTime() == null) {
            telemetry.setCollectTime(LocalDateTime.now());
        }
        judge(device, telemetry);
        save(telemetry);
        DeviceMonitorVo result = monitorList().stream()
                .filter(item -> device.getId().equals(item.getDeviceId()))
                .findFirst()
                .orElseThrow(() -> new IllegalArgumentException("设备监控数据生成失败"));
        if (shouldPush(last, telemetry)) {
            alarmPushService.publish(result);
        }
        return result;
    }
    private DeviceTelemetry latestTelemetry(Long deviceId) {
        return telemetryMapper.selectOne(
                Wrappers.<DeviceTelemetry>lambdaQuery()
                        .eq(DeviceTelemetry::getDeviceLedgerId, deviceId)
                        .orderByDesc(DeviceTelemetry::getCollectTime)
                        .last("limit 1"));
    }
    private boolean shouldPush(DeviceTelemetry previous, DeviceTelemetry current) {
        int currentLevel = alarmLevel(current == null ? null : current.getStatus());
        int previousLevel = alarmLevel(previous == null ? null : previous.getStatus());
        return currentLevel > 0 && (previousLevel == 0 || currentLevel > previousLevel);
    }
    private int alarmLevel(String status) {
        if ("alarm".equalsIgnoreCase(status)) return 2;
        if ("warning".equalsIgnoreCase(status)) return 1;
        return 0;
    }
    private void publishAlarms(List<DeviceMonitorVo> devices, Set<Long> alarmDeviceIds) {
        if (alarmDeviceIds.isEmpty()) return;
        devices.stream()
                .filter(item -> alarmDeviceIds.contains(item.getDeviceId()))
                .forEach(alarmPushService::publish);
    }
    /**
@@ -219,6 +324,7 @@
        } else {
            throw new IllegalArgumentException("不支持的操作:" + action);
        }
        Map<String, Object> dispatch = dispatchControl(device, action);
        DeviceLedger update = new DeviceLedger();
        update.setId(deviceId);
        update.setControlStatus(status);
@@ -229,11 +335,61 @@
        result.put("deviceName", device.getDeviceName());
        result.put("controlStatus", status);
        result.put("commandTime", LocalDateTime.now().toString());
        result.put("message", "running".equals(status) ? "启动指令已下发" : "停止指令已下发");
        result.putAll(dispatch);
        boolean localMode = "local".equals(dispatch.get("dispatchMode"));
        if (localMode) {
            result.put("message", "本地联调状态已更新");
        } else {
            result.put("message", "running".equals(status) ? "设备启动指令已确认" : "设备停止指令已确认");
        }
        log.info("[远程控制] deviceId={}, action={}, status={}", deviceId, action, status);
        return result;
    }
    /**
     * è°ƒç”¨ç»Ÿä¸€è®¾å¤‡æŽ§åˆ¶ç½‘关。
     * æœªé…ç½®ç½‘关时使用本地联调模式,使系统可在硬件到场前完成业务验收。
     */
    private Map<String, Object> dispatchControl(DeviceLedger device, String action) {
        String endpoint = trimToNull(gatewayUrl);
        Map<String, Object> result = new LinkedHashMap<>();
        if (endpoint == null) {
            result.put("dispatchMode", "local");
            result.put("gatewayAcknowledged", true);
            return result;
        }
        Map<String, Object> command = new LinkedHashMap<>();
        command.put("deviceId", device.getId());
        command.put("deviceName", device.getDeviceName());
        command.put("action", action.toLowerCase(Locale.ROOT));
        command.put("commandTime", LocalDateTime.now().toString());
        RequestBody body = RequestBody.create(JSON.toJSONString(command),
                MediaType.parse("application/json; charset=utf-8"));
        Request.Builder builder = new Request.Builder().url(endpoint).post(body);
        if (trimToNull(gatewayToken) != null) {
            builder.header("Authorization", "Bearer " + gatewayToken.trim());
        }
        try (Response response = controlClient.newCall(builder.build()).execute()) {
            if (!response.isSuccessful()) {
                throw new IllegalArgumentException("设备控制网关拒绝指令,状态码:" + response.code());
            }
            result.put("dispatchMode", "gateway");
            result.put("gatewayAcknowledged", true);
            result.put("gatewayStatus", response.code());
            return result;
        } catch (IllegalArgumentException e) {
            throw e;
        } catch (Exception e) {
            log.error("设备控制网关调用失败,deviceId={}, endpoint={}", device.getId(), endpoint, e);
            throw new IllegalArgumentException("设备控制网关连接失败,请检查设备网络");
        }
    }
    private String trimToNull(String value) {
        return value == null || value.trim().isEmpty() ? null : value.trim();
    }
    @Override
    public List<DeviceMonitorVo> alarms() {
        List<DeviceMonitorVo> all = monitorList();
src/main/resources/application-dev.yml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,222 @@
# èŠ¯é¡¹ç›®å¯¼-仓储物流系统相关配置
inspur:
  appId: 1211009035159994368
  appSecret:  QXdnMjAwMTIwMjYxNTE4MzkwNzMLgG
ruoyi:
  # åç§°
  name: RuoYi
  # ç‰ˆæœ¬
  version: 3.8.9
  # ç‰ˆæƒå¹´ä»½
  copyrightYear: 2025
  # æ–‡ä»¶è·¯å¾„ ç¤ºä¾‹ï¼ˆ Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
  profile: /center-lims/mis/file
  # èŽ·å–ip地址开关
  addressEnabled: false
  # éªŒè¯ç ç±»åž‹ math æ•°å­—计算 char å­—符验证
  captchaType: math
# å¼€å‘环境配置
server:
  # æœåŠ¡å™¨çš„HTTP端口,默认为8080
  port: 8038
  servlet:
    # åº”用的访问路径
    context-path: /
  tomcat:
    # tomcat的URI编码
    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://10.136.65.135:3306/wrzsxt-ruoyi-xinlan?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: zttZTT123!
      # ä»Žåº“数据源
      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
          # æ…¢SQL记录
          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: 192.168.197.128
    # ç«¯å£ï¼Œé»˜è®¤ä¸º6379
    port: 6379
    # æ•°æ®åº“索引
    database: 0
    # å¯†ç 
    password: zttZTT123!
#    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:
  # æ˜¯å¦å¼€å¯swagger
  enabled: false
  # è¯·æ±‚前缀
  pathMapping: /dev-api
# é˜²æ­¢XSS攻击
xss:
  # è¿‡æ»¤å¼€å…³
  enabled: true
  # æŽ’除链接(多个用逗号分隔)
  excludes: /system/notice
  # åŒ¹é…é“¾æŽ¥
  urlPatterns: /system/*,/monitor/*,/tool/*
# ä»£ç ç”Ÿæˆ
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
src/main/resources/application-tide.yml
@@ -1,7 +1,7 @@
# èН坼-仓储物流系统项目相关配置
# èŠ¯é¡¹ç›®å¯¼-仓储物流系统相关配置
inspur:
  appId: 1216088544800407552
  appSecret: TV96MDMwMjIwMjYxNTQyNDg1NDQvSh
  appId: 1211009035159994368
  appSecret:  QXdnMjAwMTIwMjYxNTE4MzkwNzMLgG
ruoyi:
  # åç§°
  name: RuoYi
src/main/resources/application.yml
@@ -1,4 +1,5 @@
# Spring配置
spring:
  profiles:
    active: tide
#    active: tide
    active: dev