撤回链式接力: CANCELED后生成WIP子任务还给操作人+TreeCanvas CANCELED视为串行换行

This commit is contained in:
2026-08-06 12:53:23 +08:00
parent 616638a6c8
commit e5e2a395e7
2 changed files with 32 additions and 28 deletions

View File

@ -313,43 +313,47 @@ async def end_task(
async def recall_task( async def recall_task(
db: AsyncSession, task_id: uuid.UUID, operator_id: str | None = None db: AsyncSession, task_id: uuid.UUID, operator_id: str | None = None
) -> TaskResponse: ) -> TaskResponse:
"""撤回 PENDING 转交任务:状态标记为 CANCELED,恢复父任务为 WIP。""" """撤回 PENDING 转交:标记为 CANCELED,以被撤回节点为父生成接力新任务给操作人。"""
task = await _get_task_or_404(db, task_id) task = await _get_task_or_404(db, task_id)
if task.status != TASK_STATUS_PENDING: if task.status != TASK_STATUS_PENDING:
raise HTTPException(status_code=409, detail="只有待接收(PENDING)的任务可以撤回") raise HTTPException(status_code=409, detail="只有待接收(PENDING)的任务可以撤回")
# 标记作废(不物理删除) now = get_beijing_time()
# 1. 废掉当前待接收任务
task.status = TASK_STATUS_CANCELED task.status = TASK_STATUS_CANCELED
task.completed_at = get_beijing_time() task.completed_at = now
# 写入撤回记录
await _create_task_log(db, task_id, action_type="recall", operator_id=operator_id, await _create_task_log(db, task_id, action_type="recall", operator_id=operator_id,
remark=f"撤回转交「{task.task_name}」→ {task.assignee_id},任务已作废") remark=f"撤回转交「{task.task_name}」→ {task.assignee_id}")
# 同时写入 TaskRecord 留痕 db.add(TaskRecord(task_id=task.id, remark=f"[撤回] 转交至 {task.assignee_id} 已撤回", images="[]"))
recall_record = TaskRecord(task_id=task.id, remark=f"[撤回] 转交至 {task.assignee_id} 的「{task.task_name}」已撤回", images="[]")
db.add(recall_record)
# 恢复父任务 # 2. 生成接力新任务(以撤回节点为父,还给操作人)
if task.parent_task_id: recovery = Task(
parent = await _get_task_or_404(db, task.parent_task_id) product_id=task.product_id,
if parent.status == TASK_STATUS_COMPLETED: parent_task_id=task.id,
parent.status = TASK_STATUS_WIP task_name=task.task_name,
parent.completed_at = None assignee_id=operator_id,
await _create_task_log(db, parent.id, action_type="recall", operator_id=operator_id, status=TASK_STATUS_WIP,
remark=f"撤回转交「{task.task_name}」,恢复为 WIP") notify_parent_on_complete=False,
product_result = await db.execute(select(Product).where(Product.id == parent.product_id)) is_rework=False,
product = product_result.scalar_one_or_none() remark=f"撤回「{task.task_name}」后重新接手",
if product and parent.assignee_id: )
product.current_location_id = parent.assignee_id db.add(recovery)
product.overall_status = parent.task_name await db.flush()
await _create_task_log(db, recovery.id, action_type="create", operator_id=operator_id,
remark=f"撤回接力:撤回「{task.task_name}」→ {task.assignee_id} 后重新指派给 {operator_id}")
db.add(TaskRecord(task_id=recovery.id, remark=f"[重新接手] 撤回转交后系统自动生成接力节点", images="[]"))
# 3. 更新产品位置
product_result = await db.execute(select(Product).where(Product.id == task.product_id))
product = product_result.scalar_one_or_none()
if product and operator_id:
product.current_location_id = operator_id
await db.commit() await db.commit()
await db.refresh(recovery)
if task.parent_task_id: return _to_response(recovery)
refreshed = await _get_task_or_404(db, task.parent_task_id)
return _to_response(refreshed)
return _to_response(task)
# ============================================================ # ============================================================

View File

@ -69,7 +69,7 @@ export default {
// 工业拓扑:父WIP→并行(同Y);父COMPLETED→串行(换行) // 工业拓扑:父WIP→并行(同Y);父COMPLETED→串行(换行)
// 后端保证 COMPLETED 后不可 spawn,故 COMPLETED 的子任务必为转交产物 // 后端保证 COMPLETED 后不可 spawn,故 COMPLETED 的子任务必为转交产物
const parent = this.taskDataMap[t.parent_task_id]; const parent = this.taskDataMap[t.parent_task_id];
const isSerial = !t.parent_task_id || (parent && parent.status === 'COMPLETED'); const isSerial = !t.parent_task_id || (parent && (parent.status === 'COMPLETED' || parent.status === 'CANCELED'));
const rowIdx = isSerial ? (parentRowIdx >= 0 ? parentRowIdx + 1 : rows.length) : parentRowIdx; const rowIdx = isSerial ? (parentRowIdx >= 0 ? parentRowIdx + 1 : rows.length) : parentRowIdx;
while (rows.length <= rowIdx) rows.push({ y: 0, tasks: [] }); while (rows.length <= rowIdx) rows.push({ y: 0, tasks: [] });