fix(无人值守): 远程控制加真实TCP探测+通讯日志+执行耗时+模拟模式标注;门禁点下拉status过滤写反(写死0导致5个门禁点查不到);报表总净重多除1000
已修改4个文件
113 ■■■■■ 文件已修改
src/main/java/com/ruoyi/wrzs/access/service/AccessService.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/wrzs/monitor/pojo/MonitorControl.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/wrzs/monitor/service/MonitorService.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/wrzs/report/mapper/ReportCenterMapper.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/wrzs/access/service/AccessService.java
@@ -54,8 +54,10 @@
    }
    public List<AccessGate> gateList() {
        // 门禁点下拉:只排除"停用"的(本系统 status='1' 表示正常/启用,'0' 表示停用,
        // 与 RuoYi 惯例相反。原先写死 status='0' 导致下拉框查不到任何门禁点)
        return gateMapper.selectList(Wrappers.<AccessGate>lambdaQuery()
                .eq(AccessGate::getStatus, "0")
                .ne(AccessGate::getStatus, "0")
                .orderByAsc(AccessGate::getGateCode));
    }
src/main/java/com/ruoyi/wrzs/monitor/pojo/MonitorControl.java
@@ -50,9 +50,15 @@
    /** 执行结果(0成功 1失败 2下发中) */
    private String execResult;
    /** 执行耗时(ms) */
    private Integer execDuration;
    /** 执行结果说明 */
    private String resultMsg;
    /** 执行通讯日志(每行一个步骤) */
    private String execDetail;
    private String remark;
    @TableField(fill = FieldFill.INSERT)
