wanglizhong
2025-05-04 2841e102ea4b5e9ddd40327829431a25a9122cd9
fix:增加cms同步
9个文件已添加
6个文件已修改
1143 ■■■■■ 已修改文件
.cursor/rules/ruoyi-rule.mdc 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/HttpUtil.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/CmsVehicleSyncTask.java 174 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/GpsSyncTask.java 40 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/CmsGpsLoginResponse.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/CmsVehicleDeviceListResponse.java 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/CmsVehicleDeviceResponse.java 223 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/CmsVehicleLocationResponse.java 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/VehicleInfo.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ICmsGpsCollectService.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/CmsGpsCollectServiceImpl.java 199 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/VehicleInfoMapper.xml 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/gps/map.vue 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/vehicle/index.vue 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/cms_vehicle_sync_job.sql 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.cursor/rules/ruoyi-rule.mdc
New file
@@ -0,0 +1,13 @@
---
description:
globs:
alwaysApply: false
---
这是一个ruoyi项目,前端代码放入到ruoyi-ui目录下,前端项目采用vue进行代码开发;
后端项目中有ruoyi-system,ruoyi-admin,ruoyi-common,ruoyi-framework,ruoyi-quartz
ruoyi-system是操作数据库的相关代码;
ruoyi-admin是面向ui的API接口,如各种controller;
ruoyi-common是一个通用性的代码工具类;
ruoyi-framework是框架性项目;
ruoyi-quartz是计划任务项目;
ruoyi-common/src/main/java/com/ruoyi/common/utils/HttpUtil.java
@@ -116,4 +116,74 @@
        
        return response.toString();
    }
    /**
     * 发送GET请求
     * @param url 请求URL
     * @param params 请求参数
     * @return 响应内容
     */
    public static String get(String url, Map<String, String> params) {
        StringBuilder response = new StringBuilder();
        HttpURLConnection conn = null;
        try {
            // 构建带参数的URL
            StringBuilder urlBuilder = new StringBuilder(url);
            if (params != null && !params.isEmpty()) {
                urlBuilder.append("?");
                for (Map.Entry<String, String> entry : params.entrySet()) {
                    urlBuilder.append(entry.getKey())
                            .append("=")
                            .append(entry.getValue())
                            .append("&");
                }
                urlBuilder.deleteCharAt(urlBuilder.length() - 1); // 删除最后一个&
            }
            // 创建连接
            URL requestUrl = new URL(urlBuilder.toString());
            boolean isHttps = url.toLowerCase().startsWith("https");
            // 根据协议类型创建连接
            if (isHttps) {
                conn = (HttpsURLConnection) requestUrl.openConnection();
            } else {
                conn = (HttpURLConnection) requestUrl.openConnection();
            }
            // 设置请求属性
            conn.setRequestMethod("GET");
            conn.setDoInput(true);
            conn.setUseCaches(false);
            conn.setRequestProperty("Accept", "application/json");
            // 设置超时时间
            conn.setConnectTimeout(CONNECT_TIMEOUT);
            conn.setReadTimeout(READ_TIMEOUT);
            // 获取响应码
            int responseCode = conn.getResponseCode();
            if (responseCode != HttpURLConnection.HTTP_OK) {
                throw new RuntimeException("HTTP请求失败,响应码: " + responseCode);
            }
            // 读取响应
            try (BufferedReader reader = new BufferedReader(
                    new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    response.append(line);
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("HTTP请求失败: " + e.getMessage(), e);
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
        return response.toString();
    }
ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/CmsVehicleSyncTask.java
New file
@@ -0,0 +1,174 @@
package com.ruoyi.quartz.task;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.domain.CmsVehicleDeviceResponse;
import com.ruoyi.system.domain.CmsVehicleLocationResponse;
import com.ruoyi.system.domain.VehicleGps;
import com.ruoyi.system.domain.VehicleInfo;
import com.ruoyi.system.service.ICmsGpsCollectService;
import com.ruoyi.system.service.IVehicleGpsService;
import com.ruoyi.system.service.IVehicleInfoService;
/**
 * CMS车辆同步定时任务
 *
 * @author ruoyi
 */
@Component("cmsVehicleSyncTask")
public class CmsVehicleSyncTask {
    private static final Logger log = LoggerFactory.getLogger(CmsVehicleSyncTask.class);
    @Autowired
    private ICmsGpsCollectService cmsGpsCollectService;
    @Autowired
    private IVehicleInfoService vehicleInfoService;
    @Autowired
    private IVehicleGpsService vehicleGpsService;
    public void syncVehicleInfo() {
        log.info("开始同步CMS车辆信息");
        try {
            // 获取CMS所有车辆信息
            CmsVehicleDeviceResponse response = cmsGpsCollectService.queryVehicleDevices();
            if (response.getResult() != 0) {
                log.error("获取CMS车辆信息失败");
                return;
            }
            // 获取所有CMS车辆的车牌号
            List<String> cmsPlateNos = new ArrayList<>();
            response.getVehicles().forEach(vehicle -> {
                if (StringUtils.isNotEmpty(vehicle.getNm())) {
                    // 从车辆名称中提取车牌号(假设格式为"★车牌号(地区)")
                    String plateNo =this.getPlateNo(vehicle.getNm());
                    cmsPlateNos.add(plateNo);
                }
            });
            // 获取本地所有车辆
            VehicleInfo query = new VehicleInfo();
            query.setStatus("0");
            List<VehicleInfo> localVehicles = vehicleInfoService.selectVehicleInfoList(query);
            //找到所有车辆中不是CMS平台的车辆
            List<String> notCmsVehicles =  localVehicles.stream().filter(e->!e.getPlatformCode().equals("CMS")).map(e->e.getVehicleNo()).collect((Collectors.toList()));
            List<String> onlyCms=cmsPlateNos.stream().filter(e->!notCmsVehicles.contains(e)).collect((Collectors.toList()));
            Integer syncCarCount=0;
            for(String e:onlyCms){
                VehicleInfo vehicleInfo = new VehicleInfo();
                vehicleInfo.setVehicleNo(e);
                vehicleInfo.setPlatformCode("CMS");
                vehicleInfo.setStatus("0");
                //如果车辆不存在,则插入
                if (vehicleInfoService.selectVehicleInfoList(vehicleInfo).size()==0) {
                    vehicleInfoService.insertVehicleInfo(vehicleInfo);
                    syncCarCount++;
                }
            }
            log.info("成功同步{}个CMS车辆信息", syncCarCount);
            log.info("CMS车辆信息同步完成");
        } catch (Exception e) {
            log.error("同步CMS车辆信息异常", e);
        }
    }
    //对车牌处理的通用方法
    private String getPlateNo(String plateNo){
        if (StringUtils.isNotEmpty(plateNo)) {
                // 从车辆名称中提取车牌号(假设格式为"★车牌号(地区)")
            if(plateNo.contains("(")) {
                plateNo = plateNo.replace("★", "").replace("☆", "").split("\\(")[0];
            }else{
                plateNo = plateNo.replace("★", "").replace("☆", "").split("(")[0];
            }
            }
            return plateNo;
    }
    /**
     * 同步CMS车辆位置信息
     */
    public void syncVehicleLocation() {
        log.info("开始同步CMS车辆位置信息");
        try {
                //先获得本地CMS上的所有CMS车辆
                VehicleInfo query = new VehicleInfo();
                query.setPlatformCode("CMS");
                query.setStatus("0");
                List<VehicleInfo> localVehicles = vehicleInfoService.selectVehicleInfoList(query);
                    // 获取车辆最新位置信息
                    CmsVehicleLocationResponse response = cmsGpsCollectService.getVehicleLocation(
                        null,2,1,null,null);
                    if (response.getResult() != 0 ) {
                        log.warn("获取车辆位置信息失败");
                       return;
                    }
                    List<CmsVehicleLocationResponse.VehicleLocation> cmsVehicles = response.getInfos();
                    Double defaultZero = 0.0;
                    for(CmsVehicleLocationResponse.VehicleLocation vehicle:cmsVehicles){
                        //与车辆信息进行匹配,如果匹配成功,则保存车辆位置信息
                        //对车牌进行处理
                        String plateNo =this.getPlateNo(vehicle.getVi());
                        if (!localVehicles.stream().anyMatch(e->e.getVehicleNo().equals(plateNo))) {
                            continue;
                        }
                        VehicleInfo f=  localVehicles.stream().filter(e->e.getVehicleNo().equals(plateNo)).findFirst().get();
                        if(f==null){
                            continue;
                        }
                    // 创建GPS记录
                    VehicleGps gps = new VehicleGps();
                    gps.setVehicleId(f.getVehicleId());
                    gps.setDeviceId(null);
                    gps.setLongitude(vehicle.getJd()/1000000);
                    gps.setLatitude(vehicle.getWd()/1000000);
                    gps.setSpeed(defaultZero);
                    gps.setVehicleNo(plateNo);
                    gps.setDirection(defaultZero);
                    gps.setAltitude(defaultZero);
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    String deviceTime=sdf.format(new Date(vehicle.getTm()));
                    gps.setDeviceReportTime(deviceTime);
                    gps.setPlatformProcessTime(sdf.format(new Date()));
                    gps.setCreateTime(new Date());
                    gps.setCollectTime(deviceTime);
                    // 保存GPS记录
                    vehicleGpsService.insertVehicleGps(gps);
                    }
                } catch (Exception e) {
                    log.error("同步车辆位置信息异常", e);
                }
    }
}
ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/GpsSyncTask.java
@@ -99,12 +99,16 @@
                        VehicleInfo vehicleInfo = vehicleInfoService.selectVehicleInfoByPlateNumber(plateNumber);
                        if (vehicleInfo != null) {
                            vehicleInfo.setDeviceId(deviceId);
                            //获得数据字典中的平台编码
                            vehicleInfo.setPlatformCode("GPS51");
                            vehicleInfoService.updateVehicleInfo(vehicleInfo);
                        } else {
                            VehicleInfo newVehicle = new VehicleInfo();
                            newVehicle.setVehicleNo(plateNumber);
                            newVehicle.setDeviceId(deviceId);
                            newVehicle.setStatus("0");
                            newVehicle.setPlatformCode("GPS51");
                            vehicleInfoService.insertVehicleInfo(newVehicle);
                        }
                    }
@@ -142,21 +146,39 @@
            
            //devicetime 这个是一个linux时间戳,要转换成北京时间,再转成yyyy-MM-dd HH:mm:ss格式
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            //getArrivedtime 这个是一个linux时间戳,要转换成北京时间,再转成yyyy-MM-dd HH:mm:ss格式
            long arrivedTime = position.getArrivedtime();
            Date arrivedDate;
            // 检查时间戳是否有效(大于0)
            if (arrivedTime > 0) {
                arrivedDate = new Date(arrivedTime);
                // 减去8小时
                arrivedDate.setTime(arrivedDate.getTime() - 8 * 60 * 60 * 1000);
            } else {
                // 时间戳无效,使用当前时间
                arrivedDate = new Date();
            }
            gps.setPlatformProcessTime(sdf.format(arrivedDate));
            // 设备上报时间
            long deviceTime = position.getDevicetime();
            if (deviceTime > 0 && deviceTime < 4102444800L) { // 2100-01-01 00:00:00
                gps.setDeviceReportTime(sdf.format(new Date(deviceTime * 1000L)));
            Date date;
            // 检查时间戳是否有效(大于0)
            if (deviceTime > 0) {
                date = new Date(deviceTime);
                // 减去8小时
                date.setTime(date.getTime() - 8 * 60 * 60 * 1000);
            } else {
                log.warn("车辆[{}]的设备时间戳[{}]无效,使用当前时间", vehicle.getVehicleNo(), deviceTime);
                gps.setDeviceReportTime(sdf.format(new Date()));
                // 时间戳无效,使用当前时间
                date = arrivedDate;
            }
            // 平台处理时间(当前时间)
            gps.setPlatformProcessTime(sdf.format(new Date()));
            gps.setDeviceReportTime(sdf.format(date));
            
            // 采集时间(使用设备上报时间)
            gps.setCollectTime(gps.getDeviceReportTime());
            gps.setCollectTime(sdf.format(new Date( )));
            // 保存GPS位置信息
            vehicleGpsService.insertVehicleGps(gps);
ruoyi-system/src/main/java/com/ruoyi/system/domain/CmsGpsLoginResponse.java
New file
@@ -0,0 +1,56 @@
package com.ruoyi.system.domain;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
 * CMS GPS登录响应
 */
public class CmsGpsLoginResponse {
    /** 结果码 */
    @JsonProperty("result")
    private Integer result;
    /** 会话ID */
    @JsonProperty("jsession")
    private String jsession;
    /** 账户名称 */
    @JsonProperty("account_name")
    private String accountName;
    /** 会话ID */
    @JsonProperty("JSESSIONID")
    private String sessionId;
    public Integer getResult() {
        return result;
    }
    public void setResult(Integer result) {
        this.result = result;
    }
    public String getJsession() {
        return jsession;
    }
    public void setJsession(String jsession) {
        this.jsession = jsession;
    }
    public String getAccountName() {
        return accountName;
    }
    public void setAccountName(String accountName) {
        this.accountName = accountName;
    }
    public String getSessionId() {
        return sessionId;
    }
    public void setSessionId(String sessionId) {
        this.sessionId = sessionId;
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/domain/CmsVehicleDeviceListResponse.java
New file
@@ -0,0 +1,74 @@
package com.ruoyi.system.domain;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
import java.util.List;
/**
 * CMS车辆设备列表查询响应
 */
public class CmsVehicleDeviceListResponse implements Serializable {
    private static final long serialVersionUID = 1L;
    /** 结果码 */
    @JsonProperty("result")
    private Integer result;
    /** 设备列表 */
    @JsonProperty("devices")
    private List<VehicleDevice> devices;
    public Integer getResult() {
        return result;
    }
    public void setResult(Integer result) {
        this.result = result;
    }
    public List<VehicleDevice> getDevices() {
        return devices;
    }
    public void setDevices(List<VehicleDevice> devices) {
        this.devices = devices;
    }
    public static class VehicleDevice {
        /** 车牌号 */
        @JsonProperty("vid")
        private String vehicleId;
        /** 设备类型 */
        @JsonProperty("type")
        private Integer type;
        /** 设备号 */
        @JsonProperty("did")
        private String deviceId;
        public String getVehicleId() {
            return vehicleId;
        }
        public void setVehicleId(String vehicleId) {
            this.vehicleId = vehicleId;
        }
        public Integer getType() {
            return type;
        }
        public void setType(Integer type) {
            this.type = type;
        }
        public String getDeviceId() {
            return deviceId;
        }
        public void setDeviceId(String deviceId) {
            this.deviceId = deviceId;
        }
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/domain/CmsVehicleDeviceResponse.java
New file
@@ -0,0 +1,223 @@
package com.ruoyi.system.domain;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
 * CMS车辆设备查询响应
 */
public class CmsVehicleDeviceResponse implements Serializable {
    /** 结果码 */
    @JsonProperty("result")
    private Integer result;
    /** 车辆列表 */
    @JsonProperty("vehicles")
    private List<Vehicle> vehicles;
    public Integer getResult() {
        return result;
    }
    public void setResult(Integer result) {
        this.result = result;
    }
    public List<Vehicle> getVehicles() {
        return vehicles;
    }
    public void setVehicles(List<Vehicle> vehicles) {
        this.vehicles = vehicles;
    }
    @Data
    public  class Vehicle implements Serializable{
        /** 车辆ID */
        @JsonProperty("id")
        private Integer id;
        /** 车辆名称 */
        @JsonProperty("nm")
        private String nm;
        /** 图标 */
        @JsonProperty("ic")
        private Integer ic;
        /** 父级ID */
        @JsonProperty("pid")
        private Integer pid;
        /** 父级名称 */
        @JsonProperty("pnm")
        private String parentName;
        /** 缩写 */
        @JsonProperty("abbr")
        private String abbreviation;
        /** 车牌类型 */
        @JsonProperty("pt")
        private String pt;
        /** 车辆颜色 */
        @JsonProperty("vehiColor")
        private String vehicleColor;
        /** 状态 */
        @JsonProperty("status")
        private Integer status;
        /** 车辆品牌 */
        @JsonProperty("vehiBand")
        private String vehicleBrand;
        /** 车辆类型 */
        @JsonProperty("vehiType")
        private String vehicleType;
        /** 车辆用途 */
        @JsonProperty("vehiUse")
        private String vehicleUse;
        /** 生产日期 */
        @JsonProperty("dateProduct")
        private Long productionDate;
        /** 通道数量 */
        @JsonProperty("chnCount")
        private Integer channelCount;
        /** 通道名称 */
        @JsonProperty("chnName")
        private String channelName;
        /** 输入通道数量 */
        @JsonProperty("ioInCount")
        private Integer inputChannelCount;
        /** 输入通道名称 */
        @JsonProperty("ioInName")
        private String inputChannelName;
        /** 输出通道数量 */
        @JsonProperty("ioOutCount")
        private Integer outputChannelCount;
        /** 输出通道名称 */
        @JsonProperty("ioOutName")
        private String outputChannelName;
        /** 温度通道数量 */
        @JsonProperty("tempCount")
        private Integer temperatureChannelCount;
        /** 温度通道名称 */
        @JsonProperty("tempName")
        private String temperatureChannelName;
        /** 发动机号 */
        @JsonProperty("engineNum")
        private String engineNumber;
        /** 车架号 */
        @JsonProperty("frameNum")
        private String frameNumber;
        /** 车主姓名 */
        @JsonProperty("ownerName")
        private String ownerName;
        /** 联系人 */
        @JsonProperty("linkPeople")
        private String contactPerson;
        /** 联系电话 */
        @JsonProperty("linkPhone")
        private String contactPhone;
        /** 购买日期 */
        @JsonProperty("datePurchase")
        private Long purchaseDate;
        /** 年检日期 */
        @JsonProperty("dateAnnualSurvey")
        private Long annualSurveyDate;
        /** 限速 */
        @JsonProperty("speedLimit")
        private Integer speedLimit;
        /** 运营线路 */
        @JsonProperty("linesOperation")
        private String operationLines;
        /** 行业 */
        @JsonProperty("industry")
        private String industry;
        /** 车型 */
        @JsonProperty("carType")
        private String carType;
        /** 车辆产地 */
        @JsonProperty("carPlace")
        private String carPlace;
        /** 备注 */
        @JsonProperty("remark")
        private String remark;
        /** 车辆型号 */
        @JsonProperty("vehicleModel")
        private String vehicleModel;
        /** 发动机型号 */
        @JsonProperty("engineModel")
        private String engineModel;
        /** 轴数 */
        @JsonProperty("axesNumber")
        private Integer axesNumber;
        /** 总质量 */
        @JsonProperty("totalWeight")
        private Double totalWeight;
        /** 准牵引质量 */
        @JsonProperty("quasiTractionMass")
        private Double quasiTractionMass;
        /** 外廓尺寸-长 */
        @JsonProperty("longOutlineDimensions")
        private Integer longOutlineDimensions;
        /** 外廓尺寸-宽 */
        @JsonProperty("wideOutlineDimensions")
        private Integer wideOutlineDimensions;
        /** 外廓尺寸-高 */
        @JsonProperty("highOutlineDimensions")
        private Integer highOutlineDimensions;
        /** 内廓尺寸-长 */
        @JsonProperty("longInsideDimension")
        private Integer longInsideDimension;
        /** 内廓尺寸-宽 */
        @JsonProperty("wideInnerDimensions")
        private Integer wideInnerDimensions;
        /** 内廓尺寸-高 */
        @JsonProperty("highInsideDimensions")
        private Integer highInsideDimensions;
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/domain/CmsVehicleLocationResponse.java
New file
@@ -0,0 +1,96 @@
package com.ruoyi.system.domain;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
 * CMS车辆位置信息查询响应
 */
@Data
public class CmsVehicleLocationResponse implements Serializable {
    private static final long serialVersionUID = 1L;
    /** 结果码 */
    @JsonProperty("result")
    private Integer result;
    /** 位置信息列表 */
    @JsonProperty("infos")
    private List<VehicleLocation> infos;
    /** 分页信息 */
    @JsonProperty("pagination")
    private Pagination pagination;
    @Data
    public  class VehicleLocation implements Serializable {
        /** 车牌号 */
        @JsonProperty("vi")
        private String vi;
        /** 时间戳 */
        @JsonProperty("tm")
        private Long tm;
        /** 经度 */
        @JsonProperty("jd")
        private Double jd;
        /** 纬度 */
        @JsonProperty("wd")
        private Double wd;
        /** 地理位置 */
        @JsonProperty("pos")
        private String pos;
    }
    @Data
    public  class Pagination implements Serializable{
        /** 总页数 */
        @JsonProperty("totalPages")
        private Integer totalPages;
        /** 当前页 */
        @JsonProperty("currentPage")
        private Integer currentPage;
        /** 每页记录数 */
        @JsonProperty("pageRecords")
        private Integer pageRecords;
        /** 总记录数 */
        @JsonProperty("totalRecords")
        private Integer totalRecords;
        /** 排序参数 */
        @JsonProperty("sortParams")
        private String sortParams;
        /** 是否有下一页 */
        @JsonProperty("hasNextPage")
        private Boolean hasNextPage;
        /** 是否有上一页 */
        @JsonProperty("hasPreviousPage")
        private Boolean hasPreviousPage;
        /** 下一页 */
        @JsonProperty("nextPage")
        private Integer nextPage;
        /** 上一页 */
        @JsonProperty("previousPage")
        private Integer previousPage;
        /** 起始记录 */
        @JsonProperty("startRecord")
        private Integer startRecord;
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/domain/VehicleInfo.java
@@ -38,6 +38,10 @@
    @Excel(name = "状态", readConverterExp = "0=正常,1=停用")
    private String status;
    /** 平台标识 */
    @Excel(name = "平台标识")
    private String platformCode;
    public void setVehicleId(Long vehicleId) {
        this.vehicleId = vehicleId;
    }
@@ -94,6 +98,14 @@
        return status;
    }
    public String getPlatformCode() {
        return platformCode;
    }
    public void setPlatformCode(String platformCode) {
        this.platformCode = platformCode;
    }
    @Override
    public String toString() {
        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
@@ -104,6 +116,7 @@
                .append("vehicleBrand", getVehicleBrand())
                .append("vehicleModel", getVehicleModel())
                .append("status", getStatus())
                .append("platformCode", getPlatformCode())
                .append("createBy", getCreateBy())
                .append("createTime", getCreateTime())
                .append("updateBy", getUpdateBy())
ruoyi-system/src/main/java/com/ruoyi/system/service/ICmsGpsCollectService.java
New file
@@ -0,0 +1,47 @@
package com.ruoyi.system.service;
import com.ruoyi.system.domain.CmsGpsLoginResponse;
import com.ruoyi.system.domain.CmsVehicleDeviceResponse;
import com.ruoyi.system.domain.CmsVehicleDeviceListResponse;
import com.ruoyi.system.domain.CmsVehicleLocationResponse;
/**
 * CMS GPS采集服务接口
 */
public interface ICmsGpsCollectService {
    /**
     * 登录CMS系统
     *
     * @param username 用户名
     * @param password 密码
     * @return 登录响应
     */
    CmsGpsLoginResponse login(String username, String password);
    /**
     * 查询车辆设备信息
     *
     * @return 车辆设备响应
     */
    CmsVehicleDeviceResponse queryVehicleDevices();
    /**
     * 获取车辆设备列表
     *
     * @param vehicleId 车牌号,多个以逗号分隔
     * @return 车辆设备列表响应
     */
    CmsVehicleDeviceListResponse getDeviceByVehicle(String vehicleId);
    /**
     * 获取车辆最新位置信息
     *
     * @param vehicleId 车牌号,多个以逗号分隔
     * @param toMap 地图经纬度转换(1:谷歌地图,2:百度地图)
     * @param geoAddress 是否解析地理位置(1:是)
     * @param currentPage 当前页码
     * @param pageRecords 每页记录数
     * @return 车辆位置信息响应
     */
    CmsVehicleLocationResponse getVehicleLocation(String vehicleId, Integer toMap, Integer geoAddress, Integer currentPage, Integer pageRecords);
}
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/CmsGpsCollectServiceImpl.java
New file
@@ -0,0 +1,199 @@
package com.ruoyi.system.service.impl;
import com.ruoyi.common.utils.HttpUtil;
import com.ruoyi.common.utils.http.HttpUtils;
import com.ruoyi.system.domain.CmsGpsLoginResponse;
import com.ruoyi.system.domain.CmsVehicleDeviceResponse;
import com.ruoyi.system.domain.CmsVehicleDeviceListResponse;
import com.ruoyi.system.domain.CmsVehicleLocationResponse;
import com.ruoyi.system.domain.SysGpsConfig;
import com.ruoyi.system.service.ICmsGpsCollectService;
import com.ruoyi.system.service.IGpsConfigService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson.JSONObject;
import java.util.Date;
/**
 * CMS GPS采集服务实现
 */
@Service
public class CmsGpsCollectServiceImpl implements ICmsGpsCollectService {
    private static final Logger log = LoggerFactory.getLogger(CmsGpsCollectServiceImpl.class);
    @Autowired
    private IGpsConfigService gpsConfigService;
    @Override
    public CmsGpsLoginResponse login(String username, String password) {
        try {
            // 从数据库获取CMS配置
            SysGpsConfig baseUrlConfig = gpsConfigService.selectGpsConfigByKey("gpscms");
            if (baseUrlConfig == null) {
                throw new RuntimeException("未配置CMS系统地址");
            }
            // 发送登录请求
            String response = HttpUtil.get(baseUrlConfig.getDomain() + "/StandardApiAction_login.action?account=" + username + "&password=" + password,null);
            // 解析响应
            CmsGpsLoginResponse loginResponse = JSONObject.parseObject(response, CmsGpsLoginResponse.class);
            if (loginResponse.getResult() == 0) {
                log.info("CMS系统登录成功,用户:{}", username);
                // 保存会话ID到配置表
                baseUrlConfig.setToken(loginResponse.getJsession());
                // 设置token过期时间为20小时
                baseUrlConfig.setTokenExpireTime(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 20));
                gpsConfigService.updateGpsConfig(baseUrlConfig);
            } else {
                log.error("CMS系统登录失败,url:{},user:{},password:{},response:{}", baseUrlConfig.getDomain(), username, password, response);
            }
            return loginResponse;
        } catch (Exception e) {
            log.error("CMS系统登录异常", e);
            throw new RuntimeException("CMS系统登录异常:" + e.getMessage());
        }
    }
    @Override
    public CmsVehicleDeviceResponse queryVehicleDevices() {
        try {
            // 从数据库获取CMS配置
            SysGpsConfig baseUrlConfig = gpsConfigService.selectGpsConfigByKey("gpscms");
            if (baseUrlConfig == null) {
                throw new RuntimeException("未配置CMS系统地址");
            }
            // 检查token是否过期
            if (baseUrlConfig.getTokenExpireTime() == null ||
                baseUrlConfig.getTokenExpireTime().before(new Date())) {
                // token过期,重新登录
                login(baseUrlConfig.getUsername(), baseUrlConfig.getPassword());
                baseUrlConfig = gpsConfigService.selectGpsConfigByKey("gpscms");
            }
            // 发送查询请求
            String response = HttpUtil.get(baseUrlConfig.getDomain() + "/StandardApiAction_queryUserVehicle.action?jsession=" + baseUrlConfig.getToken(),null);
            // 解析响应
            CmsVehicleDeviceResponse deviceResponse = JSONObject.parseObject(response, CmsVehicleDeviceResponse.class);
            if (deviceResponse.getResult() == 0) {
                log.info("查询车辆设备信息成功,共{}条记录", deviceResponse.getVehicles().size());
            } else {
                log.error("查询车辆设备信息失败");
            }
            return deviceResponse;
        } catch (Exception e) {
            log.error("查询车辆设备信息异常", e);
            throw new RuntimeException("查询车辆设备信息异常:" + e.getMessage());
        }
    }
    @Override
    public CmsVehicleDeviceListResponse getDeviceByVehicle(String vehicleId) {
        try {
            // 从数据库获取CMS配置
            SysGpsConfig baseUrlConfig = gpsConfigService.selectGpsConfigByKey("gpscms");
            if (baseUrlConfig == null) {
                throw new RuntimeException("未配置CMS系统地址");
            }
            // 检查token是否过期
            if (baseUrlConfig.getTokenExpireTime() == null ||
                baseUrlConfig.getTokenExpireTime().before(new Date())) {
                // token过期,重新登录
                login(baseUrlConfig.getUsername(), baseUrlConfig.getPassword());
                baseUrlConfig = gpsConfigService.selectGpsConfigByKey("gpscms");
            }
            // 构建请求URL
            String url = baseUrlConfig.getDomain() + "/StandardApiAction_getDeviceByVehicle.action?jsession=" + baseUrlConfig.getToken();
            if (vehicleId != null && !vehicleId.isEmpty()) {
                url += "&vehiIdno=" + vehicleId;
            }
            // 发送查询请求
            String response = HttpUtil.get(url,null);
            // 解析响应
            CmsVehicleDeviceListResponse deviceListResponse = JSONObject.parseObject(response, CmsVehicleDeviceListResponse.class);
            if (deviceListResponse.getResult() == 0) {
                log.info("获取车辆设备列表成功,车牌号:{}", vehicleId);
            } else {
                log.error("获取车辆设备列表失败,车牌号:{}", vehicleId);
            }
            return deviceListResponse;
        } catch (Exception e) {
            log.error("获取车辆设备列表异常,车牌号:{}", vehicleId, e);
            throw new RuntimeException("获取车辆设备列表异常:" + e.getMessage());
        }
    }
    @Override
    public CmsVehicleLocationResponse getVehicleLocation(String vehicleId, Integer toMap, Integer geoAddress, Integer currentPage, Integer pageRecords) {
        try {
            // 从数据库获取CMS配置
            SysGpsConfig baseUrlConfig = gpsConfigService.selectGpsConfigByKey("gpscms");
            if (baseUrlConfig == null) {
                throw new RuntimeException("未配置CMS系统地址");
            }
            // 检查token是否过期
            if (baseUrlConfig.getTokenExpireTime() == null ||
                baseUrlConfig.getTokenExpireTime().before(new Date())) {
                // token过期,重新登录
                login(baseUrlConfig.getUsername(), baseUrlConfig.getPassword());
                baseUrlConfig = gpsConfigService.selectGpsConfigByKey("gpscms");
            }
            // 构建请求URL
            StringBuilder url = new StringBuilder();
            url.append(baseUrlConfig.getDomain())
               .append("/StandardApiAction_vehicleStatus.action?jsession=")
               .append(baseUrlConfig.getToken());
            if (vehicleId != null && !vehicleId.isEmpty()) {
                url.append("&vehiIdno=").append(vehicleId);
            }
            if (toMap != null) {
                url.append("&toMap=").append(toMap);
            }
            if (geoAddress != null) {
                url.append("&geoaddress=").append(geoAddress);
            }
            if (currentPage != null) {
                url.append("&currentPage=").append(currentPage);
            }
            if (pageRecords != null) {
                url.append("&pageRecords=").append(pageRecords);
            }
            // 发送查询请求
            String response = HttpUtils.sendGet(url.toString());
            // 解析响应
            CmsVehicleLocationResponse locationResponse = JSONObject.parseObject(response, CmsVehicleLocationResponse.class);
            if (locationResponse.getResult() == 0) {
                log.info("获取车辆位置信息成功,车牌号:{}", vehicleId);
            } else {
                log.error("获取车辆位置信息失败,车牌号:{}", vehicleId);
            }
            return locationResponse;
        } catch (Exception e) {
            log.error("获取车辆位置信息异常,车牌号:{}", vehicleId, e);
            throw new RuntimeException("获取车辆位置信息异常:" + e.getMessage());
        }
    }
}
ruoyi-system/src/main/resources/mapper/system/VehicleInfoMapper.xml
@@ -4,23 +4,24 @@
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.system.mapper.VehicleInfoMapper">
    
    <resultMap type="VehicleInfo" id="VehicleInfoResult">
        <result property="vehicleId"    column="vehicle_id"    />
        <result property="deviceId"     column="device_id"     />
        <result property="vehicleNo"    column="vehicle_no"    />
        <result property="vehicleType"  column="vehicle_type"  />
        <result property="vehicleBrand" column="vehicle_brand" />
        <result property="vehicleModel" column="vehicle_model" />
        <result property="status"       column="status"        />
        <result property="createBy"     column="create_by"     />
        <result property="createTime"   column="create_time"   />
        <result property="updateBy"     column="update_by"     />
        <result property="updateTime"   column="update_time"   />
        <result property="remark"       column="remark"        />
    <resultMap type="com.ruoyi.system.domain.VehicleInfo" id="VehicleInfoResult">
        <id     property="vehicleId"      column="vehicle_id"      />
        <result property="deviceId"       column="device_id"       />
        <result property="vehicleNo"      column="vehicle_no"      />
        <result property="vehicleType"    column="vehicle_type"    />
        <result property="vehicleBrand"   column="vehicle_brand"   />
        <result property="vehicleModel"   column="vehicle_model"   />
        <result property="status"         column="status"          />
        <result property="platformCode"   column="platform_code"   />
        <result property="createBy"       column="create_by"       />
        <result property="createTime"     column="create_time"     />
        <result property="updateBy"       column="update_by"       />
        <result property="updateTime"     column="update_time"     />
        <result property="remark"         column="remark"          />
    </resultMap>
    <sql id="selectVehicleInfoVo">
        select vehicle_id, device_id, vehicle_no, vehicle_type, vehicle_brand, vehicle_model, status, create_by, create_time, update_by, update_time, remark
        select vehicle_id, device_id, vehicle_no, vehicle_type, vehicle_brand, vehicle_model, status, platform_code, create_by, create_time, update_by, update_time, remark
        from tb_vehicle_info
    </sql>
@@ -33,6 +34,7 @@
            <if test="vehicleBrand != null  and vehicleBrand != ''"> and vehicle_brand = #{vehicleBrand}</if>
            <if test="vehicleModel != null  and vehicleModel != ''"> and vehicle_model = #{vehicleModel}</if>
            <if test="status != null  and status != ''"> and status = #{status}</if>
            <if test="platformCode != null  and platformCode != ''"> and platform_code = #{platformCode}</if>
        </where>
    </select>
    
@@ -55,6 +57,7 @@
            <if test="vehicleBrand != null">vehicle_brand,</if>
            <if test="vehicleModel != null">vehicle_model,</if>
            <if test="status != null">status,</if>
            <if test="platformCode != null">platform_code,</if>
            <if test="createBy != null">create_by,</if>
            <if test="createTime != null">create_time,</if>
            <if test="updateBy != null">update_by,</if>
@@ -68,6 +71,7 @@
            <if test="vehicleBrand != null">#{vehicleBrand},</if>
            <if test="vehicleModel != null">#{vehicleModel},</if>
            <if test="status != null">#{status},</if>
            <if test="platformCode != null">#{platformCode},</if>
            <if test="createBy != null">#{createBy},</if>
            <if test="createTime != null">#{createTime},</if>
            <if test="updateBy != null">#{updateBy},</if>
@@ -85,8 +89,10 @@
            <if test="vehicleBrand != null">vehicle_brand = #{vehicleBrand},</if>
            <if test="vehicleModel != null">vehicle_model = #{vehicleModel},</if>
            <if test="status != null">status = #{status},</if>
            <if test="platformCode != null">platform_code = #{platformCode},</if>
            <if test="updateBy != null">update_by = #{updateBy},</if>
            update_time = sysdate()
            <if test="updateTime != null">update_time = #{updateTime},</if>
            <if test="remark != null">remark = #{remark},</if>
        </trim>
        where vehicle_id = #{vehicleId}
    </update>
ruoyi-ui/src/views/system/gps/map.vue
@@ -356,7 +356,6 @@
            marker.setLabel(label);
          } else {
            // 终点显示车辆图标
            const myIcon = new BMap.Icon(
              "/car_blue.png",
              new BMap.Size(20, 20),
@@ -384,7 +383,6 @@
            borderRadius: "3px",
          });
          marker.setLabel(label);
          // 获取地址信息
          const geoc = new BMap.Geocoder();
@@ -397,13 +395,14 @@
              addComp.street +
              addComp.streetNumber;
            // 添加信息窗口
            const infoWindow = new BMap.InfoWindow(
              `时间:${item.collectTime}<br/>速度:${
                item.speed
              }km/h<br/>方向:${item.direction}°<br/>地址:${address}`
            );
            // 添加点击事件监听器
            marker.addEventListener("click", () => {
              // 创建信息窗口
              const infoWindow = new BMap.InfoWindow(
                `时间:${currentSegment[index].collectTime}<br/>速度:${
                  currentSegment[index].speed
                }km/h<br/>方向:${currentSegment[index].direction}°<br/>地址:${address}`
              );
              this.map.openInfoWindow(infoWindow, bdPoint);
            });
          });
@@ -475,12 +474,25 @@
          });
          marker.setLabel(label);
          
          // 添加信息窗口
          const infoWindow = new BMap.InfoWindow(
            `时间:${row.collectTime}<br/>速度:${row.speed}km/h<br/>方向:${row.direction}°<br/>地址:${row.address}`
          );
          marker.addEventListener("click", () => {
            this.map.openInfoWindow(infoWindow, data.points[0]);
          // 获取地址信息
          const geoc = new BMap.Geocoder();
          geoc.getLocation(data.points[0], (rs) => {
            const addComp = rs.addressComponents;
            const address =
              addComp.province +
              addComp.city +
              addComp.district +
              addComp.street +
              addComp.streetNumber;
            // 添加点击事件监听器
            marker.addEventListener("click", () => {
              // 创建信息窗口
              const infoWindow = new BMap.InfoWindow(
                `时间:${row.collectTime}<br/>速度:${row.speed}km/h<br/>方向:${row.direction}°<br/>地址:${address}`
              );
              this.map.openInfoWindow(infoWindow, data.points[0]);
            });
          });
          
          // 保存当前标记点引用
ruoyi-ui/src/views/system/vehicle/index.vue
@@ -31,8 +31,12 @@
      </el-form-item>
      <el-form-item label="平台标识" prop="platformCode">
        <el-select v-model="queryParams.platformCode" placeholder="请选择平台" clearable size="small">
          <el-option label="A平台" value="A" />
          <el-option label="B平台" value="B" />
          <el-option
            v-for="dict in dict.type.sys_platform"
            :key="dict.value"
            :label="dict.label"
            :value="dict.value"
          />
        </el-select>
      </el-form-item>
      <el-form-item>
@@ -153,9 +157,13 @@
          <el-input v-model="form.vehicleModel" placeholder="请输入车辆型号" />
        </el-form-item>
        <el-form-item label="平台标识" prop="platformCode">
          <el-select v-model="form.platformCode" placeholder="请选择平台">
            <el-option label="A平台" value="A" />
            <el-option label="B平台" value="B" />
          <el-select v-model="form.platformCode" placeholder="请选择平台" clearable>
            <el-option
              v-for="dict in dict.type.sys_platform"
              :key="dict.value"
              :label="dict.label"
              :value="dict.value"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="状态" prop="status">
@@ -217,7 +225,16 @@
        platformCode: null
      },
      // 表单参数
      form: {},
      form: {
        vehicleId: null,
        vehicleNo: null,
        vehicleType: null,
        vehicleBrand: null,
        vehicleModel: null,
        status: "0",
        remark: null,
        platformCode: null
      },
      // 表单校验
      rules: {
        vehicleNo: [
@@ -225,6 +242,9 @@
        ],
        status: [
          { required: true, message: "状态不能为空", trigger: "change" }
        ],
        platformCode: [
          { required: true, message: "平台标识不能为空", trigger: "change" }
        ]
      }
    };
sql/cms_vehicle_sync_job.sql
New file
@@ -0,0 +1,30 @@
-- 添加CMS车辆同步定时任务
INSERT INTO sys_job (job_name, job_group, invoke_target, cron_expression, misfire_policy, concurrent, status, create_by, create_time, remark)
VALUES (
    'CMS车辆同步任务',           -- 任务名称
    'DEFAULT',                   -- 任务组名
    'cmsVehicleSyncTask.syncVehicleInfo()',  -- 调用目标字符串
    '0 0/30 * * * ?',           -- 每30分钟执行一次
    '3',                        -- 计划执行策略(3=不触发立即执行)
    '1',                        -- 是否并发执行(1=禁止)
    '0',                        -- 状态(0=正常)
    'admin',                    -- 创建者
    sysdate(),                  -- 创建时间
    'CMS车辆信息同步定时任务'    -- 备注
);
-- 添加CMS车辆同步定时任务
INSERT INTO sys_job (job_name, job_group, invoke_target, cron_expression, misfire_policy, concurrent, status, create_by, create_time, remark)
VALUES (
    'CMS车辆同步GPS',           -- 任务名称
    'DEFAULT',                   -- 任务组名
    'cmsVehicleSyncTask.syncVehicleLocation()',  -- 调用目标字符串
    '0 0/30 * * * ?',           -- 每30分钟执行一次
    '3',                        -- 计划执行策略(3=不触发立即执行)
    '1',                        -- 是否并发执行(1=禁止)
    '0',                        -- 状态(0=正常)
    'admin',                    -- 创建者
    sysdate(),                  -- 创建时间
    'CMS车辆GPS采集'    -- 备注
);