feat: 复制/周期任务时复制子任务并重置状态

- 复制任务时同时复制子任务,子任务状态重置为未完成
  - 周期任务生成时,子任务状态重置为未完成并映射到 start 工作流
  - 新增 getProjectFlowItems 方法获取项目工作流状态
  - 新增 formatFlowItemName 方法格式化工作流状态名称
  - 新增 copySubTasks 方法复制子任务到新父任务
  - 新增 moveSubTasks 方法移动子任务,重构 moveTask 复用代码
This commit is contained in:
kuaifan 2026-01-14 11:31:28 +00:00
parent 224703a6d0
commit c2c27a684b
3 changed files with 130 additions and 54 deletions

View File

@ -3051,6 +3051,11 @@ class ProjectController extends AbstractController
$copy->addLog('复制{任务}', [
'copy_from' => $task->id,
]);
// 复制子任务
$task->copySubTasks($copy, [
'reset_complete' => true,
'update_project' => true,
]);
return $copy;
});
//

View File

@ -1228,6 +1228,126 @@ class ProjectTask extends AbstractModel
});
}
/**
* 获取项目的工作流状态项start end
* @param int $projectId 项目ID
* @return array ['start' => ProjectFlowItem|null, 'end' => ProjectFlowItem|null]
*/
public static function getProjectFlowItems(int $projectId): array
{
$startFlowItem = null;
$endFlowItem = null;
$projectFlow = ProjectFlow::whereProjectId($projectId)->orderByDesc('id')->first();
if ($projectFlow) {
$flowItems = ProjectFlowItem::whereFlowId($projectFlow->id)->orderBy('sort')->get();
foreach ($flowItems as $item) {
if ($item->status == 'start' && !$startFlowItem) {
$startFlowItem = $item;
}
if ($item->status == 'end' && !$endFlowItem) {
$endFlowItem = $item;
}
}
}
return ['start' => $startFlowItem, 'end' => $endFlowItem];
}
/**
* 生成工作流状态名称
* @param ProjectFlowItem|null $flowItem
* @return string
*/
public static function formatFlowItemName(?ProjectFlowItem $flowItem): string
{
return $flowItem ? ($flowItem->status . '|' . $flowItem->name . '|' . $flowItem->color) : '';
}
/**
* 复制子任务到新的父任务
* @param ProjectTask $newParentTask 新的父任务
* @param array $options 选项
* - reset_complete: 是否重置完成状态并映射到 start 工作流(默认 true
* - sync_time: 是否同步时间到父任务的时间(默认 false
* - update_project: 是否更新项目相关字段project_id、column_id默认 false
* @return array 新创建的子任务数组
*/
public function copySubTasks(ProjectTask $newParentTask, array $options = []): array
{
$resetComplete = $options['reset_complete'] ?? true;
$syncTime = $options['sync_time'] ?? false;
$updateProject = $options['update_project'] ?? false;
$newSubTasks = [];
$subTasks = self::whereParentId($this->id)->get();
if ($subTasks->isEmpty()) {
return $newSubTasks;
}
// 获取 start 工作流状态
$flowItems = $resetComplete ? self::getProjectFlowItems($newParentTask->project_id) : ['start' => null];
$startFlowItem = $flowItems['start'];
foreach ($subTasks as $subTask) {
$newSubTask = $subTask->copyTask();
$newSubTask->parent_id = $newParentTask->id;
// 同步时间
if ($syncTime) {
$newSubTask->start_at = $newParentTask->start_at;
$newSubTask->end_at = $newParentTask->end_at;
}
// 更新项目相关字段
if ($updateProject) {
$newSubTask->project_id = $newParentTask->project_id;
$newSubTask->column_id = $newParentTask->column_id;
}
// 重置完成状态
if ($resetComplete) {
$newSubTask->complete_at = null;
$newSubTask->flow_item_id = $startFlowItem?->id ?? 0;
$newSubTask->flow_item_name = self::formatFlowItemName($startFlowItem);
}
$newSubTask->save();
$newSubTasks[] = $newSubTask;
}
return $newSubTasks;
}
/**
* 移动子任务到新项目/
* @param int $projectId 目标项目ID
* @param int $columnId 目标列ID
*/
public function moveSubTasks(int $projectId, int $columnId): void
{
$subTasks = self::whereParentId($this->id)->get();
if ($subTasks->isEmpty()) {
return;
}
$flowItems = self::getProjectFlowItems($projectId);
$startFlowItem = $flowItems['start'];
$endFlowItem = $flowItems['end'];
foreach ($subTasks as $subTask) {
$subTask->project_id = $projectId;
$subTask->column_id = $columnId;
// 根据完成状态映射工作流
if ($subTask->complete_at) {
$subTask->flow_item_id = $endFlowItem?->id ?? 0;
$subTask->flow_item_name = self::formatFlowItemName($endFlowItem);
} else {
$subTask->flow_item_id = $startFlowItem?->id ?? 0;
$subTask->flow_item_name = self::formatFlowItemName($startFlowItem);
}
$subTask->save();
}
}
/**
* 同步项目成员至聊天室
*/
@ -1961,50 +2081,7 @@ class ProjectTask extends AbstractModel
}
}
// 子任务 - 根据完成状态映射工作流
$subTasks = ProjectTask::whereParentId($this->id)->get();
if ($subTasks->isNotEmpty()) {
// 获取新项目的工作流状态
$newProjectFlow = ProjectFlow::whereProjectId($projectId)->orderByDesc('id')->first();
$startFlowItem = null;
$endFlowItem = null;
if ($newProjectFlow) {
$flowItems = ProjectFlowItem::whereFlowId($newProjectFlow->id)->orderBy('sort')->get();
foreach ($flowItems as $item) {
if ($item->status == 'start' && !$startFlowItem) {
$startFlowItem = $item;
}
if ($item->status == 'end' && !$endFlowItem) {
$endFlowItem = $item;
}
}
}
// 更新每个子任务
foreach ($subTasks as $subTask) {
$subTask->project_id = $projectId;
$subTask->column_id = $columnId;
// 根据完成状态映射工作流
if ($subTask->complete_at) {
// 已完成 -> end 状态
if ($endFlowItem) {
$subTask->flow_item_id = $endFlowItem->id;
$subTask->flow_item_name = $endFlowItem->status . '|' . $endFlowItem->name . '|' . $endFlowItem->color;
} else {
$subTask->flow_item_id = 0;
$subTask->flow_item_name = '';
}
} else {
// 未完成 -> start 状态
if ($startFlowItem) {
$subTask->flow_item_id = $startFlowItem->id;
$subTask->flow_item_name = $startFlowItem->status . '|' . $startFlowItem->name . '|' . $startFlowItem->color;
} else {
$subTask->flow_item_id = 0;
$subTask->flow_item_name = '';
}
}
$subTask->save();
}
}
$this->moveSubTasks($projectId, $columnId);
//
if ($flowItemId) {
// 更新任务流程

View File

@ -65,16 +65,10 @@ class LoopTask extends AbstractTask
$task->end_at = $task->start_at->clone()->addSeconds($diffSecond);
}
// 处理子任务
$subTasks = ProjectTask::whereParentId($item->id)->get();
if (!$subTasks->isEmpty()) {
foreach ($subTasks as $subTask) {
$newSubTask = $subTask->copyTask();
$newSubTask->parent_id = $task->id;
$newSubTask->start_at = $task->start_at;
$newSubTask->end_at = $task->end_at;
$newSubTask->save();
}
}
$item->copySubTasks($task, [
'reset_complete' => true,
'sync_time' => true,
]);
//
$task->refreshLoop(true);
$task->addLog("创建任务来自周期任务ID{$item->id}", [], $task->userid);