feat: 实现子任务deadline后端计算和存储功能

- 添加task_item表的deadline字段(数据库迁移V13)
- 在TaskItem实体类中添加deadline字段
- 更新TaskItemMapper.grabItem()方法支持deadline参数
- 在TaskItemService.grabItem()中使用WorkHoursCalculator计算deadline
- 更新前端TaskItem接口添加deadline字段
- 修改前端getSubTaskDeadline函数直接使用后端返回的deadline
This commit is contained in:
Your Name 2026-01-14 12:02:50 +08:00
parent 124e18ab78
commit 7390289f1a
8 changed files with 38 additions and 26 deletions

View File

@ -66,6 +66,11 @@ public class TaskItem implements Serializable {
*/
private BigDecimal estimatedHours;
/**
* 截止时间(接单时计算)
*/
private LocalDateTime deadline;
/**
* 接单人ID
*/

View File

@ -268,6 +268,7 @@ public interface TaskItemMapper extends BaseMapper<TaskItem> {
"receiver_name = #{receiverName}, " +
"receiver_dept_id = #{receiverDeptId}, " +
"status = 'in_progress', " +
"deadline = CASE WHEN #{deadline} IS NOT NULL THEN #{deadline} ELSE deadline END, " +
"update_time = NOW() " +
"WHERE id = #{itemId} " +
"AND status = 'pending' " +
@ -276,7 +277,8 @@ public interface TaskItemMapper extends BaseMapper<TaskItem> {
int grabItem(@Param("itemId") Long itemId,
@Param("receiverId") Long receiverId,
@Param("receiverName") String receiverName,
@Param("receiverDeptId") Long receiverDeptId);
@Param("receiverDeptId") Long receiverDeptId,
@Param("deadline") LocalDateTime deadline);
/**
* 解锁子任务

View File

@ -272,8 +272,17 @@ public class TaskItemService {
throw new BusinessException(ResultCode.TASK_ALREADY_TAKEN, "该子任务已被接单");
}
// 乐观锁抢单(同时设置接单部门ID)
int result = taskItemMapper.grabItem(itemId, userId, user.getRealName(), user.getDeptId());
// 计算deadline
LocalDateTime now = LocalDateTime.now();
LocalDateTime deadline = null;
if (item.getEstimatedHours() != null && item.getEstimatedHours().compareTo(java.math.BigDecimal.ZERO) > 0) {
deadline = workHoursCalculator.calculateDeadline(now, item.getEstimatedHours());
log.info("子任务deadline已计算:从接单时间 {} 开始,预估 {} 小时,截止时间 {}",
now, item.getEstimatedHours(), deadline);
}
// 乐观锁抢单(同时设置接单部门ID和deadline)
int result = taskItemMapper.grabItem(itemId, userId, user.getRealName(), user.getDeptId(), deadline);
if (result == 0) {
throw new BusinessException(ResultCode.TASK_ALREADY_TAKEN, "抢单失败,该子任务可能已被他人接单");
}

View File

@ -0,0 +1,5 @@
-- V13__add_task_item_deadline.sql
-- 添加子任务截止时间字段
ALTER TABLE `task_item`
ADD COLUMN `deadline` DATETIME NULL COMMENT '截止时间(接单时计算)' AFTER `estimated_hours`;

View File

@ -85,6 +85,7 @@ export interface TaskItem {
unitScore: number
totalScore: number
estimatedHours?: number
deadline?: string // 截止时间(接单时计算)
receiverId?: number
receiverName?: string
status: string // locked/pending/in_progress/completed/rejected

View File

@ -167,6 +167,7 @@ interface TaskItem {
status: string
createTime?: string
estimatedHours?: number
deadline?: string // 截止时间(接单时计算)
}
interface Task {
@ -340,18 +341,15 @@ const getSubTaskStatusClass = (status: string) => {
return classMap[status] || ''
}
// 计算子任务完成截止日期(从接单开始计算+预计完成需要的时间)
// 获取子任务完成截止日期(使用后端返回的deadline字段)
// 只有已接单或进行中的子任务才显示截止时间
const getSubTaskDeadline = (subTask: TaskItem) => {
// 只有进行中或已完成的子任务才显示截止时间
if (subTask.status !== 'in_progress' && subTask.status !== 'completed') return null
if (!subTask.createTime || !subTask.estimatedHours) return null
// 从创建时间(接单时间)开始计算
const startTime = dayjs(subTask.createTime)
// 计算截止时间:创建时间 + 预计工时
const deadline = startTime.add(subTask.estimatedHours, 'hour')
// 格式化为"XX月XX日XX时"
return deadline.format('MM月DD日HH时')
// 直接使用后端返回的deadline字段
if (!subTask.deadline) return null
// 格式化为"XX月XX日HH时"
return dayjs(subTask.deadline).format('MM月DD日HH时')
}
</script>

View File

@ -988,21 +988,16 @@ const estimatedTotalHours = computed(() => {
return totalHours
})
// 格式化时效显示
// 格式化时效显示(基于工作时间配置)
const formattedEstimatedTime = computed(() => {
const hours = estimatedTotalHours.value
if (hours === 0) return '--'
if (hours < 1) {
return `${Math.round(hours * 60)}分钟`
} else if (hours < 24) {
const h = Math.floor(hours)
const m = Math.round((hours - h) * 60)
return m > 0 ? `${h}小时${m}分钟` : `${h}小时`
} else {
const days = Math.floor(hours / 24)
const remainHours = Math.round(hours % 24)
return remainHours > 0 ? `${days}天${remainHours}小时` : `${days}天`
// 使用formatWorkHours函数来格式化(基于工作时间配置)
return formatWorkHours(hours)
}
})

View File

@ -2913,18 +2913,15 @@ const getSubTaskStatusText = (status: string | undefined) => {
return statusMap[status || ''] || status || '未知'
}
// 计算子任务完成截止日期(从接单开始计算+预计完成需要的时间)
// 获取子任务完成截止日期(使用后端返回的deadline字段)
// 只有已接单或进行中的子任务才显示截止时间
const getSubTaskDeadline = (subTask: any) => {
// 只有进行中或已完成的子任务才显示截止时间
if (subTask.status !== 'in_progress' && subTask.status !== 'completed') return null
if (!subTask.createTime || !subTask.estimatedHours) return null
// 从创建时间(接单时间)开始计算
const startTime = dayjs(subTask.createTime)
// 计算截止时间:创建时间 + 预计工时
const deadline = startTime.add(subTask.estimatedHours, 'hour')
// 直接使用后端返回的deadline字段
if (!subTask.deadline) return null
// 格式化为"XX月XX日HH时"
return deadline.format('MM月DD日HH时')
return dayjs(subTask.deadline).format('MM月DD日HH时')
}
// 展开/折叠任务子任务详情