diff --git a/backend/src/main/java/com/designhub/entity/TaskItem.java b/backend/src/main/java/com/designhub/entity/TaskItem.java index ec1365c..233d647 100644 --- a/backend/src/main/java/com/designhub/entity/TaskItem.java +++ b/backend/src/main/java/com/designhub/entity/TaskItem.java @@ -66,6 +66,11 @@ public class TaskItem implements Serializable { */ private BigDecimal estimatedHours; + /** + * 截止时间(接单时计算) + */ + private LocalDateTime deadline; + /** * 接单人ID */ diff --git a/backend/src/main/java/com/designhub/mapper/TaskItemMapper.java b/backend/src/main/java/com/designhub/mapper/TaskItemMapper.java index 6a2a616..d9de69f 100644 --- a/backend/src/main/java/com/designhub/mapper/TaskItemMapper.java +++ b/backend/src/main/java/com/designhub/mapper/TaskItemMapper.java @@ -268,6 +268,7 @@ public interface TaskItemMapper extends BaseMapper { "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 { 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); /** * 解锁子任务 diff --git a/backend/src/main/java/com/designhub/service/TaskItemService.java b/backend/src/main/java/com/designhub/service/TaskItemService.java index 101a638..2983ef4 100644 --- a/backend/src/main/java/com/designhub/service/TaskItemService.java +++ b/backend/src/main/java/com/designhub/service/TaskItemService.java @@ -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, "抢单失败,该子任务可能已被他人接单"); } diff --git a/backend/src/main/resources/db/migration/V13__add_task_item_deadline.sql b/backend/src/main/resources/db/migration/V13__add_task_item_deadline.sql new file mode 100644 index 0000000..b03a63d --- /dev/null +++ b/backend/src/main/resources/db/migration/V13__add_task_item_deadline.sql @@ -0,0 +1,5 @@ +-- V13__add_task_item_deadline.sql + +-- 添加子任务截止时间字段 +ALTER TABLE `task_item` + ADD COLUMN `deadline` DATETIME NULL COMMENT '截止时间(接单时计算)' AFTER `estimated_hours`; diff --git a/frontend/src/api/task.ts b/frontend/src/api/task.ts index 06974ce..a9e9f06 100644 --- a/frontend/src/api/task.ts +++ b/frontend/src/api/task.ts @@ -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 diff --git a/frontend/src/components/TaskCard.vue b/frontend/src/components/TaskCard.vue index dd9e112..897e986 100644 --- a/frontend/src/components/TaskCard.vue +++ b/frontend/src/components/TaskCard.vue @@ -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时') } diff --git a/frontend/src/views/CreateTask.vue b/frontend/src/views/CreateTask.vue index 3afb034..41121c5 100644 --- a/frontend/src/views/CreateTask.vue +++ b/frontend/src/views/CreateTask.vue @@ -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) } }) diff --git a/frontend/src/views/TaskHall.vue b/frontend/src/views/TaskHall.vue index c3f6886..9fda124 100644 --- a/frontend/src/views/TaskHall.vue +++ b/frontend/src/views/TaskHall.vue @@ -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时') } // 展开/折叠任务子任务详情