# 转运时间写入planned_start_time功能说明
## 功能概述
在创建急救转运任务时,将用户选择的**转运时间**同时写入主任务表的 `planned_start_time` 字段(计划开始时间),确保任务调度和时间管理的准确性。
## 业务背景
急救转运任务的转运时间具有双重含义:
1. **业务层面**:表示预计何时开始转运患者(存储在扩展表的 transfer_time)
2. **系统层面**:表示任务计划开始时间(存储在主任务表的 planned_start_time)
这两个时间通常是一致的,因此需要同时写入两个表。
## 技术实现
### 前端数据提交
在 `buildSubmitData()` 方法中添加 `plannedStartTime` 字段:
```javascript
const submitData = {
taskType: 'EMERGENCY_TRANSFER',
transferTime: this.taskForm.transferTime, // 转运时间(扩展表字段)
plannedStartTime: this.taskForm.transferTime, // 计划开始时间(主任务表字段)
// ... 其他字段
}
```
### 数据库字段
**主任务表(sys_task)**:
```sql
-- 计划开始时间
planned_start_time DATETIME
```
**扩展信息表(sys_task_emergency)**:
```sql
-- 转运时间
transfer_time DATETIME
```
## 数据结构
### 前端表单数据
```javascript
taskForm: {
transferTime: "2025-01-25 14:30:00", // 用户选择的转运时间
// ... 其他字段
}
```
### 提交数据
```javascript
{
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` | 用户选择的转运时间 |
## 时间相关字段说明
### 主任务表时间字段
```java
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)
2. **actual_start_time**(实际开始时间):
- 司机点击"出发"按钮时自动记录
- 用于统计实际执行情况
- 计算任务延迟时间
3. **actual_end_time**(实际结束时间):
- 任务完成时自动记录
- 用于计算任务耗时
- 绩效考核的重要依据
## 应用场景
### 1. 任务调度
根据计划开始时间安排车辆和人员:
```sql
-- 查询即将开始的任务(未来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. 任务提醒
提前通知执行人员:
```javascript
// 在计划开始时间前30分钟发送提醒
const reminderTime = new Date(task.plannedStartTime)
reminderTime.setMinutes(reminderTime.getMinutes() - 30)
scheduleNotification({
userId: task.assigneeId,
time: reminderTime,
title: '任务即将开始',
content: `您的转运任务将在30分钟后开始,请做好准备`
})
```
### 3. 超时检测
判断任务是否延迟开始:
```javascript
// 检查任务是否超时开始
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. 任务列表显示
按计划开始时间排序显示:
```vue
{{ task.taskCode }}
计划时间:{{ formatTime(task.plannedStartTime) }}
{{ getStatusText(task) }}
```
## 数据一致性
### 双重存储说明
转运时间在两个地方存储:
1. **主任务表(sys_task.planned_start_time)**
- 用于通用任务调度
- 所有类型任务统一使用
- 便于跨任务类型统计和查询
2. **扩展表(sys_task_emergency.transfer_time)**
- 保留急救转运的业务语义
- 便于业务人员理解
- 与旧系统字段对应
### 数据同步保证
前端代码确保数据一致:
```javascript
transferTime: this.taskForm.transferTime, // 扩展表
plannedStartTime: this.taskForm.transferTime, // 主任务表
// 两者使用同一个值,确保一致
```
## 时间格式说明
### 前端时间格式
uni-datetime-picker 组件返回格式:
```javascript
// 格式:YYYY-MM-DD HH:mm:ss
transferTime: "2025-01-25 14:30:00"
```
### 后端时间格式
Java Date 类型,JSON序列化格式:
```java
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date plannedStartTime;
```
返回前端示例:
```json
{
"plannedStartTime": "2025-01-25 14:30:00"
}
```
### 数据库时间格式
MySQL DATETIME 类型:
```sql
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小时:
```javascript
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. 快捷选择
提供常用时间快捷按钮:
```vue
```
### 3. 时间冲突检测
检查是否与其他任务时间冲突:
```javascript
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 使用相同的值
- ✅ 确保主任务表和扩展表数据一致