撤回链式接力: CANCELED后生成WIP子任务还给操作人+TreeCanvas CANCELED视为串行换行
This commit is contained in:
@ -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)
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
|||||||
@ -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: [] });
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user