编辑 | blame | 历史 | 原始文档

转运时间写入planned_start_time功能说明

功能概述

在创建急救转运任务时,将用户选择的**转运时间**同时写入主任务表的 planned_start_time 字段(计划开始时间),确保任务调度和时间管理的准确性。

业务背景

急救转运任务的转运时间具有双重含义:
1. 业务层面:表示预计何时开始转运患者(存储在扩展表的 transfer_time)
2. 系统层面:表示任务计划开始时间(存储在主任务表的 planned_start_time)

这两个时间通常是一致的,因此需要同时写入两个表。

技术实现

前端数据提交

buildSubmitData() 方法中添加 plannedStartTime 字段:

const submitData = {
  taskType: 'EMERGENCY_TRANSFER',
  transferTime: this.taskForm.transferTime,      // 转运时间(扩展表字段)
  plannedStartTime: this.taskForm.transferTime,  // 计划开始时间(主任务表字段)
  // ... 其他字段
}

数据库字段

主任务表(sys_task)

-- 计划开始时间
planned_start_time DATETIME

扩展信息表(sys_task_emergency)

-- 转运时间
transfer_time DATETIME

数据结构

前端表单数据

taskForm: {
  transferTime: "2025-01-25 14:30:00", // 用户选择的转运时间
  // ... 其他字段
}

提交数据

{
  taskType: "EMERGENCY_TRANSFER",
  transferTime: "2025-01-25 14:30:00",      // 扩展表:转运时间
  plannedStartTime: "2025-01-25 14:30:00",  // 主任务表:计划开始时间
  // ... 其他字段
}

数据库存储

主任务表(sys_task)

字段 说明
task_id 10001 任务ID
planned_start_time 2025-01-25 14:30:00 计划开始时间
actual_start_time NULL 实际开始时间(任务开始后填写)

扩展信息表(sys_task_emergency)

字段 说明
emergency_id 20001 扩展信息ID
task_id 10001 关联任务ID
transfer_time 2025-01-25 14:30:00 转运时间

数据映射关系

前端字段 主任务表字段 扩展表字段 说明
taskForm.transferTime planned_start_time transfer_time 用户选择的转运时间

时间相关字段说明

主任务表时间字段

public class SysTask {
    /** 计划开始时间 */
    private Date plannedStartTime;    // 用户指定的计划开始时间
    
    /** 计划结束时间 */
    private Date plannedEndTime;      // 预计结束时间(可选)
    
    /** 实际开始时间 */
    private Date actualStartTime;     // 任务实际开始时记录
    
    /** 实际结束时间 */
    private Date actualEndTime;       // 任务实际完成时记录
}

时间字段使用场景

  1. planned_start_time(计划开始时间):
  • 任务创建时由用户指定
  • 用于任务调度和提醒
  • 判断任务是否超时(actual_start_time > planned_start_time)
  1. actual_start_time(实际开始时间):
  • 司机点击"出发"按钮时自动记录
  • 用于统计实际执行情况
  • 计算任务延迟时间
  1. actual_end_time(实际结束时间):
  • 任务完成时自动记录
  • 用于计算任务耗时
  • 绩效考核的重要依据

应用场景

1. 任务调度

根据计划开始时间安排车辆和人员:

-- 查询即将开始的任务(未来2小时内)
SELECT 
  t.task_id,
  t.task_code,
  t.planned_start_time,
  t.assignee_name,
  v.vehicle_no
FROM sys_task t
LEFT JOIN sys_task_vehicle tv ON t.task_id = tv.task_id
LEFT JOIN tb_vehicle_info v ON tv.vehicle_id = v.vehicle_id
WHERE t.task_status = 'PENDING'
  AND t.planned_start_time BETWEEN NOW() AND DATE_ADD(NOW(), INTERVAL 2 HOUR)
ORDER BY t.planned_start_time

2. 任务提醒

提前通知执行人员:

// 在计划开始时间前30分钟发送提醒
const reminderTime = new Date(task.plannedStartTime)
reminderTime.setMinutes(reminderTime.getMinutes() - 30)

scheduleNotification({
  userId: task.assigneeId,
  time: reminderTime,
  title: '任务即将开始',
  content: `您的转运任务将在30分钟后开始,请做好准备`
})

3. 超时检测

判断任务是否延迟开始:

// 检查任务是否超时开始
function isDelayedStart(task) {
  if (!task.actualStartTime || !task.plannedStartTime) {
    return false
  }
  return new Date(task.actualStartTime) > new Date(task.plannedStartTime)
}

// 计算延迟时长(分钟)
function getDelayMinutes(task) {
  if (!isDelayedStart(task)) {
    return 0
  }
  const planned = new Date(task.plannedStartTime)
  const actual = new Date(task.actualStartTime)
  return Math.floor((actual - planned) / (1000 * 60))
}

4. 任务列表显示

按计划开始时间排序显示:

<view class="task-item" v-for="task in sortedTasks" :key="task.taskId">
  <text class="task-code">{{ task.taskCode }}</text>
  <text class="task-time">计划时间:{{ formatTime(task.plannedStartTime) }}</text>
  <text class="task-status" :class="getStatusClass(task)">
    {{ getStatusText(task) }}
  </text>
</view>

