在创建急救转运任务时,自动将**转出医院地址**作为**出发地**,将**转入医院地址**作为**目的地**,写入主任务表(sys_task)的相应字段,便于任务调度、路径规划和统计分析。
急救转运任务的核心就是将患者从一个医院转运到另一个医院:
- 转出医院 = 任务的起点(出发地)
- 转入医院 = 任务的终点(目的地)
将这些信息写入主任务表的好处:
1. 统一数据结构:所有类型任务(急救转运、福祉车、保养等)都有出发地和目的地
2. 便于路径规划:GPS导航可以直接使用出发地和目的地坐标
3. 统计分析:可以统计各区域的任务量、行驶距离等
4. 任务调度:调度系统可以根据地理位置分配最近的车辆和人员
5. 旧系统兼容:便于与旧系统的数据同步
前端文件: app/pages/task/create-emergency.vue
后端实体: com.ruoyi.system.domain.SysTask
在 buildSubmitData() 方法中,添加出发地和目的地字段:
const submitData = {
taskType: 'EMERGENCY_TRANSFER',
vehicleIds: this.selectedVehicleId ? [this.selectedVehicleId] : [],
assigneeIds: this.selectedStaff.map(staff => staff.userId),
transferTime: this.taskForm.transferTime,
documentTypeId: this.selectedDocumentTypeId,
taskTypeId: this.selectedEmergencyTaskTypeId,
// 将转出医院地址作为出发地,转入医院地址作为目的地
departureAddress: this.taskForm.hospitalOut.address || '',
destinationAddress: this.taskForm.hospitalIn.address || '',
patient: { ... },
hospitalOut: this.taskForm.hospitalOut,
hospitalIn: this.taskForm.hospitalIn,
transferDistance: this.taskForm.transferDistance ? parseFloat(this.taskForm.transferDistance) : null,
price: this.taskForm.price ? parseFloat(this.taskForm.price) : null
}
如果有GPS坐标信息,同时写入出发地和目的地的经纬度:
if (this.addressCoordinates.hospitalOutAddress) {
// 转出医院GPS坐标写入扩展表
if (!submitData.hospitalOut) submitData.hospitalOut = {}
submitData.hospitalOut.longitude = this.addressCoordinates.hospitalOutAddress.lng
submitData.hospitalOut.latitude = this.addressCoordinates.hospitalOutAddress.lat
// 同时写入主任务表的出发地经纬度
submitData.departureLongitude = this.addressCoordinates.hospitalOutAddress.lng
submitData.departureLatitude = this.addressCoordinates.hospitalOutAddress.lat
}
if (this.addressCoordinates.hospitalInAddress) {
// 转入医院GPS坐标写入扩展表
if (!submitData.hospitalIn) submitData.hospitalIn = {}
submitData.hospitalIn.longitude = this.addressCoordinates.hospitalInAddress.lng
submitData.hospitalIn.latitude = this.addressCoordinates.hospitalInAddress.lat
// 同时写入主任务表的目的地经纬度
submitData.destinationLongitude = this.addressCoordinates.hospitalInAddress.lng
submitData.destinationLatitude = this.addressCoordinates.hospitalInAddress.lat
}
主任务表(sys_task)相关字段:
-- 出发地址
departure_address VARCHAR(500)
-- 目的地址
destination_address VARCHAR(500)
-- 出发地经度
departure_longitude DECIMAL(10,7)
-- 出发地纬度
departure_latitude DECIMAL(10,7)
-- 目的地经度
destination_longitude DECIMAL(10,7)
-- 目的地纬度
destination_latitude DECIMAL(10,7)
对应的Java实体字段:
public class SysTask extends BaseEntity {
/** 出发地址 */
private String departureAddress;
/** 目的地址 */
private String destinationAddress;
/** 出发地经度 */
private java.math.BigDecimal departureLongitude;
/** 出发地纬度 */
private java.math.BigDecimal departureLatitude;
/** 目的地经度 */
private java.math.BigDecimal destinationLongitude;
/** 目的地纬度 */
private java.math.BigDecimal destinationLatitude;
// ... getters and setters
}
用户选择转出医院
↓
选择医院后,自动填充 taskForm.hospitalOut.address
(例如:"广东省广州市天河区中山大道123号")
↓
用户选择转入医院
↓
选择医院后,自动填充 taskForm.hospitalIn.address
(例如:"广东省广州市越秀区解放路456号")
↓
用户点击"保存"
↓
调用 buildSubmitData()
↓
设置 departureAddress = taskForm.hospitalOut.address
设置 destinationAddress = taskForm.hospitalIn.address
↓
如果有GPS坐标,同时设置经纬度字段
↓
提交到后端
↓
保存到 sys_task 表
taskForm: {
hospitalOut: {
id: 123, // 医院ID
name: "广州市第一人民医院", // 医院名称
department: "急诊科", // 科室
departmentId: 10, // 科室ID
bedNumber: "A101", // 床号
address: "广东省广州市天河区中山大道123号", // 完整地址
longitude: 113.3245, // 经度(如果有GPS)
latitude: 23.1291 // 纬度(如果有GPS)
},
hospitalIn: {
id: 456,
name: "广州市中医医院",
department: "骨科",
departmentId: 15,
bedNumber: "B205",
address: "广东省广州市越秀区解放路456号",
longitude: 113.2654,
latitude: 23.1389
}
}
{
taskType: "EMERGENCY_TRANSFER",
vehicleIds: [101],
assigneeIds: [1, 2, 3],
// 主任务表字段
departureAddress: "广东省广州市天河区中山大道123号",
destinationAddress: "广东省广州市越秀区解放路456号",
departureLongitude: 113.3245,
departureLatitude: 23.1291,
destinationLongitude: 113.2654,
destinationLatitude: 23.1389,
// 扩展信息
hospitalOut: {
id: 123,
name: "广州市第一人民医院",
department: "急诊科",
departmentId: 10,
bedNumber: "A101",
address: "广东省广州市天河区中山大道123号",
longitude: 113.3245,
latitude: 23.1291
},
hospitalIn: {
id: 456,
name: "广州市中医医院",
department: "骨科",
departmentId: 15,
bedNumber: "B205",
address: "广东省广州市越秀区解放路456号",
longitude: 113.2654,
latitude: 23.1389
},
patient: { ... },
transferDistance: 15.5,
price: 300.00
}
主任务表(sys_task):
| 字段 | 值 | 说明 |
|---|---|---|
| task_id | 10001 | 任务ID |
| task_type | EMERGENCY_TRANSFER | 任务类型 |
| departure_address | 广东省广州市天河区中山大道123号 | 出发地址(转出医院) |
| destination_address | 广东省广州市越秀区解放路456号 | 目的地址(转入医院) |
| departure_longitude | 113.3245 | 出发地经度 |
| departure_latitude | 23.1291 | 出发地纬度 |
| destination_longitude | 113.2654 | 目的地经度 |
| destination_latitude | 23.1389 | 目的地纬度 |
扩展信息表(sys_task_emergency):
| 字段 | 值 | 说明 |
|---|---|---|
| emergency_id | 20001 | 扩展信息ID |
| task_id | 10001 | 关联任务ID |
| hospital_out_id | 123 | 转出医院ID |
| hospital_out_name | 广州市第一人民医院 | 转出医院名称 |
| hospital_out_department_id | 10 | 转出科室ID |
| hospital_out_department | 急诊科 | 转出科室 |
| hospital_out_address | 广东省广州市天河区中山大道123号 | 转出医院地址 |
| hospital_in_id | 456 | 转入医院ID |
| hospital_in_name | 广州市中医医院 | 转入医院名称 |
| hospital_in_department_id | 15 | 转入科室ID |
| hospital_in_department | 骨科 | 转入科室 |
| hospital_in_address | 广东省广州市越秀区解放路456号 | 转入医院地址 |
调度系统可以根据出发地坐标,计算距离最近的可用车辆:
// 查询离出发地最近的车辆
SELECT v.*,
SQRT(POW(v.current_longitude - task.departure_longitude, 2) +
POW(v.current_latitude - task.departure_latitude, 2)) AS distance
FROM tb_vehicle_info v
JOIN sys_task task ON task.task_id = ?
WHERE v.status = 'AVAILABLE'
ORDER BY distance
LIMIT 5
GPS导航可以直接使用出发地和目的地坐标:
// 调用地图API规划路线
const route = await mapAPI.planRoute({
origin: {
longitude: task.departureLongitude,
latitude: task.departureLatitude
},
destination: {
longitude: task.destinationLongitude,
latitude: task.destinationLatitude
}
})
// 显示预计时间和距离
console.log('预计行驶时间:', route.duration, '分钟')
console.log('预计行驶距离:', route.distance, '公里')
按区域统计任务量:
-- 统计各区域的任务数量
SELECT
SUBSTRING(departure_address, 1, CHARINDEX('市', departure_address)) AS region,
COUNT(*) AS task_count,
AVG(estimated_distance) AS avg_distance
FROM sys_task
WHERE task_type = 'EMERGENCY_TRANSFER'
AND create_time >= '2025-01-01'
GROUP BY SUBSTRING(departure_address, 1, CHARINDEX('市', departure_address))
ORDER BY task_count DESC
在任务列表中显示出发地和目的地:
<view class="task-item">
<view class="task-info">
<text class="task-code">{{ task.taskCode }}</text>
<text class="task-type">急救转运</text>
</view>
<view class="task-location">
<view class="location-item">
<uni-icons type="location" size="16" color="#007AFF"></uni-icons>
<text class="departure">{{ task.departureAddress }}</text>
</view>
<view class="arrow">→</view>
<view class="location-item">
<uni-icons type="location-filled" size="16" color="#ff4d4f"></uni-icons>
<text class="destination">{{ task.destinationAddress }}</text>
</view>
</view>
</view>
场景: 用户选择了医院但地址未能获取
处理:javascript departureAddress: this.taskForm.hospitalOut.address || '', destinationAddress: this.taskForm.hospitalIn.address || '',
结果: 使用空字符串,不影响任务创建
场景: 医院数据中没有GPS坐标信息
处理:javascript if (this.addressCoordinates.hospitalOutAddress) { // 只在有坐标时才设置 submitData.departureLongitude = ... submitData.departureLatitude = ... }
结果: 经纬度字段为 NULL,不影响任务创建
场景: 完整地址超过数据库字段长度限制(500字符)
处理: 数据库字段定义为 VARCHAR(500),通常足够
备选方案: 如果确实超长,可以在前端截取:javascript departureAddress: (this.taskForm.hospitalOut.address || '').substring(0, 500), destinationAddress: (this.taskForm.hospitalIn.address || '').substring(0, 500),
出发地和目的地信息在两个地方存储:
departure_address / destination_addressdeparture_longitude / departure_latitudedestination_longitude / destination_latitudehospital_out_address / hospital_in_addresshospital_out_longitude / hospital_out_latitudehospital_in_longitude / hospital_in_latitude前端代码确保数据一致:
```javascript
// 地址一致
departureAddress: this.taskForm.hospitalOut.address
hospitalOut.address: this.taskForm.hospitalOut.address
// GPS坐标一致
departureLongitude: this.addressCoordinates.hospitalOutAddress.lng
hospitalOut.longitude: this.addressCoordinates.hospitalOutAddress.lng
```
根据项目记忆:
操作:
1. 选择转出医院:"广州市第一人民医院"
2. 地址自动填充:"广东省广州市天河区中山大道123号"
3. 选择转入医院:"广州市中医医院"
4. 地址自动填充:"广东省广州市越秀区解放路456号"
5. 填写其他信息并保存
验证:sql SELECT task_id, departure_address, destination_address, departure_longitude, departure_latitude, destination_longitude, destination_latitude FROM sys_task WHERE task_id = ?
预期结果:
- departure_address = "广东省广州市天河区中山大道123号"
- destination_address = "广东省广州市越秀区解放路456号"
- 如果有GPS坐标,经纬度字段有值
- 如果无GPS坐标,经纬度字段为 NULL
操作:
1. 选择的医院数据中没有地址信息
2. 保存任务
预期结果:
- departure_address = ""(空字符串)
- destination_address = ""(空字符串)
- 任务创建成功,不报错
操作:
1. 先选择医院A,地址自动填充
2. 又选择医院B,地址应该更新
3. 保存任务
预期结果:
- 保存的地址是最后选择的医院B的地址
- 不会残留医院A的地址
操作:
1. 创建任务后
2. 在任务列表查看
预期结果:
- 任务列表正确显示出发地和目的地
- GPS地图能正确定位两个位置
完整地址可能过长,可以提供简化版本:
// 提取关键信息:市 + 区 + 主要道路
function simplifyAddress(fullAddress) {
// "广东省广州市天河区中山大道123号" → "天河区中山大道"
const match = fullAddress.match(/(.+?市)?(.+?区)(.+?)(\d+号)?/)
if (match) {
return (match[2] || '') + (match[3] || '')
}
return fullAddress
}
// 在任务列表中使用简化地址
const displayAddress = simplifyAddress(task.departureAddress)
确保地址格式正确:
function validateAddress(address) {
if (!address || address.length < 5) {
return false
}
// 检查是否包含关键词
const keywords = ['省', '市', '区', '县', '路', '街', '道', '号']
return keywords.some(keyword => address.includes(keyword))
}
如果有GPS坐标,自动计算两点距离:
function calculateDistance(lat1, lng1, lat2, lng2) {
const R = 6371 // 地球半径(公里)
const dLat = (lat2 - lat1) * Math.PI / 180
const dLng = (lng2 - lng1) * Math.PI / 180
const a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
Math.sin(dLng/2) * Math.sin(dLng/2)
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a))
return R * c
}
// 自动填充转运距离
if (this.addressCoordinates.hospitalOutAddress &&
this.addressCoordinates.hospitalInAddress) {
const distance = calculateDistance(
this.addressCoordinates.hospitalOutAddress.lat,
this.addressCoordinates.hospitalOutAddress.lng,
this.addressCoordinates.hospitalInAddress.lat,
this.addressCoordinates.hospitalInAddress.lng
)
this.taskForm.transferDistance = distance.toFixed(2)
}