feat: 上游显示真实发送人 — 从task_logs追溯created_by
This commit is contained in:
@ -116,6 +116,7 @@ class TaskSummaryResponse(BaseModel):
|
|||||||
received_at: datetime | None = None
|
received_at: datetime | None = None
|
||||||
completed_at: datetime | None = None
|
completed_at: datetime | None = None
|
||||||
created_at: datetime
|
created_at: datetime
|
||||||
|
created_by: str | None = None # 谁创建的(从task_logs追溯)
|
||||||
|
|
||||||
model_config = {"from_attributes": True}
|
model_config = {"from_attributes": True}
|
||||||
|
|
||||||
@ -140,6 +141,7 @@ class TaskResponse(BaseModel):
|
|||||||
created_at: datetime
|
created_at: datetime
|
||||||
child_tasks: list[TaskResponse] = []
|
child_tasks: list[TaskResponse] = []
|
||||||
records: list[TaskRecordResponse] = []
|
records: list[TaskRecordResponse] = []
|
||||||
|
created_by: str | None = None # 谁创建的(从task_logs追溯)
|
||||||
|
|
||||||
model_config = {"from_attributes": True}
|
model_config = {"from_attributes": True}
|
||||||
|
|
||||||
|
|||||||
@ -42,6 +42,7 @@ def _task_to_response(task: Task) -> TaskResponse:
|
|||||||
created_at=task.created_at,
|
created_at=task.created_at,
|
||||||
child_tasks=[_task_to_response(c) for c in task.child_tasks],
|
child_tasks=[_task_to_response(c) for c in task.child_tasks],
|
||||||
records=[TaskRecordResponse.model_validate(r) for r in (task.records or [])],
|
records=[TaskRecordResponse.model_validate(r) for r in (task.records or [])],
|
||||||
|
created_by=getattr(task, "created_by", None),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -85,15 +86,54 @@ async def get_product_by_serial(db: AsyncSession, serial_number: str) -> Product
|
|||||||
|
|
||||||
# 🔧 收集任务树中所有 assignee_id → 查中文姓名映射
|
# 🔧 收集任务树中所有 assignee_id → 查中文姓名映射
|
||||||
assignee_ids: set[str] = set()
|
assignee_ids: set[str] = set()
|
||||||
|
all_task_ids: list[uuid.UUID] = []
|
||||||
def _collect_ids(tasks):
|
def _collect_ids(tasks):
|
||||||
for t in tasks:
|
for t in tasks:
|
||||||
|
all_task_ids.append(t.id)
|
||||||
if t.assignee_id: assignee_ids.add(t.assignee_id)
|
if t.assignee_id: assignee_ids.add(t.assignee_id)
|
||||||
if t.child_tasks: _collect_ids(t.child_tasks)
|
if t.child_tasks: _collect_ids(t.child_tasks)
|
||||||
for t in top_tasks:
|
for t in top_tasks:
|
||||||
|
all_task_ids.append(t.id)
|
||||||
if t.assignee_id: assignee_ids.add(t.assignee_id)
|
if t.assignee_id: assignee_ids.add(t.assignee_id)
|
||||||
_collect_ids(task_tree)
|
_collect_ids(task_tree)
|
||||||
assignee_names = _lookup_display_names(list(assignee_ids))
|
assignee_names = _lookup_display_names(list(assignee_ids))
|
||||||
|
|
||||||
|
# 🔧 批量查 task_logs: 谁创建了每个任务
|
||||||
|
creator_map: dict[uuid.UUID, str] = {}
|
||||||
|
if all_task_ids:
|
||||||
|
from app.models.task_log import TaskLog
|
||||||
|
# 每个 task 取最早的 create 日志的 operator_id
|
||||||
|
sub = (
|
||||||
|
select(
|
||||||
|
TaskLog.task_id,
|
||||||
|
TaskLog.operator_id,
|
||||||
|
func.row_number().over(
|
||||||
|
partition_by=TaskLog.task_id,
|
||||||
|
order_by=TaskLog.created_at.asc()
|
||||||
|
).label("rn")
|
||||||
|
)
|
||||||
|
.where(
|
||||||
|
TaskLog.task_id.in_(all_task_ids),
|
||||||
|
TaskLog.action_type == "create"
|
||||||
|
)
|
||||||
|
).subquery()
|
||||||
|
log_result = await db.execute(
|
||||||
|
select(sub.c.task_id, sub.c.operator_id).where(sub.c.rn == 1)
|
||||||
|
)
|
||||||
|
for row in log_result:
|
||||||
|
if row[1]:
|
||||||
|
creator_map[row[0]] = row[1]
|
||||||
|
|
||||||
|
# 注入 created_by 到 task_tree 和 top_tasks
|
||||||
|
def _inject_creator(tasks):
|
||||||
|
for t in tasks:
|
||||||
|
t.created_by = creator_map.get(t.id)
|
||||||
|
if t.child_tasks:
|
||||||
|
_inject_creator(t.child_tasks)
|
||||||
|
_inject_creator(task_tree)
|
||||||
|
for t in top_tasks:
|
||||||
|
t.created_by = creator_map.get(t.id)
|
||||||
|
|
||||||
return ProductScanResponse(
|
return ProductScanResponse(
|
||||||
id=product.id,
|
id=product.id,
|
||||||
serial_number=product.serial_number,
|
serial_number=product.serial_number,
|
||||||
|
|||||||
@ -61,7 +61,7 @@
|
|||||||
|
|
||||||
<view v-if="lockedTask.parent_task_id && parentTaskOf(lockedTask)" class="fc-link fc-up">
|
<view v-if="lockedTask.parent_task_id && parentTaskOf(lockedTask)" class="fc-link fc-up">
|
||||||
<text class="fc-link-label">{{ isNestedSpawn ? '⬆ 上游协助 (嵌套)' : '⬆ 上游工序' }}</text>
|
<text class="fc-link-label">{{ isNestedSpawn ? '⬆ 上游协助 (嵌套)' : '⬆ 上游工序' }}</text>
|
||||||
<text class="fc-link-name">{{ parentTaskOf(lockedTask).task_name }} → {{ formatUserName(parentTaskOf(lockedTask).assignee_id) || '—' }}</text>
|
<text class="fc-link-name">{{ parentTaskOf(lockedTask).task_name }} → {{ formatUserName(lockedTask.created_by) || formatUserName(parentTaskOf(lockedTask).assignee_id) || '—' }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view v-if="lockedTask.child_tasks && lockedTask.child_tasks.length" class="fc-link fc-down">
|
<view v-if="lockedTask.child_tasks && lockedTask.child_tasks.length" class="fc-link fc-down">
|
||||||
<text class="fc-link-label">⬇ 下游分支 ({{ lockedTask.child_tasks.length }})</text>
|
<text class="fc-link-label">⬇ 下游分支 ({{ lockedTask.child_tasks.length }})</text>
|
||||||
|
|||||||
Reference in New Issue
Block a user