<script>
export default {
  computed: {
    sortedTasks() {
      return this.tasks.sort((a, b) => {
        return new Date(a.plannedStartTime) - new Date(b.plannedStartTime)
      })
    }
  },
  methods: {
    getStatusText(task) {
      const now = new Date()
      const planned = new Date(task.plannedStartTime)
      
      if (task.taskStatus === 'PENDING') {
        if (now > planned) {
          return '已超时'
        } else if (now > new Date(planned.getTime() - 30 * 60 * 1000)) {
          return '即将开始'
        }
      }
      return task.taskStatusText
    }
  }
}
</script>

数据一致性

双重存储说明

转运时间在两个地方存储:

  1. 主任务表(sys_task.planned_start_time)
  • 用于通用任务调度
  • 所有类型任务统一使用
  • 便于跨任务类型统计和查询
  1. 扩展表(sys_task_emergency.transfer_time)
  • 保留急救转运的业务语义
  • 便于业务人员理解
  • 与旧系统字段对应

数据同步保证

前端代码确保数据一致:

transferTime: this.taskForm.transferTime,      // 扩展表
plannedStartTime: this.taskForm.transferTime,  // 主任务表
// 两者使用同一个值,确保一致

时间格式说明

前端时间格式

uni-datetime-picker 组件返回格式:

// 格式:YYYY-MM-DD HH:mm:ss
transferTime: "2025-01-25 14:30:00"

后端时间格式

Java Date 类型,JSON序列化格式:

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date plannedStartTime;

返回前端示例:
json { "plannedStartTime": "2025-01-25 14:30:00" }

数据库时间格式

MySQL DATETIME 类型:

planned_start_time DATETIME
-- 存储格式:2025-01-25 14:30:00

异常处理

1. 未选择转运时间

场景:用户未选择转运时间

处理
javascript transferTime: this.taskForm.transferTime, // 可能为空字符串或null plannedStartTime: this.taskForm.transferTime, // 同样为空

结果
- planned_start_time = NULL
- 允许后续补充
- 任务可以创建,但可能不会出现在"即将开始"列表中

2. 时间已过期

场景:用户选择的时间已经过去

前端验证(建议添加):
```javascript
validateForm() {
// ... 其他验证

if (this.taskForm.transferTime) {
const selectedTime = new Date(this.taskForm.transferTime)
const now = new Date()

if (selectedTime < now) {
  this.$modal.confirm('选择的转运时间已过期,是否继续?').then(() => {
    // 用户确认继续
  }).catch(() => {
    return false
  })
}

}

return true
}
```

3. 时间格式错误

场景:时间格式不正确

后端验证
java if (task.getPlannedStartTime() == null) { // 允许为空 } else { // 验证时间格式 try { // Spring会自动转换,这里只是示例 Date time = task.getPlannedStartTime(); } catch (Exception e) { throw new ServiceException("转运时间格式错误"); } }

测试建议

测试场景1:正常选择时间

操作
1. 选择转运时间:2025-01-25 14:30:00
2. 保存任务

验证
```sql
-- 检查主任务表
SELECT planned_start_time FROM sys_task WHERE task_id = ?
-- 预期:2025-01-25 14:30:00

-- 检查扩展表
SELECT transfer_time FROM sys_task_emergency WHERE task_id = ?
-- 预期:2025-01-25 14:30:00
```

测试场景2:未选择时间

操作
1. 不选择转运时间
2. 保存任务

验证
- planned_start_time = NULL
- transfer_time = NULL
- 任务创建成功

测试场景3:时间列表排序

操作
1. 创建多个任务,选择不同的转运时间
2. 查看任务列表

验证
- 任务按计划开始时间正确排序
- 最早的任务显示在前面

测试场景4:超时提醒

操作
1. 创建任务,转运时间设为当前时间+10分钟
2. 等待10分钟
3. 检查任务状态

验证
- 任务状态仍为"待处理"
- 但显示"已超时"标记

优化建议

1. 默认时间设置

自动设置为当前时间后1小时:

data() {
  return {
    taskForm: {
      transferTime: this.getDefaultTransferTime(),
      // ...
    }
  }
},
methods: {
  getDefaultTransferTime() {
    const now = new Date()
    now.setHours(now.getHours() + 1)
    now.setMinutes(0)
    now.setSeconds(0)
    
    // 格式化为 YYYY-MM-DD HH:mm:ss
    return this.formatDateTime(now)
  }
}

2. 快捷选择

提供常用时间快捷按钮:

<view class="quick-time-select">
  <button @click="setTransferTime(30)">30分钟后</button>
  <button @click="setTransferTime(60)">1小时后</button>
  <button @click="setTransferTime(120)">2小时后</button>
  <button @click="setTransferTime(180)">3小时后</button>
</view>

<script>
methods: {
  setTransferTime(minutes) {
    const time = new Date()
    time.setMinutes(time.getMinutes() + minutes)
    this.taskForm.transferTime = this.formatDateTime(time)
  }
}
</script>

3. 时间冲突检测

检查是否与其他任务时间冲突:

async checkTimeConflict() {
  const params = {
    vehicleId: this.selectedVehicleId,
    plannedStartTime: this.taskForm.transferTime
  }
  
  const conflicts = await checkVehicleSchedule(params)
  
  if (conflicts.length > 0) {
    this.$modal.confirm(
      `所选车辆在该时间段已有${conflicts.length}个任务,是否继续?`
    )
  }
}

版本历史

  • 2025-01-25 v1: 初始版本
  • ✅ 添加 plannedStartTime 字段
  • ✅ 与 transferTime 使用相同的值
  • ✅ 确保主任务表和扩展表数据一致