wlzboy
2025-11-01 8b005a808d6ab8fae1480ed57bdfd68af2dafcd4
feat:输入出入地址时,会自动计算距离
10个文件已修改
207 ■■■■ 已修改文件
app/App.vue 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/pages/login.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/pages/task/create-emergency.vue 86 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/VehicleGpsController.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/application.yml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/GpsSyncTask.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/LegacySystemSyncServiceImpl.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/HospDataMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/legacy_status_sync_job.sql 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/task_status_push_job.sql 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/App.vue
@@ -15,6 +15,17 @@
      this.lastToken = getToken()
      this.initApp()
      
      // 监听用户登录事件
      uni.$on('user-login', () => {
        console.log('接收到用户登录事件,启动消息轮询')
        const token = getToken()
        if (token) {
          this.lastToken = token
          this.updateUnreadMessageBadge()
          this.startMessagePolling()
        }
      })
      // 监听用户登出事件
      uni.$on('user-logout', () => {
        console.log('接收到用户登出事件,停止消息轮询')
@@ -64,12 +75,8 @@
        this.checkLogin()
        //#endif
        
        // 如果已登录,启动未读消息轮询
        if (getToken()) {
          this.updateUnreadMessageBadge()
          // 每30秒轮询一次
          this.startMessagePolling()
        }
        // 注意:不在应用启动时自动启动轮询
        // 只有在用户主动登录成功后才启动(通过 user-login 事件触发)
      },
      initConfig() {
        this.globalData.config = config
app/pages/login.vue
@@ -125,6 +125,8 @@
      loginSuccess(result) {
        // 设置用户信息
        this.$store.dispatch('GetInfo').then(res => {
          // 触发登录成功事件,启动消息轮询
          uni.$emit('user-login')
          this.$tab.reLaunch('/pages/index')
        })
      }
app/pages/task/create-emergency.vue
@@ -78,7 +78,7 @@
    
      
      <view class="form-item">
        <view class="form-label">转运时间</view>
        <view class="form-label required">转运时间</view>
        <uni-datetime-picker 
          v-model="taskForm.transferTime" 
          type="datetime" 
@@ -443,7 +443,7 @@
import uniPopup from '@/uni_modules/uni-popup/components/uni-popup/uni-popup.vue'
import { addTask } from "@/api/task"
import { listAvailableVehicles, getUserBoundVehicle } from "@/api/vehicle"
import { calculateDistance } from "@/api/map"
import { calculateDistance, baiduDistanceByAddress } from "@/api/map"
import { searchHospitals } from "@/api/hospital"
import { listUser } from "@/api/system/user"
import { searchIcd10 } from "@/api/icd10"
@@ -560,6 +560,9 @@
    }
  },
  onLoad(options) {
    // 设置默认转运时间为当前时间
    this.setDefaultTransferTime()
    // 先加载车辆列表,然后加载绑定车辆信息
    this.getAvailableVehicles().then(() => {
      this.getUserBoundVehicleInfo()
@@ -902,15 +905,12 @@
      this.showHospitalOutResults = false
      this.hospitalOutResults = []
      
      // 如果有GPS坐标,保存下来
      // 注意:HospData表中可能没有GPS坐标,需要根据地址进行地理编码
      // 这里先置为null,后续可以通过地址解析获取
      this.addressCoordinates.hospitalOutAddress = null
      // 保存转出医院的城市信息
      this.taskForm.hospitalOut.city = hospital.hopsCity || ''
      
      // 如果两个医院都已选择,自动计算距离
      if (this.taskForm.hospitalIn.address) {
        // 这里可以调用地址解析和距离计算
        // 暂时留空,由用户手动输入距离
        this.calculateHospitalDistance()
      }
    },
    
@@ -986,13 +986,12 @@
      this.showHospitalInResults = false
      this.hospitalInResults = []
      
      // 如果有GPS坐标,保存下来
      this.addressCoordinates.hospitalInAddress = null
      // 保存转入医院的城市信息
      this.taskForm.hospitalIn.city = hospital.hopsCity || ''
      
      // 如果两个医院都已选择,自动计算距离
      if (this.taskForm.hospitalOut.address) {
        // 这里可以调用地址解析和距离计算
        // 暂时留空,由用户手动输入距离
        this.calculateHospitalDistance()
      }
    },
    
@@ -1271,6 +1270,20 @@
      this.selectedDiseases.splice(index, 1)
    },
    
    // 设置默认转运时间为当前时间
    setDefaultTransferTime() {
      const now = new Date()
      const year = now.getFullYear()
      const month = String(now.getMonth() + 1).padStart(2, '0')
      const day = String(now.getDate()).padStart(2, '0')
      const hours = String(now.getHours()).padStart(2, '0')
      const minutes = String(now.getMinutes()).padStart(2, '0')
      const seconds = String(now.getSeconds()).padStart(2, '0')
      // 格式化为 YYYY-MM-DD HH:mm:ss
      this.taskForm.transferTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
    },
    validateForm() {
      if (!this.selectedVehicleId) {
        this.$modal.showToast('请选择任务车辆')
@@ -1284,6 +1297,11 @@
      
      if (!this.selectedDocumentType) {
        this.$modal.showToast('请选择单据类型')
        return false
      }
      if (!this.taskForm.transferTime) {
        this.$modal.showToast('请选择转运时间')
        return false
      }
      
@@ -1447,6 +1465,50 @@
        parts.push(hospital.hospAddress)
      }
      return parts.join('')
    },
    // 自动计算两个医院之间的距离
    calculateHospitalDistance() {
      const fromAddress = this.taskForm.hospitalOut.address
      const fromCity = this.taskForm.hospitalOut.city
      const toAddress = this.taskForm.hospitalIn.address
      const toCity = this.taskForm.hospitalIn.city
      if (!fromAddress || !toAddress) {
        console.log('地址信息不完整,无法计算距离')
        return
      }
      console.log('开始计算距离:', fromAddress, '->', toAddress)
      // 显示加载提示
      uni.showLoading({
        title: '计算距离中...'
      })
      // 调用百度地图API计算距离
      baiduDistanceByAddress(fromAddress, fromCity, toAddress, toCity)
        .then(response => {
          uni.hideLoading()
          if (response.code === 200 && response.data) {
            const distanceInMeters = response.data.distance
            // 转换为公里,保留1位小数
            const distanceInKm = (distanceInMeters / 1000).toFixed(1)
            this.taskForm.transferDistance = distanceInKm
            console.log('距离计算成功:', distanceInKm, 'km')
            this.$modal.showToast(`距离计算成功: ${distanceInKm}公里`)
          } else {
            console.error('距离计算失败:', response.msg)
            this.$modal.showToast('距离计算失败,请手动输入')
          }
        })
        .catch(error => {
          uni.hideLoading()
          console.error('距离计算失败:', error)
          this.$modal.showToast('距离计算失败,请手动输入')
        })
    }
  }
}
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/VehicleGpsController.java
@@ -575,6 +575,17 @@
            String geocodingResponse1 = HttpUtils.sendGet(geocodingUrl1, geocodingParams1);
            logger.info("起点地理编码响应: {}", geocodingResponse1);
            
            // 解析起点坐标
            com.alibaba.fastjson2.JSONObject geocodingJson1 = com.alibaba.fastjson2.JSONObject.parseObject(geocodingResponse1);
            if (geocodingJson1.getInteger("status") != 0) {
                logger.error("起点地理编码失败: {}", geocodingResponse1);
                return AjaxResult.error("起点地址解析失败");
            }
            com.alibaba.fastjson2.JSONObject location1 = geocodingJson1.getJSONObject("result").getJSONObject("location");
            double fromLat = location1.getDouble("lat");
            double fromLng = location1.getDouble("lng");
            logger.info("起点坐标: lat={}, lng={}", fromLat, fromLng);
            // 第二步:终点地址转坐标
            String geocodingUrl2 = "https://api.map.baidu.com/geocoding/v3/";
            String geocodingParams2 = "address=" + URLEncoder.encode(toAddress, StandardCharsets.UTF_8.toString()) +