src/main/java/com/ruoyi/wrzs/monitor/service/MonitorService.java
@@ -23,7 +23,11 @@
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -310,7 +314,14 @@
    /**
     * 远程启停控制
     * 不支持远程控制的设备直接拒绝,避免页面上出现"假按钮"
     * <p>
     * 不支持远程控制的设备直接拒绝,避免页面上出现"假按钮"。
     * <p>
     * 执行流程(每一步都写进 exec_detail,前端可展开查看通讯日志):
     * 1. 校验设备与远程控制开关
     * 2. 若配置了合法 IP,先做真实 TCP 连通性探测(Modbus 502 端口,800ms 超时)
     * 3. 当前未接入现场设备,进入模拟模式:不写控制寄存器,但如实记录探测结果
     * 4. 更新设备状态并记录耗时
     */
    @Transactional(rollbackFor = Exception.class)
    public MonitorControl control(Long deviceId, String command, String remark) {
@@ -321,7 +332,51 @@
        if (!"1".equals(device.getControlEnabled())) {
            throw new IllegalArgumentException("该设备未接入远程控制回路,不支持远程启停,请现场操作");
        }
        LocalDateTime now = LocalDateTime.now();
        long startMs = System.currentTimeMillis();
        List<String> logs = new ArrayList<>();
        String protocol = StringUtils.hasText(device.getCommProtocol()) ? device.getCommProtocol() : "未配置";
        String ip = StringUtils.hasText(device.getIpAddress()) ? device.getIpAddress().trim() : "";
        logs.add(ts() + " 设备:" + device.getDeviceName() + ",协议:" + protocol + ",地址:" + (ip.isEmpty() ? "未配置" : ip));
        String simulateTip;
        boolean ipValid = ip.matches("^\\d{1,3}(\\.\\d{1,3}){3}$");
        if (ipValid) {
            logs.add(ts() + " 尝试连接 " + ip + ":502 …");
            boolean tcpOk = probeTcp(ip, 502, 800);
            if (tcpOk) {
                logs.add(ts() + " TCP 连接成功");
                logs.add(ts() + " 模拟模式:连通性正常,但未配置寄存器映射,未写入控制寄存器");
                simulateTip = "设备在线(" + ip + ":502 可达),模拟模式指令未实际写入";
            } else {
                logs.add(ts() + " 连接失败:" + ip + ":502 无响应(800ms 超时)");
                logs.add(ts() + " 模拟模式:跳过控制寄存器写入");
                simulateTip = "设备 " + ip + ":502 无法连接,指令未实际下发";
            }
        } else {
            logs.add(ts() + " 设备未配置有效通讯地址,跳过连通性探测");
            logs.add(ts() + " 模拟模式:跳过控制寄存器写入");
            simulateTip = "设备未配置通讯地址,指令未实际下发";
        }
        String execMsg;
        if ("stop".equalsIgnoreCase(command)) {
            device.setRunStatus("0");
            execMsg = "停机指令已受理";
        } else if ("start".equalsIgnoreCase(command)) {
            device.setRunStatus("1");
            execMsg = "启动指令已受理";
        } else if ("restart".equalsIgnoreCase(command)) {
            device.setRunStatus("1");
            execMsg = "重启指令已受理";
        } else {
            throw new IllegalArgumentException("不支持的控制指令");
        }
        long duration = System.currentTimeMillis() - startMs;
        logs.add(ts() + " " + execMsg + ",耗时 " + duration + "ms");
        MonitorControl control = new MonitorControl();
        control.setDeviceId(deviceId);
        control.setDeviceName(device.getDeviceName());
@@ -329,26 +384,38 @@
        control.setControlType(StringUtils.hasText(device.getControlType()) ? device.getControlType() : "启停");
        control.setCommandSource("远程控制");
        control.setOperator(currentUserName());
        control.setOperateTime(now);
        control.setOperateTime(LocalDateTime.now());
        control.setRemark(remark);
        if ("stop".equalsIgnoreCase(command)) {
            device.setRunStatus("0");
            control.setResultMsg("停机指令已下发");
        } else if ("start".equalsIgnoreCase(command)) {
            device.setRunStatus("1");
            control.setResultMsg("启动指令已下发");
        } else if ("restart".equalsIgnoreCase(command)) {
            device.setRunStatus("1");
            control.setResultMsg("重启指令已下发");
        } else {
            throw new IllegalArgumentException("不支持的控制指令");
        }
        control.setExecResult("0");
        control.setExecDuration((int) duration);
        control.setResultMsg(execMsg + "(" + simulateTip + ")");
        control.setExecDetail(String.join("\n", logs));
        controlMapper.insert(control);
        deviceMapper.updateById(device);
        return control;
    }
    /** 当前时间戳 HH:mm:ss.SSS,用于通讯日志 */
    private static String ts() {
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss.SSS"));
    }
    /** TCP 连通性探测(真实连接,用于判断设备是否可达) */
    private static boolean probeTcp(String host, int port, int timeoutMs) {
        Socket socket = new Socket();
        try {
            socket.connect(new InetSocketAddress(host, port), timeoutMs);
            return true;
        } catch (Exception e) {
            return false;
        } finally {
            try {
                socket.close();
            } catch (Exception ignored) {
            }
        }
    }
    public IPage<MonitorControl> controlPage(Page<MonitorControl> page, MonitorControl query) {
        return controlMapper.selectPage(page, Wrappers.<MonitorControl>lambdaQuery()
                .like(StringUtils.hasText(query.getDeviceName()), MonitorControl::getDeviceName, query.getDeviceName())
src/main/java/com/ruoyi/wrzs/report/mapper/ReportCenterMapper.java
@@ -19,7 +19,7 @@
    /** 每日过磅统计(自动称重记录) */
    @Select("select date_format(first_weight_time, '%Y-%m-%d') as statDate, "
            + "count(*) as tripCount, "
            + "ifnull(sum(net_weight), 0) / 1000 as cargoVolume, "
            + "ifnull(sum(net_weight), 0) as cargoVolume, "
            + "ifnull(avg(order_deviation), 0) as deviationRate, "
            + "sum(case when cheat_flag = '1' then 1 else 0 end) as cheatCount "
            + "from wrzs_weighbridge_record "
@@ -45,7 +45,7 @@
            + "sum(case when appointment_time is not null and first_weight_time > appointment_time then 1 else 0 end) as lateTrips, "
            + "sum(case when cheat_flag = '1' then 1 else 0 end) as cheatCount, "
            + "ifnull(avg(order_deviation), 0) as avgDeviation, "
            + "ifnull(sum(net_weight), 0) / 1000 as totalNetWeight "
            + "ifnull(sum(net_weight), 0) as totalNetWeight "
            + "from wrzs_weighbridge_record "
            + "where weigh_status = '2' and first_weight_time between #{start} and #{end} "
            + "and carrier_name is not null and carrier_name <> '' "
@@ -54,7 +54,7 @@
    /** 区间总览 */
    @Select("select count(*) as totalTrips, "
            + "ifnull(sum(net_weight), 0) / 1000 as totalNetWeight, "
            + "ifnull(sum(net_weight), 0) as totalNetWeight, "
            + "ifnull(avg(order_deviation), 0) as avgDeviation, "
            + "sum(case when cheat_flag = '1' then 1 else 0 end) as cheatCount, "
            + "sum(case when appointment_time is not null and first_weight_time <= appointment_time then 1 else 0 end) as onTimeTrips, "