@@ -586,14 +597,70 @@
            String geocodingResponse2 = HttpUtils.sendGet(geocodingUrl2, geocodingParams2);
            logger.info("终点地理编码响应: {}", geocodingResponse2);
            
            // 解析坐标(这里简化处理,实际应该解析JSON)
            // 注意:需要从响应中提取坐标,这里返回中间结果供前端处理
            Map<String, Object> result = new HashMap<>();
            result.put("fromGeocoding", geocodingResponse1);
            result.put("toGeocoding", geocodingResponse2);
            result.put("message", "请解析坐标后调用 /baidu/route/driving 接口计算距离");
            // 解析终点坐标
            com.alibaba.fastjson2.JSONObject geocodingJson2 = com.alibaba.fastjson2.JSONObject.parseObject(geocodingResponse2);
            if (geocodingJson2.getInteger("status") != 0) {
                logger.error("终点地理编码失败: {}", geocodingResponse2);
                return AjaxResult.error("终点地址解析失败");
            }
            com.alibaba.fastjson2.JSONObject location2 = geocodingJson2.getJSONObject("result").getJSONObject("location");
            double toLat = location2.getDouble("lat");
            double toLng = location2.getDouble("lng");
            logger.info("终点坐标: lat={}, lng={}", toLat, toLng);
            
            return AjaxResult.success("地理编码成功", result);
            // 第三步:调用路线规划接口计算距离
            String routeUrl = "https://api.map.baidu.com/directionlite/v1/driving";
            String origin = fromLat + "," + fromLng;
            String destination = toLat + "," + toLng;
            String routeParams = "origin=" + origin +
                                "&destination=" + destination +
                                "&ak=" + baiduMapConfig.getAk();
            logger.info("路线规划请求: origin={}, destination={}", origin, destination);
            String routeResponse = HttpUtils.sendGet(routeUrl, routeParams);
            logger.info("路线规划响应: {}", routeResponse);
            // 解析距离结果
            com.alibaba.fastjson2.JSONObject routeJson = com.alibaba.fastjson2.JSONObject.parseObject(routeResponse);
            if (routeJson.getInteger("status") != 0) {
                logger.error("路线规划失败: {}", routeResponse);
                return AjaxResult.error("路线规划失败");
            }
            // 提取距离信息(单位:米)
            com.alibaba.fastjson2.JSONObject result = routeJson.getJSONObject("result");
            com.alibaba.fastjson2.JSONArray routes = result.getJSONArray("routes");
            if (routes == null || routes.isEmpty()) {
                logger.error("未找到路线信息");
                return AjaxResult.error("未找到路线信息");
            }
            com.alibaba.fastjson2.JSONObject route = routes.getJSONObject(0);
            int distance = route.getInteger("distance"); // 距离,单位:米
            int duration = route.getInteger("duration"); // 时长,单位:秒
            logger.info("计算成功: 距离={}米, 时长={}秒", distance, duration);
            // 构建返回结果
            Map<String, Object> resultMap = new HashMap<>();
            resultMap.put("distance", distance); // 距离(米)
            resultMap.put("duration", duration); // 时长(秒)
            resultMap.put("distanceKm", String.format("%.1f", distance / 1000.0)); // 距离(公里)
            resultMap.put("durationMin", duration / 60); // 时长(分钟)
            // 起点坐标
            Map<String, Object> fromLocation = new HashMap<>();
            fromLocation.put("lat", fromLat);
            fromLocation.put("lng", fromLng);
            resultMap.put("fromLocation", fromLocation);
            // 终点坐标
            Map<String, Object> toLocation = new HashMap<>();
            toLocation.put("lat", toLat);
            toLocation.put("lng", toLng);
            resultMap.put("toLocation", toLocation);
            return AjaxResult.success("计算成功", resultMap);
        } catch (Exception e) {
            logger.error("计算地址距离失败", e);
            return AjaxResult.error("计算距离失败:" + e.getMessage());
ruoyi-admin/src/main/resources/application.yml
@@ -56,7 +56,7 @@
    basename: i18n/messages
  profiles:
    # 环境 dev|test|prod
    active: prod
    active: dev
  # 文件上传
  servlet:
    multipart:
@@ -159,4 +159,4 @@
# 百度地图配置
baidu:
  map:
    ak: YOUR_BAIDU_MAP_AK_HERE  # 请替换为您的百度地图API Key
    ak: GX7G1RmAbTEQHor9NKpzRiB2jerqaY1E  # 请替换为您的百度地图API Key
ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/GpsSyncTask.java
@@ -114,9 +114,9 @@
            // 保存GPS位置信息
            vehicleGpsService.insertVehicleGps(gps);
            log.info("车辆[{}]GPS位置已更新: 经度={}, 纬度={}, 设备时间={}, 处理时间={}",
                    vehicle.getVehicleNo(), position.getCallon(), position.getCallat(),
                    gps.getDeviceReportTime(), gps.getPlatformProcessTime());
//            log.info("车辆[{}]GPS位置已更新: 经度={}, 纬度={}, 设备时间={}, 处理时间={}",
//                    vehicle.getVehicleNo(), position.getCallon(), position.getCallat(),
//                    gps.getDeviceReportTime(), gps.getPlatformProcessTime());
        } catch (Exception e) {
            log.error("更新车辆[{}]GPS位置失败: {}", vehicle.getVehicleNo(), e.getMessage());
        }
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/LegacySystemSyncServiceImpl.java
@@ -383,7 +383,7 @@
            while (true) {
                // 分页查询已同步服务单但未同步调度单的任务
                List<SysTaskEmergency> pendingTasks = sysTaskEmergencyService.selectPendingDispatchSyncTasks(offset, pageSize);
                log.info("查询到未同步调度单的任务数量: {}", pendingTasks.size());
                if (pendingTasks == null || pendingTasks.isEmpty()) {
                    log.info("没有更多需要同步调度单的任务,offset: {}", offset);
                    break; // 没有更多数据,退出循环
@@ -393,6 +393,7 @@
                
                int pageSuccessCount = 0;
                for (SysTaskEmergency emergency : pendingTasks) {
                    log.info("开始同步调度单,任务ID: {}", emergency.getTaskId());
                    Long dispatchOrdId = syncDispatchOrderToLegacy(emergency.getTaskId());
                    if (dispatchOrdId != null && dispatchOrdId > 0) {
                        pageSuccessCount++;
ruoyi-system/src/main/resources/mapper/system/HospDataMapper.xml
@@ -23,7 +23,7 @@
    </resultMap>
    <select id="searchHospitals" resultMap="HospDataResult">
        SELECT TOP 100
        SELECT TOP 1000
            HospID, HospName, HospCityID, HospShort, 
            HopsProvince, HopsCity, HopsArea, HospAddress, 
            HospTEL, HospUnitID, HospState, HospOAID, 
sql/legacy_status_sync_job.sql
@@ -40,7 +40,7 @@
    `update_time`,
    `remark`
) VALUES (
    '任务状态同步',
    '任务状态旧到新同步',
    'DEFAULT',
    'legacySystemSyncTask.syncTaskStatusFromLegacy()',
    '0 0/5 * * * ?',
sql/task_status_push_job.sql
@@ -3,5 +3,5 @@
-- ----------------------------
INSERT INTO sys_job (job_name, job_group, invoke_target, cron_expression, misfire_policy, concurrent, status, create_by, create_time, update_by, update_time, remark)
VALUES 
('任务状态推送', 'DEFAULT', 'legacySystemSyncTask.pushTaskStatusToLegacy()', '0 0/3 * * * ?', '3', '1', '1', 'admin', sysdate(), 'admin', sysdate(),
('任务状态推送新到旧', 'DEFAULT', 'legacySystemSyncTask.pushTaskStatusToLegacy()', '0 0/3 * * * ?', '3', '1', '1', 'admin', sysdate(), 'admin', sysdate(),
'每3分钟执行一次,将新系统中状态已变化的任务推送到旧系统。推送规则:出发中->4,任务中->6,返程中->7,已完成->8,已取消->10。旧系统状态>=目标状态时不推送。');