This commit is contained in:
zlwu committed 2026-06-25 12:41:07 +08:00
1 parent 66609c0e21
commit 0b50eff3d8
93 files changed
+40172 -30060

No files matched your search

+1 -1
View File
@@ -1 +1 @@
32839
78910
+7 -3
View File
@@ -429,7 +429,7 @@ class StorageFacilityCreateBody(BaseModel):
parent_id: str = Field("0", alias="parentId")
node_name: str = Field(..., alias="nodeName")
node_code: str = Field(..., alias="nodeCode", min_length=1, max_length=32)
node_code: str = Field("", alias="nodeCode", max_length=32)
node_type: Optional[str] = Field(None, alias="nodeType")
is_leaf: Optional[int] = Field(0, alias="isLeaf")
order_num: Optional[int] = Field(0, alias="orderNum")
@@ -440,7 +440,7 @@ class StorageFacilityUpdateBody(BaseModel):
model_config = ConfigDict(populate_by_name=True)
node_name: Optional[str] = Field(None, alias="nodeName")
node_code: Optional[str] = Field(None, alias="nodeCode", min_length=1, max_length=32)
node_code: Optional[str] = Field(None, alias="nodeCode", max_length=32)
node_type: Optional[str] = Field(None, alias="nodeType")
is_leaf: Optional[int] = Field(None, alias="isLeaf")
order_num: Optional[int] = Field(None, alias="orderNum")
@@ -553,6 +553,7 @@ class StorageSlotCreateBody(BaseModel):
slot_code: Optional[str] = Field(None, alias="slotCode")
slot_status: Optional[str] = Field(None, alias="slotStatus")
order_num: Optional[int] = Field(0, alias="orderNum")
at_row_num: Optional[int] = Field(None, alias="atRowNum", ge=1)
remark: Optional[str] = None
@@ -692,6 +693,7 @@ async def create_storage_slot(
order_num=body.order_num,
remark=body.remark,
user_id=user.get("user_id"),
at_row_num=body.at_row_num,
)
return success_response(storage_facility_svc._slot_to_dict_for_row(db, obj), message="创建成功")
except ValueError as e:
@@ -774,7 +776,9 @@ async def delete_storage_slot(
):
try:
tenant_no = _get_tenant_no(db, user)
storage_facility_svc.soft_delete_slot(db, slot_id, tenant_no=tenant_no)
storage_facility_svc.soft_delete_slot(
db, slot_id, tenant_no=tenant_no, user_id=user.get("user_id")
)
return success_response(None, message="删除成功")
except ValueError as e:
return error_response(400, str(e))
+70 -4
View File
@@ -55,6 +55,7 @@ from app.schemas.production_task import (
ProductionTaskStatusUpdate,
ProductionTaskForwardBody,
ProductionTaskAssigneeUpdate,
ProductionTaskStepDispatchBody,
TaskStepAdd,
TaskStepUpdate,
TaskStepMaterialItem,
@@ -102,11 +103,35 @@ def _assert_production_task_write_access(
if scope == "mine":
if role != SUPER_ADMIN_ROLE and "production:my-task:list" not in codes:
raise PermissionError("无菜单访问权限")
production_task_svc.assert_task_assignee(db, task_id, user_id, tenant_no)
production_task_svc.assert_task_step_assignee_or_legacy(
db, task_id, user_id, tenant_no
)
elif role != SUPER_ADMIN_ROLE and "production:work-order:list" not in codes:
raise PermissionError("无菜单访问权限")
def _assert_production_task_step_write_access(
db: Session,
user: dict,
step_id: str,
tenant_no: str,
operate_scope: Optional[str],
) -> None:
scope = (operate_scope or "manage").strip().lower()
user_id = _get_current_user_id(user)
role = user.get("role")
codes = get_user_menu_codes(db, user_id) if role != SUPER_ADMIN_ROLE else set()
manage = scope != "mine"
if not manage:
if role != SUPER_ADMIN_ROLE and "production:my-task:list" not in codes:
raise PermissionError("无菜单访问权限")
elif role != SUPER_ADMIN_ROLE and "production:work-order:list" not in codes:
raise PermissionError("无菜单访问权限")
production_task_svc.assert_step_assignee_for_write(
db, step_id, user_id, tenant_no, manage=manage
)
# ---------------------------------------------------------------------------
# 1. 耗材
# ---------------------------------------------------------------------------
@@ -1840,7 +1865,7 @@ async def list_production_work_order_cell_product_options(
):
try:
tenant_no = _get_tenant_no(db, user)
items = qc_work_order_svc.list_cell_product_options_for_production_work_order(
items = qc_work_order_svc.list_step_output_dispatch_options_for_production_work_order(
db, production_work_order_id=wo_id, tenant_no=tenant_no
)
return success_response(items)
@@ -1860,8 +1885,7 @@ async def create_qc_work_order_from_production_api(
wo = qc_work_order_svc.create_qc_work_order_from_production(
db,
production_work_order_id=wo_id,
cell_product_id=body.cell_product_id,
production_task_id=body.production_task_id,
production_step_output_id=body.production_step_output_id,
remark=body.remark or "",
tenant_no=tenant_no,
user_id=user.get("user_id"),
@@ -2026,6 +2050,40 @@ async def reassign_production_task_api(
return error_response(400, str(e))
@router.put("/production-tasks/{task_id}/steps/dispatch")
async def dispatch_production_task_steps_api(
task_id: str,
body: ProductionTaskStepDispatchBody,
operateScope: Optional[str] = Query(None, alias="operateScope"),
user: dict = Depends(require_any_menu_code("production:my-task:list", "production:work-order:list")),
db: Session = Depends(get_db),
):
try:
tenant_no = _get_tenant_no(db, user)
_assert_production_task_write_access(db, user, task_id, tenant_no, operateScope)
payload = [
{
"step_id": it.step_id,
"assignee_user_ids": it.assignee_user_ids,
"planned_start_time": it.planned_start_time,
}
for it in body.steps
]
detail = production_task_svc.dispatch_task_steps(
db,
task_id,
tenant_no=tenant_no,
steps_payload=payload,
operator_id=user.get("user_id"),
create_dept_id=user.get("dept_id", ""),
)
db.commit()
return success_response(detail, message="步骤派发成功")
except ValueError as e:
db.rollback()
return error_response(400, str(e))
@router.get("/production-tasks/{task_id}")
async def get_production_task_detail(
task_id: str,
@@ -2132,6 +2190,7 @@ async def update_production_task_step(
try:
tenant_no = _get_tenant_no(db, user)
_assert_production_task_write_access(db, user, task_id, tenant_no, operateScope)
_assert_production_task_step_write_access(db, user, step_id, tenant_no, operateScope)
data = body.model_dump(exclude_unset=True, by_alias=False)
step = production_task_svc.update_task_step(
db, step_id, tenant_no, user.get("user_id"), data,
@@ -2154,6 +2213,7 @@ async def delete_production_task_step(
try:
tenant_no = _get_tenant_no(db, user)
_assert_production_task_write_access(db, user, task_id, tenant_no, operateScope)
_assert_production_task_step_write_access(db, user, step_id, tenant_no, operateScope)
production_task_svc.delete_task_step(db, step_id, tenant_no, user.get("user_id"))
return success_response(None, message="删除成功")
except PermissionError as e:
@@ -2191,6 +2251,7 @@ async def copy_production_task_step(
try:
tenant_no = _get_tenant_no(db, user)
_assert_production_task_write_access(db, user, task_id, tenant_no, operateScope)
_assert_production_task_step_write_access(db, user, step_id, tenant_no, operateScope)
step_data = production_task_svc.copy_task_step(
db,
step_id=step_id,
@@ -2216,6 +2277,7 @@ async def replace_task_step_reagents(
try:
tenant_no = _get_tenant_no(db, user)
_assert_production_task_write_access(db, user, task_id, tenant_no, operateScope)
_assert_production_task_step_write_access(db, user, step_id, tenant_no, operateScope)
items_data = []
for it in body:
d = it.model_dump(by_alias=False)
@@ -2250,6 +2312,7 @@ async def replace_task_step_consumables(
try:
tenant_no = _get_tenant_no(db, user)
_assert_production_task_write_access(db, user, task_id, tenant_no, operateScope)
_assert_production_task_step_write_access(db, user, step_id, tenant_no, operateScope)
items_data = []
for it in body:
d = it.model_dump(by_alias=False)
@@ -2284,6 +2347,7 @@ async def replace_task_step_configured_reagents(
try:
tenant_no = _get_tenant_no(db, user)
_assert_production_task_write_access(db, user, task_id, tenant_no, operateScope)
_assert_production_task_step_write_access(db, user, step_id, tenant_no, operateScope)
items_data = []
for it in body:
d = it.model_dump(by_alias=False)
@@ -2319,6 +2383,7 @@ async def replace_task_step_data_collections(
try:
tenant_no = _get_tenant_no(db, user)
_assert_production_task_write_access(db, user, task_id, tenant_no, operateScope)
_assert_production_task_step_write_access(db, user, step_id, tenant_no, operateScope)
items_data = [it.model_dump(by_alias=False) for it in body]
production_task_svc.replace_step_data_collections(db, step_id, tenant_no, user.get("user_id"), items_data)
return success_response(None, message="更新成功")
@@ -2340,6 +2405,7 @@ async def replace_task_step_outputs(
try:
tenant_no = _get_tenant_no(db, user)
_assert_production_task_write_access(db, user, task_id, tenant_no, operateScope)
_assert_production_task_step_write_access(db, user, step_id, tenant_no, operateScope)
items_data = [it.model_dump(by_alias=False) for it in body]
production_task_svc.replace_step_outputs(db, step_id, tenant_no, user.get("user_id"), items_data)
return success_response(None, message="更新成功")
+13 -1
View File
@@ -12,6 +12,7 @@ from app.core.utils import generate_uuid
from app.crud.equipment_snapshot import parse_equipment_ids, resolve_equipment_list_snapshot
from app.models.models import (
BizProcess,
BizProcessCategory,
BizProcessConsumable,
BizProcessDataCollection,
BizProcessOutput,
@@ -124,7 +125,18 @@ def list_processes(
page_size = min(max(1, page_size), 100)
rows = (
_filtered()
.order_by(BizProcess.create_time.desc())
.outerjoin(
BizProcessCategory,
and_(
BizProcess.category_id == BizProcessCategory.id,
BizProcessCategory.del_flag == 0,
),
)
.order_by(
BizProcessCategory.category_name.is_(None),
BizProcessCategory.category_name.asc(),
BizProcess.create_time.desc(),
)
.offset((page - 1) * page_size)
.limit(page_size)
.all()
+10 -1
View File
@@ -11,6 +11,7 @@ from sqlalchemy.orm import Session
from app.core.utils import generate_uuid
from app.crud.equipment_snapshot import parse_equipment_ids, resolve_equipment_list_snapshot
from app.models.models import (
BizCellProductType,
BizProcess,
BizProcessReagent,
BizProcessConsumable,
@@ -145,8 +146,16 @@ def list_routes(db: Session, *, page: int = 1, page_size: int = 10,
page_size = min(max(1, page_size), 100)
rows = (
_filtered()
.outerjoin(
BizCellProductType,
and_(
BizProcessRoute.product_type_id == BizCellProductType.id,
BizCellProductType.del_flag == 0,
),
)
.order_by(
func.coalesce(BizProcessRoute.order_num, 999999).asc(),
BizCellProductType.product_type_name.is_(None),
BizCellProductType.product_type_name.asc(),
BizProcessRoute.create_time.desc(),
)
.offset((page - 1) * page_size)
+161 -9
View File
@@ -29,6 +29,7 @@ from app.models.models import (
BizProductionTaskInput,
BizProductionTaskOutput,
BizProductionTaskStep,
BizProductionTaskStepAssignee,
BizProductionTaskStepConsumable,
BizProductionTaskStepConfiguredReagent,
BizProductionTaskStepDataCollection,
@@ -305,8 +306,14 @@ def _step_to_dict(s: BizProductionTaskStep) -> Dict[str, Any]:
"actualDuration": s.actual_duration or "",
"sopIds": s.sop_ids or "",
"stepStatus": s.step_status or "dzx",
"plannedStartTime": _fmt_dt(getattr(s, "planned_start_time", None)),
"startedTime": _fmt_dt(s.started_time),
"completedTime": _fmt_dt(s.completed_time),
"completedBy": getattr(s, "completed_by", None) or "",
"completedByName": "",
"assigneeUserIds": [],
"assigneeNames": [],
"assignees": [],
"remark": s.remark or "",
}
@@ -684,12 +691,54 @@ def get_production_task_row(
def assert_task_assignee(
db: Session, task_id: str, user_id: str, tenant_no: str
) -> BizProductionTask:
"""Deprecated:优先使用 assert_task_step_assignee_or_legacy。"""
return assert_task_step_assignee_or_legacy(db, task_id, user_id, tenant_no)
def assert_task_step_assignee_or_legacy(
db: Session, task_id: str, user_id: str, tenant_no: str
) -> BizProductionTask:
from app.crud import production_task_step_assignee as step_assignee_svc
task = get_production_task_row(db, task_id, tenant_no)
if not task:
raise ValueError("任务不存在")
if (task.current_assignee_user_id or "") != (user_id or ""):
raise PermissionError("非当前任务负责人,无权操作")
return task
if step_assignee_svc.user_is_any_step_assignee_of_task(db, task_id, user_id, tenant_no):
return task
if (task.current_assignee_user_id or "") == (user_id or ""):
return task
raise PermissionError("非任务步骤负责人,无权操作")
def assert_step_assignee_for_write(
db: Session,
step_id: str,
user_id: str,
tenant_no: str,
*,
manage: bool = False,
) -> BizProductionTaskStep:
if manage:
step = db.query(BizProductionTaskStep).filter(
BizProductionTaskStep.id == step_id,
BizProductionTaskStep.tenant_no == tenant_no,
BizProductionTaskStep.del_flag == 0,
).first()
if not step:
raise ValueError("步骤不存在")
return step
from app.crud import production_task_step_assignee as step_assignee_svc
step = db.query(BizProductionTaskStep).filter(
BizProductionTaskStep.id == step_id,
BizProductionTaskStep.tenant_no == tenant_no,
BizProductionTaskStep.del_flag == 0,
).first()
if not step:
raise ValueError("步骤不存在")
if not step_assignee_svc.user_is_step_assignee(db, step_id, user_id, tenant_no):
raise PermissionError("非当前步骤负责人,无权操作")
return step
def _record_task_handover(
@@ -842,7 +891,9 @@ def create_production_task(
task_type = "outbound"
assignee_raw = (data.get("assignee_user_id") or data.get("assigneeUserId") or "").strip()
assignee_user_id = _validate_assignee_user_id(db, assignee_raw, tenant_no)
assignee_user_id = None
if assignee_raw:
assignee_user_id = _validate_assignee_user_id(db, assignee_raw, tenant_no)
task_no = _generate_task_no(db, tenant_no)
now = datetime.now()
@@ -891,6 +942,71 @@ def create_production_task(
return task
def dispatch_task_steps(
db: Session,
task_id: str,
*,
tenant_no: str,
steps_payload: List[Dict[str, Any]],
operator_id: Optional[str] = None,
create_dept_id: str = "",
) -> Dict[str, Any]:
from app.crud import production_task_step_assignee as step_assignee_svc
task = get_production_task_row(db, task_id, tenant_no)
if not task:
raise ValueError("任务不存在")
if not steps_payload:
raise ValueError("请至少指定一个步骤的负责人")
step_rows = {
s.id: s
for s in db.query(BizProductionTaskStep).filter(
BizProductionTaskStep.task_id == task_id,
BizProductionTaskStep.tenant_no == tenant_no,
BizProductionTaskStep.del_flag == 0,
).all()
}
for item in steps_payload:
sid = (item.get("step_id") or item.get("stepId") or "").strip()
if sid not in step_rows:
raise ValueError(f"步骤不存在或不属于该任务: {sid}")
user_ids = item.get("assignee_user_ids") or item.get("assigneeUserIds") or []
step_assignee_svc.sync_assignees_for_step(
db,
sid,
user_ids,
tenant_no=tenant_no,
operator_id=operator_id,
create_dept_id=create_dept_id,
)
step = step_rows[sid]
if "planned_start_time" in item or "plannedStartTime" in item:
pst = item.get("planned_start_time")
if pst is None and "plannedStartTime" in item:
pst = item.get("plannedStartTime")
if pst is None or pst == "":
step.planned_start_time = None
else:
if isinstance(pst, datetime):
step.planned_start_time = pst
else:
parsed = parse_datetime(str(pst).strip())
if parsed is None:
raise ValueError("计划开始时间格式无效")
step.planned_start_time = parsed
step.update_by = operator_id
step.update_time = datetime.now()
db.add(step)
db.flush()
detail = get_production_task_detail(db, task_id, tenant_no)
if not detail:
raise ValueError("任务不存在")
return detail
def list_production_tasks(
db: Session, *, tenant_no: str,
page: int = 1, page_size: int = 10,
@@ -901,6 +1017,7 @@ def list_production_tasks(
started_time_start: Optional[str] = None,
started_time_end: Optional[str] = None,
current_assignee_user_id: Optional[str] = None,
step_assignee_user_id: Optional[str] = None,
mine_history_user_id: Optional[str] = None,
work_order_type: Optional[str] = None,
reception_source: Optional[str] = None,
@@ -954,6 +1071,29 @@ def list_production_tasks(
ca_uid = (current_assignee_user_id or "").strip()
if ca_uid:
q = q.filter(BizProductionTask.current_assignee_user_id == ca_uid)
sa_uid = (step_assignee_user_id or "").strip()
if sa_uid:
step_task_ids = (
db.query(BizProductionTaskStep.task_id)
.join(
BizProductionTaskStepAssignee,
BizProductionTaskStepAssignee.step_id == BizProductionTaskStep.id,
)
.filter(
BizProductionTaskStepAssignee.user_id == sa_uid,
BizProductionTaskStepAssignee.tenant_no == tenant_no,
BizProductionTaskStepAssignee.del_flag == 0,
BizProductionTaskStep.del_flag == 0,
)
.distinct()
.subquery()
)
q = q.filter(
or_(
BizProductionTask.id.in_(step_task_ids),
BizProductionTask.current_assignee_user_id == sa_uid,
)
)
mh_uid = (mine_history_user_id or "").strip()
if mh_uid:
handover_task_ids = (
@@ -1252,7 +1392,7 @@ def list_mine_production_tasks(
page_size=page_size,
keyword=keyword,
task_status=task_status,
current_assignee_user_id=uid,
step_assignee_user_id=uid,
work_order_type=work_order_type,
reception_source=reception_source,
biz_type=biz_type,
@@ -1350,6 +1490,11 @@ def get_production_task_detail(db: Session, task_id: str, tenant_no: str) -> Opt
sd["dataCollections"] = [_step_data_collection_to_dict(d) for d in data_collections]
step_list.append(sd)
from app.crud.production_task_step_assignee import enrich_step_assignees, enrich_step_completed_by_names
enrich_step_assignees(db, step_list, tenant_no=tenant_no)
enrich_step_completed_by_names(db, step_list)
inputs = db.query(BizProductionTaskInput).filter(
and_(BizProductionTaskInput.task_id == task_id, BizProductionTaskInput.del_flag == 0)
).order_by(BizProductionTaskInput.order_num.asc(), BizProductionTaskInput.create_time.asc()).all()
@@ -1729,11 +1874,18 @@ def update_task_step(
step.equipment_ids = eids
step.equipment_name = enames
if data.get("started_time"):
from dateutil.parser import parse as _parse_dt
step.started_time = _parse_dt(data["started_time"])
parsed = parse_datetime(str(data["started_time"]).strip())
if parsed is None:
raise ValueError("开始时间格式无效")
step.started_time = parsed
if data.get("completed_time"):
from dateutil.parser import parse as _parse_dt
step.completed_time = _parse_dt(data["completed_time"])
parsed = parse_datetime(str(data["completed_time"]).strip())
if parsed is None:
raise ValueError("完成时间格式无效")
step.completed_time = parsed
new_status = (data.get("step_status") or "").strip()
if new_status == "ywc" and user_id:
step.completed_by = user_id
step.update_by = user_id
step.update_time = datetime.now()
db.add(step)
@@ -0,0 +1,209 @@
"""生产任务步骤负责人 CRUD"""
from __future__ import annotations
from datetime import datetime
from typing import Dict, List, Optional, Set
from sqlalchemy.orm import Session
from app.core.db_collation import eq_unicode
from app.core.utils import generate_uuid
from app.models.models import BizProductionTaskStep, BizProductionTaskStepAssignee, SysUser
def _active_users_in_tenant(
db: Session, user_ids: List[str], tenant_no: str
) -> Set[str]:
ids = [x.strip() for x in user_ids if (x or "").strip()]
if not ids:
return set()
rows = (
db.query(SysUser.id)
.filter(
SysUser.id.in_(ids),
SysUser.tenant_no == tenant_no,
SysUser.del_flag == 0,
SysUser.status == "1",
)
.all()
)
return {r[0] for r in rows}
def validate_assignee_user_ids(
db: Session, user_ids: List[str], tenant_no: str
) -> List[str]:
"""去重并校验用户有效,返回有序列表。"""
seen: Set[str] = set()
ordered: List[str] = []
for uid in user_ids:
u = (uid or "").strip()
if not u or u in seen:
continue
seen.add(u)
ordered.append(u)
if not ordered:
raise ValueError("请至少指定一名负责人")
valid = _active_users_in_tenant(db, ordered, tenant_no)
invalid = [u for u in ordered if u not in valid]
if invalid:
raise ValueError("存在无效或已停用的负责人用户")
return ordered
def sync_assignees_for_step(
db: Session,
step_id: str,
user_ids: List[str],
*,
tenant_no: str,
operator_id: Optional[str] = None,
create_dept_id: str = "",
) -> None:
"""全量替换步骤负责人(软删旧记录 + 插入新记录)。"""
normalized = validate_assignee_user_ids(db, user_ids, tenant_no)
now = datetime.now()
existing = (
db.query(BizProductionTaskStepAssignee)
.filter(
BizProductionTaskStepAssignee.step_id == step_id,
BizProductionTaskStepAssignee.tenant_no == tenant_no,
BizProductionTaskStepAssignee.del_flag == 0,
)
.all()
)
keep_ids = set(normalized)
for row in existing:
if row.user_id not in keep_ids:
row.del_flag = 1
row.update_by = operator_id
row.update_time = now
db.add(row)
existing_map = {r.user_id: r for r in existing if r.del_flag == 0}
for idx, uid in enumerate(normalized):
if uid in existing_map:
row = existing_map[uid]
row.order_num = idx + 1
row.update_by = operator_id
row.update_time = now
db.add(row)
continue
db.add(
BizProductionTaskStepAssignee(
id=generate_uuid(),
step_id=step_id,
user_id=uid,
tenant_no=tenant_no,
order_num=idx + 1,
create_by=operator_id,
create_dept_id=create_dept_id or "",
create_time=now,
update_by=operator_id,
update_time=now,
del_flag=0,
)
)
def enrich_step_assignees(
db: Session, steps: List[Dict], *, tenant_no: str
) -> None:
"""批量填充 assigneeUserIds / assigneeNames / assignees。"""
step_ids = [s["id"] for s in steps if s.get("id")]
if not step_ids:
return
rows = (
db.query(BizProductionTaskStepAssignee, SysUser)
.outerjoin(SysUser, eq_unicode(SysUser.id, BizProductionTaskStepAssignee.user_id))
.filter(
BizProductionTaskStepAssignee.step_id.in_(step_ids),
BizProductionTaskStepAssignee.tenant_no == tenant_no,
BizProductionTaskStepAssignee.del_flag == 0,
)
.order_by(
BizProductionTaskStepAssignee.order_num.asc(),
BizProductionTaskStepAssignee.create_time.asc(),
)
.all()
)
by_step: Dict[str, List] = {}
for assignee, user in rows:
by_step.setdefault(assignee.step_id, []).append((assignee, user))
for step in steps:
pairs = by_step.get(step["id"], [])
step["assigneeUserIds"] = [a.user_id for a, _ in pairs]
step["assigneeNames"] = [
f"{(u.nick_name or u.user_name or '')}({u.user_no or ''})".strip()
for _, u in pairs
if u
]
step["assignees"] = [
{
"userId": a.user_id,
"nickName": (u.nick_name or u.user_name or "") if u else "",
"userNo": (u.user_no or "") if u else "",
}
for a, u in pairs
]
def enrich_step_completed_by_names(db: Session, steps: List[Dict]) -> None:
uids = list({(s.get("completedBy") or "").strip() for s in steps if (s.get("completedBy") or "").strip()})
if not uids:
return
users = {
u.id: u
for u in db.query(SysUser).filter(SysUser.id.in_(uids), SysUser.del_flag == 0).all()
}
for step in steps:
uid = (step.get("completedBy") or "").strip()
if not uid:
step["completedByName"] = ""
continue
u = users.get(uid)
step["completedByName"] = (
f"{(u.nick_name or u.user_name or '')}({u.user_no or ''})".strip() if u else uid
)
def user_is_step_assignee(
db: Session, step_id: str, user_id: str, tenant_no: str
) -> bool:
uid = (user_id or "").strip()
if not uid:
return False
row = (
db.query(BizProductionTaskStepAssignee.id)
.filter(
BizProductionTaskStepAssignee.step_id == step_id,
BizProductionTaskStepAssignee.user_id == uid,
BizProductionTaskStepAssignee.tenant_no == tenant_no,
BizProductionTaskStepAssignee.del_flag == 0,
)
.first()
)
return row is not None
def user_is_any_step_assignee_of_task(
db: Session, task_id: str, user_id: str, tenant_no: str
) -> bool:
uid = (user_id or "").strip()
if not uid:
return False
row = (
db.query(BizProductionTaskStepAssignee.id)
.join(
BizProductionTaskStep,
BizProductionTaskStep.id == BizProductionTaskStepAssignee.step_id,
)
.filter(
BizProductionTaskStep.task_id == task_id,
BizProductionTaskStep.del_flag == 0,
BizProductionTaskStepAssignee.user_id == uid,
BizProductionTaskStepAssignee.tenant_no == tenant_no,
BizProductionTaskStepAssignee.del_flag == 0,
)
.first()
)
return row is not None
+2 -2
View File
@@ -519,9 +519,9 @@ def get_task_detail(db: Session, row_id: str, tenant_no: str) -> Dict[str, Any]:
out = _task_row_to_dict(t, wo, db, tenant_no)
out = _enrich_tasks_reception_fields(db, [out])[0]
out = assignee_svc.enrich_tasks_with_assignees(db, [out], tenant_no)[0]
from app.crud.qc_work_order import _enrich_work_order_cell_products
from app.crud.qc_work_order import _enrich_work_order_qc_targets
out = _enrich_work_order_cell_products(db, tenant_no, [out])[0]
out = _enrich_work_order_qc_targets(db, tenant_no, [out])[0]
if (out.get("qcBizType") or "") != "temporary":
resolved_reception_id = _resolve_reception_id_for_work_order_detail(db, wo, tenant_no)
reception_detail = (
+235 -116
View File
@@ -33,8 +33,10 @@ from app.models.models import (
BizPilotPatient,
BizProtocolSample,
BizProductionTask,
BizProductionTaskOutput,
BizProductionTaskStep,
BizProductionTaskStepOutput,
BizProductionWorkOrder,
BizOutputType,
BizQcLabTestItem,
BizQcTask,
BizQcThirdPartyItem,
@@ -65,6 +67,7 @@ _TRIGGER_SOURCE_LABELS = {
}
_ACCEPT_CONCLUSION_LABELS = {"js": "接收", "bj": "拒收"}
_WORK_ORDER_STATUS_LABELS = {"dpz": "待配置", "ypz": "已配置", "ywc": "已完成", "yzf": "已作废"}
_VALID_QC_DISPATCH_OUTPUT_TYPE_NAME = "有效物"
_VALID_QC_WORK_ORDER_STATUSES = frozenset(_WORK_ORDER_STATUS_LABELS.keys())
_TASK_TYPE_LABELS = {"sysz": "实验室自检", "dsfjc": "第三方检测"}
_TASK_STATUS_LABELS = {"dzx": "待执行", "jxz": "进行中", "ywc": "已完成"}
@@ -199,6 +202,52 @@ def _enrich_work_order_cell_products(
return items
def _enrich_work_order_step_outputs(
db: Session, tenant_no: str, items: List[Dict[str, Any]]
) -> List[Dict[str, Any]]:
"""步骤产出物触发的质检:无细胞产品时用工单条码/名称展示质检对象。"""
if not items:
return items
step_output_ids = list(
{
x.get("productionStepOutputId")
for x in items
if (x.get("productionStepOutputId") or "").strip()
and not (x.get("cellProductId") or "").strip()
}
)
if not step_output_ids:
return items
rows = (
db.query(BizProductionTaskStepOutput)
.filter(
BizProductionTaskStepOutput.id.in_(step_output_ids),
BizProductionTaskStepOutput.tenant_no == tenant_no,
BizProductionTaskStepOutput.del_flag == 0,
)
.all()
)
row_map = {r.id: r for r in rows}
for item in items:
if (item.get("cellProductId") or "").strip():
continue
so = row_map.get(item.get("productionStepOutputId") or "")
if not so:
continue
if not (item.get("cellProductCode") or "").strip():
item["cellProductCode"] = (so.output_barcode or "").strip()
if not (item.get("cellProductName") or "").strip():
item["cellProductName"] = (so.output_name or "").strip()
return items
def _enrich_work_order_qc_targets(
db: Session, tenant_no: str, items: List[Dict[str, Any]]
) -> List[Dict[str, Any]]:
items = _enrich_work_order_cell_products(db, tenant_no, items)
return _enrich_work_order_step_outputs(db, tenant_no, items)
def _enrich_outbound_work_orders(
db: Session, tenant_no: str, items: List[Dict[str, Any]]
) -> List[Dict[str, Any]]:
@@ -710,6 +759,7 @@ def _wo_to_dict(
"productionWorkOrderId": getattr(wo, "production_work_order_id", None) or "",
"productionWorkOrderNo": "",
"productionTaskId": getattr(wo, "production_task_id", None) or "",
"productionStepOutputId": getattr(wo, "production_step_output_id", None) or "",
"receptionTime": "",
"handlerName": "",
"sampleTypeName": "",
@@ -846,12 +896,42 @@ def create_work_order_from_reception(
return wo
def _valid_qc_dispatch_output_type_ids(db: Session, tenant_no: str) -> set:
rows = (
db.query(BizOutputType)
.filter(
BizOutputType.tenant_no == tenant_no,
BizOutputType.del_flag == 0,
BizOutputType.type_name == _VALID_QC_DISPATCH_OUTPUT_TYPE_NAME,
)
.all()
)
return {r.id for r in rows}
def _resolve_cell_product_id_by_barcode(
db: Session, barcode: Optional[str], tenant_no: str
) -> Optional[str]:
code = (barcode or "").strip()
if not code:
return None
cp = (
db.query(BizCellProduct)
.filter(
BizCellProduct.product_code == code,
BizCellProduct.tenant_no == tenant_no,
BizCellProduct.del_flag == 0,
)
.first()
)
return cp.id if cp else None
def create_qc_work_order_from_production(
db: Session,
*,
production_work_order_id: str,
cell_product_id: str,
production_task_id: Optional[str] = None,
production_step_output_id: str,
remark: str = "",
tenant_no: str,
user_id: Optional[str] = None,
@@ -878,28 +958,76 @@ def create_qc_work_order_from_production(
if status not in ("jxz", "ywc"):
raise ValueError("仅进行中或已完成的生产工单可派发质检")
cp_id = _validate_cell_product_id(db, cell_product_id, tenant_no)
options = list_cell_product_options_for_production_work_order(
step_output_id = (production_step_output_id or "").strip()
if not step_output_id:
raise ValueError("请选择步骤产出物")
options = list_step_output_dispatch_options_for_production_work_order(
db, production_work_order_id=production_work_order_id, tenant_no=tenant_no
)
option_ids = {o["id"] for o in options}
if cp_id not in option_ids:
raise ValueError("所选细胞产品不在该生产工单的产出列表中")
option_map = {o["id"]: o for o in options}
selected = option_map.get(step_output_id)
if not selected:
raise ValueError("所选步骤产出物不在该生产工单的可派发列表中")
task_id = (production_task_id or "").strip() or None
if task_id:
task = (
db.query(BizProductionTask)
.filter(
BizProductionTask.id == task_id,
BizProductionTask.work_order_id == pwo.id,
BizProductionTask.tenant_no == tenant_no,
BizProductionTask.del_flag == 0,
)
.first()
step_output = (
db.query(BizProductionTaskStepOutput)
.filter(
BizProductionTaskStepOutput.id == step_output_id,
BizProductionTaskStepOutput.tenant_no == tenant_no,
BizProductionTaskStepOutput.del_flag == 0,
)
if not task:
raise ValueError("关联生产任务不存在")
.first()
)
if not step_output:
raise ValueError("步骤产出物不存在")
step = (
db.query(BizProductionTaskStep)
.filter(
BizProductionTaskStep.id == step_output.step_id,
BizProductionTaskStep.tenant_no == tenant_no,
BizProductionTaskStep.del_flag == 0,
)
.first()
)
if not step:
raise ValueError("关联工艺步骤不存在")
task = (
db.query(BizProductionTask)
.filter(
BizProductionTask.id == step.task_id,
BizProductionTask.work_order_id == pwo.id,
BizProductionTask.tenant_no == tenant_no,
BizProductionTask.del_flag == 0,
)
.first()
)
if not task:
raise ValueError("关联生产任务不存在")
cp_id = _resolve_cell_product_id_by_barcode(db, step_output.output_barcode, tenant_no)
task_id = task.id
common_fields = dict(
trigger_source="production",
cell_product_id=cp_id,
production_work_order_id=pwo.id,
production_task_id=task_id,
production_step_output_id=step_output_id,
work_order_status="dpz",
has_lab="0",
has_third_party="0",
tenant_no=tenant_no,
remark=(remark or "").strip() or None,
create_by=user_id,
create_dept_id=create_dept_id or "",
update_by=user_id,
create_time=datetime.now(),
update_time=datetime.now(),
del_flag=0,
)
if wo_type == "outbound":
schedule_id = (getattr(pwo, "infusion_schedule_id", None) or "").strip()
@@ -921,21 +1049,7 @@ def create_qc_work_order_from_production(
qc_biz_type="outbound",
biz_type=biz_type,
infusion_schedule_id=schedule_id,
trigger_source="production",
cell_product_id=cp_id,
production_work_order_id=pwo.id,
production_task_id=task_id,
work_order_status="dpz",
has_lab="0",
has_third_party="0",
tenant_no=tenant_no,
remark=(remark or "").strip() or None,
create_by=user_id,
create_dept_id=create_dept_id or "",
update_by=user_id,
create_time=datetime.now(),
update_time=datetime.now(),
del_flag=0,
**common_fields,
)
elif wo_type == "temporary":
wo = BizQcWorkOrder(
@@ -949,29 +1063,13 @@ def create_qc_work_order_from_production(
sample_code=None,
reception_source=None,
qc_biz_type="temporary",
trigger_source="production",
cell_product_id=cp_id,
production_work_order_id=pwo.id,
production_task_id=task_id,
work_order_status="dpz",
has_lab="0",
has_third_party="0",
tenant_no=tenant_no,
remark=(remark or "").strip() or None,
create_by=user_id,
create_dept_id=create_dept_id or "",
update_by=user_id,
create_time=datetime.now(),
update_time=datetime.now(),
del_flag=0,
**common_fields,
)
else:
wo = BizQcWorkOrder(
id=generate_uuid(),
work_order_no=_gen_daily_no(db, tenant_no, "ZJ"),
work_order_type="inbound",
# 生产过程触发不写入 reception_id,避免与 uk_qc_wo_reception(reception_id,del_flag) 冲突;
# 样本关联仍通过 sample_code / protocol_sample_id 等快照字段。
reception_id=None,
protocol_sample_id=pwo.protocol_sample_id,
collection_protocol_id=pwo.collection_protocol_id,
@@ -979,34 +1077,20 @@ def create_qc_work_order_from_production(
sample_code=pwo.sample_code or None,
reception_source=getattr(pwo, "reception_source", "cell_storage") or "cell_storage",
qc_biz_type="inbound",
trigger_source="production",
cell_product_id=cp_id,
production_work_order_id=pwo.id,
production_task_id=task_id,
work_order_status="dpz",
has_lab="0",
has_third_party="0",
tenant_no=tenant_no,
remark=(remark or "").strip() or None,
create_by=user_id,
create_dept_id=create_dept_id or "",
update_by=user_id,
create_time=datetime.now(),
update_time=datetime.now(),
del_flag=0,
**common_fields,
)
db.add(wo)
db.flush()
return wo
def list_cell_product_options_for_production_work_order(
def list_step_output_dispatch_options_for_production_work_order(
db: Session,
*,
production_work_order_id: str,
tenant_no: str,
) -> List[Dict[str, Any]]:
"""汇总生产工单下任务产出中的细胞产品(去重)。"""
"""汇总生产工单下可派发的步骤产出物:类型为有效物且有条码。"""
pwo = (
db.query(BizProductionWorkOrder)
.filter(
@@ -1019,6 +1103,10 @@ def list_cell_product_options_for_production_work_order(
if not pwo:
raise ValueError("生产工单不存在")
valid_type_ids = _valid_qc_dispatch_output_type_ids(db, tenant_no)
if not valid_type_ids:
return []
tasks = (
db.query(BizProductionTask)
.filter(
@@ -1033,60 +1121,91 @@ def list_cell_product_options_for_production_work_order(
task_map = {t.id: t for t in tasks}
task_ids = list(task_map.keys())
outputs = (
db.query(BizProductionTaskOutput)
steps = (
db.query(BizProductionTaskStep)
.filter(
BizProductionTaskOutput.task_id.in_(task_ids),
BizProductionTaskOutput.tenant_no == tenant_no,
BizProductionTaskOutput.del_flag == 0,
BizProductionTaskOutput.cell_product_id.isnot(None),
BizProductionTaskOutput.cell_product_id != "",
BizProductionTaskStep.task_id.in_(task_ids),
BizProductionTaskStep.tenant_no == tenant_no,
BizProductionTaskStep.del_flag == 0,
)
.order_by(BizProductionTaskOutput.order_num.asc(), BizProductionTaskOutput.create_time.asc())
.all()
)
seen: set[str] = set()
items: List[Dict[str, Any]] = []
for out in outputs:
cp_id = (out.cell_product_id or "").strip()
if not cp_id or cp_id in seen:
continue
seen.add(cp_id)
task = task_map.get(out.task_id)
items.append(
{
"id": cp_id,
"cellProductId": cp_id,
"taskId": out.task_id,
"taskNo": task.task_no if task else "",
}
)
if not items:
if not steps:
return []
cp_ids = [i["id"] for i in items]
products = (
db.query(BizCellProduct)
step_map = {s.id: s for s in steps}
step_ids = list(step_map.keys())
type_rows = (
db.query(BizOutputType)
.filter(
BizCellProduct.id.in_(cp_ids),
BizCellProduct.tenant_no == tenant_no,
BizCellProduct.del_flag == 0,
BizOutputType.id.in_(list(valid_type_ids)),
BizOutputType.del_flag == 0,
)
.all()
)
product_dicts = _cp_enrich(db, tenant_no, [_cp_row_to_dict(p) for p in products])
product_map = {d["id"]: d for d in product_dicts}
for item in items:
cp = product_map.get(item["id"]) or {}
item["productCode"] = cp.get("productCode", "")
item["productName"] = cp.get("productName", "")
item["productTypeName"] = cp.get("productTypeName", "")
item["passage"] = cp.get("passage", "")
item["storageSlotName"] = cp.get("storageSlotName", "")
type_name_map = {t.id: t.type_name or "" for t in type_rows}
outputs = (
db.query(BizProductionTaskStepOutput)
.filter(
BizProductionTaskStepOutput.step_id.in_(step_ids),
BizProductionTaskStepOutput.tenant_no == tenant_no,
BizProductionTaskStepOutput.del_flag == 0,
BizProductionTaskStepOutput.output_type_id.in_(list(valid_type_ids)),
)
.order_by(
BizProductionTaskStepOutput.order_num.asc(),
BizProductionTaskStepOutput.create_time.asc(),
)
.all()
)
items: List[Dict[str, Any]] = []
for out in outputs:
barcode = (out.output_barcode or "").strip()
if not barcode:
continue
step = step_map.get(out.step_id)
if not step:
continue
task = task_map.get(step.task_id)
if not task:
continue
type_id = (out.output_type_id or "").strip()
cell_product_id = _resolve_cell_product_id_by_barcode(db, barcode, tenant_no)
items.append(
{
"id": out.id,
"stepOutputId": out.id,
"taskId": task.id,
"taskNo": task.task_no or "",
"taskName": task.task_name or "",
"stepId": step.id,
"processName": step.process_name or "",
"outputTypeId": type_id,
"outputTypeName": type_name_map.get(type_id, _VALID_QC_DISPATCH_OUTPUT_TYPE_NAME),
"outputName": (out.output_name or "").strip(),
"outputBarcode": barcode,
"cellProductId": cell_product_id or "",
}
)
return items
def list_cell_product_options_for_production_work_order(
db: Session,
*,
production_work_order_id: str,
tenant_no: str,
) -> List[Dict[str, Any]]:
"""兼容旧接口名:返回步骤产出物派发选项。"""
return list_step_output_dispatch_options_for_production_work_order(
db,
production_work_order_id=production_work_order_id,
tenant_no=tenant_no,
)
def list_work_orders(
db: Session,
*,
@@ -1201,7 +1320,7 @@ def list_work_orders(
items = [_wo_to_dict(x) for x in rows]
items = _enrich_work_order_list_items(db, items)
if tenant_no:
items = _enrich_work_order_cell_products(db, tenant_no, items)
items = _enrich_work_order_qc_targets(db, tenant_no, items)
items = _enrich_production_work_order_refs(db, items)
items = _enrich_outbound_work_orders(db, tenant_no, items)
items = enrich_work_order_fee_summaries(db, items, tenant_no)
@@ -1355,7 +1474,7 @@ def _process_qc_info_items_from_wos(
return []
items = [_qc_info_item(db, wo, tenant_no) for wo in wos]
items = _enrich_work_order_list_items(db, items)
items = _enrich_work_order_cell_products(db, tenant_no, items)
items = _enrich_work_order_qc_targets(db, tenant_no, items)
items = _enrich_production_work_order_refs(db, items)
return items
@@ -1535,7 +1654,7 @@ def get_work_order_detail(
out = _wo_to_dict(wo)
enriched_list = _enrich_work_order_list_items(db, [out])
if tenant_no:
enriched_list = _enrich_work_order_cell_products(db, tenant_no, enriched_list)
enriched_list = _enrich_work_order_qc_targets(db, tenant_no, enriched_list)
enriched = enriched_list[0]
out.update(
{
@@ -2253,7 +2372,7 @@ def list_temp_qc_work_orders(
)
items = [_wo_to_dict(x) for x in rows]
if tenant_no:
items = _enrich_work_order_cell_products(db, tenant_no, items)
items = _enrich_work_order_qc_targets(db, tenant_no, items)
return items, int(total)
@@ -2277,7 +2396,7 @@ def get_temp_qc_work_order_detail(
if not wo:
return None
out = _wo_to_dict(wo)
enriched_list = _enrich_work_order_cell_products(db, tenant_no, [out])
enriched_list = _enrich_work_order_qc_targets(db, tenant_no, [out])
out = enriched_list[0]
out["labItems"] = _temp_lab_lines(db, wo.id)
out["thirdPartyItems"] = _temp_third_lines(db, wo.id)
+130 -25
View File
@@ -15,6 +15,8 @@ from app.services.storage_facility_code import (
build_full_slot_code,
cascade_refresh_slot_name_snapshots_for_nodes,
cascade_refresh_slot_name_snapshots_for_slots,
decode_slot_layout_order,
encode_slot_layout_order,
load_facility_map,
validate_node_code,
)
@@ -385,10 +387,21 @@ def create_slot(
order_num: Optional[int] = None,
remark: Optional[str] = None,
user_id: Optional[str] = None,
at_row_num: Optional[int] = None,
) -> BizStorageFacilitySlot:
fid = (facility_node_id or "").strip()
if not fid:
raise ValueError("所属设施节点不能为空")
if at_row_num is not None:
return insert_slot_at_row_end(
db,
facility_node_id=fid,
row_num=at_row_num,
slot_status=slot_status,
tenant_no=tenant_no,
remark=remark,
user_id=user_id,
)
obj = BizStorageFacilitySlot(
id=generate_uuid(),
facility_node_id=fid,
@@ -410,6 +423,58 @@ def create_slot(
return obj
def insert_slot_at_row_end(
db: Session,
*,
facility_node_id: str,
row_num: int,
slot_status: Optional[str] = None,
tenant_no: str = "zkxw",
remark: Optional[str] = None,
user_id: Optional[str] = None,
) -> BizStorageFacilitySlot:
fid = (facility_node_id or "").strip()
row = int(row_num)
if row < 1:
raise ValueError("行号参数不合法")
node = db.query(BizStorageFacility).filter(
and_(BizStorageFacility.id == fid, BizStorageFacility.del_flag == 0)
).first()
if not node:
raise ValueError("所属设施节点不存在")
if not node.is_leaf:
raise ValueError("只能为叶子节点创建格子")
max_col = 0
for slot in _list_facility_slots(db, fid, tenant_no):
slot_row, slot_col = _resolve_slot_layout_position(slot)
if slot_row == row:
max_col = max(max_col, slot_col)
_ensure_slot_layout_order(slot, user_id=user_id)
now = datetime.now()
obj = BizStorageFacilitySlot(
id=generate_uuid(),
facility_node_id=fid,
slot_code=None,
slot_status=(slot_status or "空").strip() or "空",
tenant_no=tenant_no,
order_num=encode_slot_layout_order(row, max_col + 1),
remark=(remark or "").strip() or None,
create_by=user_id,
create_dept_id="",
create_time=now,
update_by=user_id,
update_time=now,
del_flag=0,
)
db.add(obj)
db.commit()
db.refresh(obj)
return obj
def update_slot(
db: Session,
slot_id: str,
@@ -446,12 +511,13 @@ def update_slot(
return obj
def soft_delete_slot(db: Session, slot_id: str, tenant_no: Optional[str] = None) -> None:
def soft_delete_slot(db: Session, slot_id: str, tenant_no: Optional[str] = None, user_id: Optional[str] = None) -> None:
obj = get_slot(db, slot_id, tenant_no=tenant_no)
if not obj:
raise ValueError("存储设施小格不存在")
obj.del_flag = 1
obj.update_time = datetime.now()
obj.update_by = user_id
db.add(obj)
db.commit()
@@ -488,6 +554,59 @@ def clear_slots_by_facility_node(
return int(updated or 0)
# ---------------------------------------------------------------------------
# Slot layout & sequential code helpers
# ---------------------------------------------------------------------------
def _resolve_slot_layout_position(slot: BizStorageFacilitySlot) -> tuple[int, int]:
order = int(slot.order_num or 0)
row, col = decode_slot_layout_order(order)
if row > 0:
return row, col
match = re.match(r"^(.*?)(\d+)-(\d+)$", (slot.slot_code or "").strip())
if match:
return int(match.group(2)), int(match.group(3))
return 0, 0
def _ensure_slot_layout_order(
slot: BizStorageFacilitySlot,
*,
user_id: Optional[str] = None,
) -> tuple[int, int]:
row, col = _resolve_slot_layout_position(slot)
if row > 0:
expected = encode_slot_layout_order(row, col)
if int(slot.order_num or 0) != expected:
slot.order_num = expected
if user_id:
slot.update_by = user_id
slot.update_time = datetime.now()
return row, col
return 0, 0
def _list_facility_slots(
db: Session,
facility_node_id: str,
tenant_no: str,
) -> List[BizStorageFacilitySlot]:
return (
db.query(BizStorageFacilitySlot)
.filter(
BizStorageFacilitySlot.facility_node_id == facility_node_id,
BizStorageFacilitySlot.del_flag == 0,
BizStorageFacilitySlot.tenant_no == tenant_no,
)
.order_by(
BizStorageFacilitySlot.order_num.asc(),
BizStorageFacilitySlot.create_time.asc(),
)
.all()
)
def batch_create_slots(
db: Session,
*,
@@ -513,17 +632,17 @@ def batch_create_slots(
raise ValueError("只能为叶子节点创建格子")
now = datetime.now()
slots: List[BizStorageFacilitySlot] = []
seq = 0
for r in range(1, rows + 1):
for c in range(1, cols + 1):
code = f"{r}-{c}"
order = (r - 1) * cols + c
seq += 1
obj = BizStorageFacilitySlot(
id=generate_uuid(),
facility_node_id=fid,
slot_code=code,
slot_code=str(seq),
slot_status="空",
tenant_no=tenant_no,
order_num=order,
order_num=encode_slot_layout_order(r, c),
remark=None,
create_by=user_id,
create_dept_id="",
@@ -570,30 +689,19 @@ def insert_row_and_shift_slots(
if not node.is_leaf:
raise ValueError("只能为叶子节点新增格子行")
row_code_pattern = re.compile(r"^(.*?)(\d+)-(\d+)$")
slots = db.query(BizStorageFacilitySlot).filter(
and_(
BizStorageFacilitySlot.facility_node_id == fid,
BizStorageFacilitySlot.del_flag == 0,
BizStorageFacilitySlot.tenant_no == tenant_no,
)
).all()
slots = _list_facility_slots(db, fid, tenant_no)
now = datetime.now()
shifted_count = 0
affected_slot_ids: List[str] = []
for slot in slots:
match = row_code_pattern.match((slot.slot_code or "").strip())
if not match:
row_no, col_no = _ensure_slot_layout_order(slot, user_id=user_id)
if row_no <= 0:
continue
_prefix, row_no_raw, col_no_raw = match.groups()
row_no = int(row_no_raw)
if row_no > after_row_num:
slot.slot_code = f"{row_no + 1}-{int(col_no_raw)}"
slot.order_num = encode_slot_layout_order(row_no + 1, col_no)
slot.update_by = user_id
slot.update_time = now
shifted_count += 1
affected_slot_ids.append(slot.id)
insert_row_num = after_row_num + 1
created_slots: List[BizStorageFacilitySlot] = []
@@ -602,10 +710,10 @@ def insert_row_and_shift_slots(
BizStorageFacilitySlot(
id=generate_uuid(),
facility_node_id=fid,
slot_code=f"{insert_row_num}-{col}",
slot_code=None,
slot_status="空",
tenant_no=tenant_no,
order_num=0,
order_num=encode_slot_layout_order(insert_row_num, col),
remark=None,
create_by=user_id,
create_dept_id="",
@@ -617,8 +725,5 @@ def insert_row_and_shift_slots(
)
if created_slots:
db.add_all(created_slots)
affected_slot_ids.extend(s.id for s in created_slots)
if affected_slot_ids:
cascade_refresh_slot_name_snapshots_for_slots(db, affected_slot_ids, tenant_no)
db.commit()
return {"shiftedCount": shifted_count, "createdCount": len(created_slots)}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_crud_production_task_py", "label": "production_task.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L1"}, {"id": "crud_production_task_require_requisition_for_catalog_material", "label": "_require_requisition_for_catalog_material()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L49"}, {"id": "crud_production_task_production_conclusion_label", "label": "_production_conclusion_label()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L101"}, {"id": "crud_production_task_fmt_dt", "label": "_fmt_dt()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L108"}, {"id": "crud_production_task_normalize_date_bound", "label": "_normalize_date_bound()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L112"}, {"id": "crud_production_task_enrich_outbound_task_items", "label": "_enrich_outbound_task_items()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L119"}, {"id": "crud_production_task_generate_task_no", "label": "_generate_task_no()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L253"}, {"id": "crud_production_task_task_to_dict", "label": "_task_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L267"}, {"id": "crud_production_task_step_to_dict", "label": "_step_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L294"}, {"id": "crud_production_task_step_reagent_to_dict", "label": "_step_reagent_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L321"}, {"id": "crud_production_task_step_configured_reagent_to_dict", "label": "_step_configured_reagent_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L338"}, {"id": "crud_production_task_step_consumable_to_dict", "label": "_step_consumable_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L354"}, {"id": "crud_production_task_step_output_to_dict", "label": "_step_output_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L371"}, {"id": "crud_production_task_step_data_collection_to_dict", "label": "_step_data_collection_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L383"}, {"id": "crud_production_task_task_input_to_dict", "label": "_task_input_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L395"}, {"id": "crud_production_task_task_output_to_dict", "label": "_task_output_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L417"}, {"id": "crud_production_task_soft_delete_sub", "label": "_soft_delete_sub()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L442"}, {"id": "crud_production_task_resolve_sub_table_order_num", "label": "_resolve_sub_table_order_num()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L455"}, {"id": "crud_production_task_step_sub_tables_to_dict", "label": "_step_sub_tables_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L464"}, {"id": "crud_production_task_clone_step_reagents", "label": "_clone_step_reagents()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L490"}, {"id": "crud_production_task_clone_step_consumables", "label": "_clone_step_consumables()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L513"}, {"id": "crud_production_task_clone_step_outputs", "label": "_clone_step_outputs()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L536"}, Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_crud_process_py", "label": "process.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process.py", "source_location": "L1"}, {"id": "crud_process_reagent_row_to_dict", "label": "_reagent_row_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process.py", "source_location": "L25"}, {"id": "crud_process_consumable_row_to_dict", "label": "_consumable_row_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process.py", "source_location": "L40"}, {"id": "crud_process_output_row_to_dict", "label": "_output_row_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process.py", "source_location": "L54"}, {"id": "crud_process_data_collection_row_to_dict", "label": "_data_collection_row_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process.py", "source_location": "L66"}, {"id": "crud_process_row_to_dict", "label": "_row_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process.py", "source_location": "L77"}, {"id": "crud_process_list_processes", "label": "list_processes()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process.py", "source_location": "L96"}, {"id": "crud_process_list_all_processes", "label": "list_all_processes()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process.py", "source_location": "L147"}, {"id": "crud_process_get_process", "label": "get_process()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process.py", "source_location": "L156"}, {"id": "crud_process_get_process_detail", "label": "get_process_detail()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process.py", "source_location": "L164"}, {"id": "crud_process_process_code_exists", "label": "process_code_exists()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process.py", "source_location": "L204"}, {"id": "crud_process_create_sub_reagent", "label": "_create_sub_reagent()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process.py", "source_location": "L213"}, {"id": "crud_process_create_sub_consumable", "label": "_create_sub_consumable()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process.py", "source_location": "L236"}, {"id": "crud_process_create_sub_output", "label": "_create_sub_output()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process.py", "source_location": "L259"}, {"id": "crud_process_sub_item_with_order", "label": "_sub_item_with_order()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process.py", "source_location": "L279"}, {"id": "crud_process_create_sub_data_collection", "label": "_create_sub_data_collection()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process.py", "source_location": "L286"}, {"id": "crud_process_create_process", "label": "create_process()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process.py", "source_location": "L305"}, {"id": "crud_process_soft_delete_sub_records", "label": "_soft_delete_sub_records()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process.py", "source_location": "L372"}, {"id": "crud_process_update_process", "label": "update_process()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process.py", "source_location": "L384"}, {"id": "crud_process_soft_delete_process", "label": "soft_delete_process()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process.py", "source_location": "L461"}, {"id": "crud_process_total_pages", "label": "total_pages()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process.py", "source_location": "L478"}, {"id": "crud_process_rationale_1", "label": "\u5de5\u827a\u5e93 CRUD\uff08biz_process + biz_process_reagent/consumable/output\uff09", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process.py", "source_location": "L1"}, {"id": "crud_process_rationale_280", "label": "\u4fdd\u5b58\u5b50\u8868\u65f6\u6309\u5217\u8868\u4e0b\u6807\u5199\u5165 order_num\uff0c\u907f\u514d\u540c\u6279 create_time \u5bfc\u81f4\u67e5\u8be2\u987a\u5e8f\u6f02\u79fb\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process.py", "source_location": "L280"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_processLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_crud_process_route_py", "label": "process_route.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process_route.py", "source_location": "L1"}, {"id": "crud_process_route_route_row_to_dict", "label": "_route_row_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process_route.py", "source_location": "L33"}, {"id": "crud_process_route_step_row_to_dict", "label": "_step_row_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process_route.py", "source_location": "L49"}, {"id": "crud_process_route_step_reagent_row_to_dict", "label": "_step_reagent_row_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process_route.py", "source_location": "L67"}, {"id": "crud_process_route_step_consumable_row_to_dict", "label": "_step_consumable_row_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process_route.py", "source_location": "L82"}, {"id": "crud_process_route_step_output_row_to_dict", "label": "_step_output_row_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process_route.py", "source_location": "L97"}, {"id": "crud_process_route_step_data_collection_row_to_dict", "label": "_step_data_collection_row_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process_route.py", "source_location": "L109"}, {"id": "crud_process_route_list_routes", "label": "list_routes()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process_route.py", "source_location": "L124"}, {"id": "crud_process_route_get_route", "label": "get_route()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process_route.py", "source_location": "L168"}, {"id": "crud_process_route_get_route_detail", "label": "get_route_detail()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process_route.py", "source_location": "L174"}, {"id": "crud_process_route_create_route", "label": "create_route()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process_route.py", "source_location": "L212"}, {"id": "crud_process_route_update_route", "label": "update_route()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process_route.py", "source_location": "L243"}, {"id": "crud_process_route_set_current_route", "label": "set_current_route()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process_route.py", "source_location": "L259"}, {"id": "crud_process_route_soft_delete_sub_records", "label": "_soft_delete_sub_records()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process_route.py", "source_location": "L285"}, {"id": "crud_process_route_create_step_reagent", "label": "_create_step_reagent()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process_route.py", "source_location": "L295"}, {"id": "crud_process_route_create_step_consumable", "label": "_create_step_consumable()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process_route.py", "source_location": "L311"}, {"id": "crud_process_route_create_step_output", "label": "_create_step_output()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process_route.py", "source_location": "L327"}, {"id": "crud_process_route_create_step_data_collection", "label": "_create_step_data_collection()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process_route.py", "source_location": "L340"}, {"id": "crud_process_route_add_step", "label": "add_step()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process_route.py", "source_location": "L356"}, {"id": "crud_process_route_update_step", "label": "update_step()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process_route.py", "source_location": "L432"}, {"id": "crud_process_route_reorder_steps", "label": "reorder_steps()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process_route.py", "source_location": "L457"}, {"id": "crud_process_route_copy_step", "label": "copy_step()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process_route.py", "source_location": "L471"}, {"id": "crud_process_route_soft_delete_step", "label": "soft_delete_step()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process_route.py", "source_location": "L576"}, {"id": "crud_process_route_soft_delete_routLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_crud_production_task_py", "label": "production_task.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L1"}, {"id": "crud_production_task_require_requisition_for_catalog_material", "label": "_require_requisition_for_catalog_material()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L49"}, {"id": "crud_production_task_production_conclusion_label", "label": "_production_conclusion_label()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L101"}, {"id": "crud_production_task_fmt_dt", "label": "_fmt_dt()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L108"}, {"id": "crud_production_task_normalize_date_bound", "label": "_normalize_date_bound()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L112"}, {"id": "crud_production_task_enrich_outbound_task_items", "label": "_enrich_outbound_task_items()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L119"}, {"id": "crud_production_task_generate_task_no", "label": "_generate_task_no()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L253"}, {"id": "crud_production_task_task_to_dict", "label": "_task_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L267"}, {"id": "crud_production_task_step_to_dict", "label": "_step_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L294"}, {"id": "crud_production_task_step_reagent_to_dict", "label": "_step_reagent_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L321"}, {"id": "crud_production_task_step_configured_reagent_to_dict", "label": "_step_configured_reagent_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L338"}, {"id": "crud_production_task_step_consumable_to_dict", "label": "_step_consumable_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L354"}, {"id": "crud_production_task_step_output_to_dict", "label": "_step_output_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L371"}, {"id": "crud_production_task_step_data_collection_to_dict", "label": "_step_data_collection_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L383"}, {"id": "crud_production_task_task_input_to_dict", "label": "_task_input_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L395"}, {"id": "crud_production_task_task_output_to_dict", "label": "_task_output_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L417"}, {"id": "crud_production_task_soft_delete_sub", "label": "_soft_delete_sub()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L442"}, {"id": "crud_production_task_resolve_sub_table_order_num", "label": "_resolve_sub_table_order_num()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L455"}, {"id": "crud_production_task_step_sub_tables_to_dict", "label": "_step_sub_tables_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L464"}, {"id": "crud_production_task_clone_step_reagents", "label": "_clone_step_reagents()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L490"}, {"id": "crud_production_task_clone_step_consumables", "label": "_clone_step_consumables()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L513"}, {"id": "crud_production_task_clone_step_outputs", "label": "_clone_step_outputs()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L536"}, Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_services_storage_facility_code_py", "label": "storage_facility_code.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/services/storage_facility_code.py", "source_location": "L1"}, {"id": "services_storage_facility_code_encode_slot_layout_order", "label": "encode_slot_layout_order()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/services/storage_facility_code.py", "source_location": "L19"}, {"id": "services_storage_facility_code_decode_slot_layout_order", "label": "decode_slot_layout_order()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/services/storage_facility_code.py", "source_location": "L30"}, {"id": "services_storage_facility_code_validate_node_code", "label": "validate_node_code()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/services/storage_facility_code.py", "source_location": "L38"}, {"id": "services_storage_facility_code_assert_sibling_node_code_unique", "label": "assert_sibling_node_code_unique()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/services/storage_facility_code.py", "source_location": "L47"}, {"id": "services_storage_facility_code_build_node_code_path", "label": "build_node_code_path()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/services/storage_facility_code.py", "source_location": "L71"}, {"id": "services_storage_facility_code_build_full_slot_code", "label": "build_full_slot_code()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/services/storage_facility_code.py", "source_location": "L96"}, {"id": "services_storage_facility_code_load_facility_map", "label": "load_facility_map()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/services/storage_facility_code.py", "source_location": "L108"}, {"id": "services_storage_facility_code_collect_slot_ids_under_nodes", "label": "collect_slot_ids_under_nodes()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/services/storage_facility_code.py", "source_location": "L120"}, {"id": "services_storage_facility_code_refresh_outbound_release_cell_slot_names", "label": "refresh_outbound_release_cell_slot_names()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/services/storage_facility_code.py", "source_location": "L163"}, {"id": "services_storage_facility_code_cascade_refresh_slot_name_snapshots_for_slots", "label": "cascade_refresh_slot_name_snapshots_for_slots()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/services/storage_facility_code.py", "source_location": "L208"}, {"id": "services_storage_facility_code_cascade_refresh_slot_name_snapshots_for_nodes", "label": "cascade_refresh_slot_name_snapshots_for_nodes()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/services/storage_facility_code.py", "source_location": "L237"}, {"id": "services_storage_facility_code_rationale_1", "label": "\u5e93\u4f4d\u8282\u70b9\u7f16\u53f7\u8def\u5f84\u4e0e\u50a8\u7269\u683c\u5b8c\u6574\u7f16\u53f7\u8ba1\u7b97\u3001\u5feb\u7167\u7ea7\u8054\u5237\u65b0\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/services/storage_facility_code.py", "source_location": "L1"}, {"id": "services_storage_facility_code_rationale_20", "label": "\u5c06\u8d27\u67b6\u884c\u5217\u4f4d\u7f6e\u7f16\u7801\u4e3a order_num\uff08row*1000+col\uff09\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/services/storage_facility_code.py", "source_location": "L20"}, {"id": "services_storage_facility_code_rationale_31", "label": "\u89e3\u6790 order_num \u4e2d\u7684\u884c\u5217\u4f4d\u7f6e\uff1b\u975e\u5e03\u5c40\u7f16\u7801\u65f6\u8fd4\u56de (0, order)\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/services/storage_facility_code.py", "source_location": "L31"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_backend_app_services_storage_facility_code_py", "target": "datetime", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/services/storage_facility_code.py", "source_location": "L4", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_services_storage_facility_code_py", "target": "typing", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/services/storage_facility_code.py", "source_location": "L5", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_services_storage_facility_code_py", "target": "sqlalchemy_orm", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/worLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_work_order_py", "label": "qc_work_order.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_work_order.py", "source_location": "L1"}, {"id": "crud_qc_work_order_resolve_outbound_biz_type_for_schedule", "label": "resolve_outbound_biz_type_for_schedule()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_work_order.py", "source_location": "L89"}, {"id": "crud_qc_work_order_dec", "label": "_dec()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_work_order.py", "source_location": "L130"}, {"id": "crud_qc_work_order_fmt_dt", "label": "_fmt_dt()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_work_order.py", "source_location": "L136"}, {"id": "crud_qc_work_order_normalize_date_bound", "label": "_normalize_date_bound()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_work_order.py", "source_location": "L140"}, {"id": "crud_qc_work_order_reception_source_label", "label": "_reception_source_label()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_work_order.py", "source_location": "L147"}, {"id": "crud_qc_work_order_qc_biz_type_label", "label": "_qc_biz_type_label()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_work_order.py", "source_location": "L152"}, {"id": "crud_qc_work_order_work_order_qc_biz_type", "label": "_work_order_qc_biz_type()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_work_order.py", "source_location": "L157"}, {"id": "crud_qc_work_order_validate_cell_product_id", "label": "_validate_cell_product_id()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_work_order.py", "source_location": "L164"}, {"id": "crud_qc_work_order_enrich_work_order_cell_products", "label": "_enrich_work_order_cell_products()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_work_order.py", "source_location": "L176"}, {"id": "crud_qc_work_order_enrich_work_order_step_outputs", "label": "_enrich_work_order_step_outputs()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_work_order.py", "source_location": "L205"}, {"id": "crud_qc_work_order_enrich_work_order_qc_targets", "label": "_enrich_work_order_qc_targets()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_work_order.py", "source_location": "L244"}, {"id": "crud_qc_work_order_enrich_outbound_work_orders", "label": "_enrich_outbound_work_orders()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_work_order.py", "source_location": "L251"}, {"id": "crud_qc_work_order_outbound_qc_work_order_ids_by_keyword", "label": "_outbound_qc_work_order_ids_by_keyword()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_work_order.py", "source_location": "L372"}, {"id": "crud_qc_work_order_accept_conclusion_label", "label": "_accept_conclusion_label()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_work_order.py", "source_location": "L419"}, {"id": "crud_qc_work_order_load_sample_type_name_map", "label": "_load_sample_type_name_map()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_work_order.py", "source_location": "L424"}, {"id": "crud_qc_work_order_effective_reception_source", "label": "_effective_reception_source()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_work_order.py", "source_location": "L436"}, {"id": "crud_qc_work_order_effective_donation_sample_id", "label": "_effective_donation_sample_id()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_work_order.py", "source_location": "L445"}, {"id": "crud_qc_work_order_effective_protocol_sample_id", "label": "_effective_protocol_sample_id()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_work_order.py", "source_location": "L454"}, {"id": "crud_qc_work_order_sample_type_name_for_item", "label": "_sample_type_name_for_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_work_order.py", "source_location": "L462"}, {"id": "crud_qc_work_order_sample_type_name_for_reception", "label": "_sample_type_name_for_reception()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_work_order.py", "source_location": "L480"}, {"id": "crud_qc_work_order_enrich_work_order_list_items", "label": "_enrich_work_order_list_items()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/bLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_schemas_production_task_py", "label": "production_task.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/production_task.py", "source_location": "L1"}, {"id": "schemas_production_task_productiontaskcreate", "label": "ProductionTaskCreate", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/production_task.py", "source_location": "L6"}, {"id": "basemodel", "label": "BaseModel", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "schemas_production_task_productiontaskforwardbody", "label": "ProductionTaskForwardBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/production_task.py", "source_location": "L17"}, {"id": "schemas_production_task_productiontaskassigneeupdate", "label": "ProductionTaskAssigneeUpdate", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/production_task.py", "source_location": "L23"}, {"id": "schemas_production_task_productiontasklistquery", "label": "ProductionTaskListQuery", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/production_task.py", "source_location": "L29"}, {"id": "schemas_production_task_productiontaskupdate", "label": "ProductionTaskUpdate", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/production_task.py", "source_location": "L38"}, {"id": "schemas_production_task_productiontaskstatusupdate", "label": "ProductionTaskStatusUpdate", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/production_task.py", "source_location": "L45"}, {"id": "schemas_production_task_taskstepadd", "label": "TaskStepAdd", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/production_task.py", "source_location": "L52"}, {"id": "schemas_production_task_taskstepupdate", "label": "TaskStepUpdate", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/production_task.py", "source_location": "L63"}, {"id": "schemas_production_task_taskstepmaterialitem", "label": "TaskStepMaterialItem", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/production_task.py", "source_location": "L73"}, {"id": "schemas_production_task_taskstepoutputitem", "label": "TaskStepOutputItem", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/production_task.py", "source_location": "L88"}, {"id": "schemas_production_task_taskstepdatacollectionitem", "label": "TaskStepDataCollectionItem", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/production_task.py", "source_location": "L95"}, {"id": "schemas_production_task_taskinputitem", "label": "TaskInputItem", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/production_task.py", "source_location": "L102"}, {"id": "schemas_production_task_taskoutputitem", "label": "TaskOutputItem", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/production_task.py", "source_location": "L111"}, {"id": "schemas_production_task_taskstepreorderbody", "label": "TaskStepReorderBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/production_task.py", "source_location": "L139"}, {"id": "schemas_production_task_productiontaskstepdispatchitem", "label": "ProductionTaskStepDispatchItem", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/production_task.py", "source_location": "L145"}, {"id": "schemas_production_task_productiontaskstepdispatchbody", "label": "ProductionTaskStepDispatchBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/production_task.py", "source_location": "L152"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_production_task_py", "target": "pydantic", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/production_task.py", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_production_task_py", "target": "typing", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/production_task.py", "source_location": "L2", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_production_task_py", "target": "datetime", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/production_task.py", "source_location": "L3", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_production_task_py", "targeLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_crud_storage_facility_py", "label": "storage_facility.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_facility.py", "source_location": "L1"}, {"id": "crud_storage_facility_row_to_dict", "label": "_row_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_facility.py", "source_location": "L30"}, {"id": "crud_storage_facility_list_all_facilities", "label": "list_all_facilities()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_facility.py", "source_location": "L46"}, {"id": "crud_storage_facility_get_facility", "label": "get_facility()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_facility.py", "source_location": "L69"}, {"id": "crud_storage_facility_get_children", "label": "get_children()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_facility.py", "source_location": "L78"}, {"id": "crud_storage_facility_collect_descendant_ids", "label": "collect_descendant_ids()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_facility.py", "source_location": "L94"}, {"id": "crud_storage_facility_create_facility", "label": "create_facility()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_facility.py", "source_location": "L107"}, {"id": "crud_storage_facility_update_facility", "label": "update_facility()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_facility.py", "source_location": "L151"}, {"id": "crud_storage_facility_soft_delete_facility", "label": "soft_delete_facility()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_facility.py", "source_location": "L209"}, {"id": "crud_storage_facility_slot_to_dict", "label": "_slot_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_facility.py", "source_location": "L235"}, {"id": "crud_storage_facility_list_slots", "label": "list_slots()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_facility.py", "source_location": "L258"}, {"id": "crud_storage_facility_is_slot_available_row", "label": "_is_slot_available_row()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_facility.py", "source_location": "L277"}, {"id": "crud_storage_facility_slot_select_display_name", "label": "_slot_select_display_name()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_facility.py", "source_location": "L283"}, {"id": "crud_storage_facility_list_slot_select_options", "label": "list_slot_select_options()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_facility.py", "source_location": "L289"}, {"id": "crud_storage_facility_get_slot", "label": "get_slot()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_facility.py", "source_location": "L366"}, {"id": "crud_storage_facility_slot_to_dict_for_row", "label": "_slot_to_dict_for_row()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_facility.py", "source_location": "L375"}, {"id": "crud_storage_facility_create_slot", "label": "create_slot()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_facility.py", "source_location": "L380"}, {"id": "crud_storage_facility_insert_slot_at_row_end", "label": "insert_slot_at_row_end()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_facility.py", "source_location": "L426"}, {"id": "crud_storage_facility_update_slot", "label": "update_slot()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_facility.py", "source_location": "L478"}, {"id": "crud_storage_facility_soft_delete_slot", "label": "soft_delete_slot()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_facility.py", "source_location": "L514"}, {"id": "crud_storage_facility_clear_slots_by_facility_node", "label": "clear_slots_by_facility_node()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_facility.py", "source_location": "L525"}, {"id": "crud_storage_facility_resolve_slot_layout_position", "label": "_resolve_slot_layout_position()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_facility.py", "source_location": "L562"}, {"id": "crud_storage_facility_ensure_slot_layout_order", "label": "_ensure_slot_layout_order()", "file_type": "code", "source_file": "Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_api_operations_py", "label": "operations.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/operations.py", "source_location": "L1"}, {"id": "api_operations_get_tenant_no", "label": "_get_tenant_no()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/operations.py", "source_location": "L24"}, {"id": "api_operations_cleanroomcreatebody", "label": "CleanRoomCreateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/operations.py", "source_location": "L39"}, {"id": "basemodel", "label": "BaseModel", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "api_operations_cleanroomupdatebody", "label": "CleanRoomUpdateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/operations.py", "source_location": "L52"}, {"id": "api_operations_list_clean_rooms", "label": "list_clean_rooms()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/operations.py", "source_location": "L66"}, {"id": "api_operations_create_clean_room", "label": "create_clean_room()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/operations.py", "source_location": "L91"}, {"id": "api_operations_update_clean_room", "label": "update_clean_room()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/operations.py", "source_location": "L117"}, {"id": "api_operations_delete_clean_room", "label": "delete_clean_room()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/operations.py", "source_location": "L146"}, {"id": "api_operations_accesslogcreatebody", "label": "AccessLogCreateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/operations.py", "source_location": "L164"}, {"id": "api_operations_accesslogupdatebody", "label": "AccessLogUpdateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/operations.py", "source_location": "L178"}, {"id": "api_operations_parse_optional_dt", "label": "_parse_optional_dt()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/operations.py", "source_location": "L191"}, {"id": "api_operations_list_access_logs", "label": "list_access_logs()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/operations.py", "source_location": "L198"}, {"id": "api_operations_create_access_log", "label": "create_access_log()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/operations.py", "source_location": "L227"}, {"id": "api_operations_update_access_log", "label": "update_access_log()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/operations.py", "source_location": "L254"}, {"id": "api_operations_delete_access_log", "label": "delete_access_log()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/operations.py", "source_location": "L283"}, {"id": "api_operations_uvdisinfectlogcreatebody", "label": "UvDisinfectLogCreateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/operations.py", "source_location": "L301"}, {"id": "api_operations_uvdisinfectlogupdatebody", "label": "UvDisinfectLogUpdateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/operations.py", "source_location": "L313"}, {"id": "api_operations_list_uv_disinfect_logs", "label": "list_uv_disinfect_logs()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/operations.py", "source_location": "L325"}, {"id": "api_operations_create_uv_disinfect_log", "label": "create_uv_disinfect_log()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/operations.py", "source_location": "L356"}, {"id": "api_operations_update_uv_disinfect_log", "label": "update_uv_disinfect_log()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/operations.py", "source_location": "L381"}, {"id": "api_operations_delete_uv_disinfect_log", "label": "delete_uv_disinfect_log()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/operations.py", "source_location": "L409"}, {"id": "api_operations_storagefacilitycreatebody", "label": "StorageFacilityCreateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/operations.py", "source_location": "L427"}, {"id": "api_operations_storagefacilityupdatebody", "label": "StorageFacilityUpdateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/operations.py", "source_location": "L439"}, {"id": "api_operations_list_storage_facilities", "labelLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_api_production_py", "label": "production.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L1"}, {"id": "api_production_legacy_material_write_gone", "label": "_legacy_material_write_gone()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L75"}, {"id": "api_production_get_tenant_no", "label": "_get_tenant_no()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L79"}, {"id": "api_production_get_current_user_id", "label": "_get_current_user_id()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L87"}, {"id": "api_production_assert_production_task_write_access", "label": "_assert_production_task_write_access()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L91"}, {"id": "api_production_consumablecreatebody", "label": "ConsumableCreateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L115"}, {"id": "basemodel", "label": "BaseModel", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "api_production_consumableupdatebody", "label": "ConsumableUpdateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L129"}, {"id": "api_production_list_consumables", "label": "list_consumables()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L144"}, {"id": "api_production_list_all_consumables", "label": "list_all_consumables()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L158"}, {"id": "api_production_create_consumable", "label": "create_consumable()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L168"}, {"id": "api_production_update_consumable", "label": "update_consumable()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L177"}, {"id": "api_production_delete_consumable", "label": "delete_consumable()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L187"}, {"id": "api_production_stockinbody", "label": "StockInBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L195"}, {"id": "api_production_stockoutbody", "label": "StockOutBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L201"}, {"id": "api_production_batchstockinitem", "label": "BatchStockInItem", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L209"}, {"id": "api_production_batchstockinbody", "label": "BatchStockInBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L216"}, {"id": "api_production_batchstockoutitem", "label": "BatchStockOutItem", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L222"}, {"id": "api_production_batchstockoutbody", "label": "BatchStockOutBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L228"}, {"id": "api_production_consumable_stock_in", "label": "consumable_stock_in()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L237"}, {"id": "api_production_consumable_stock_out", "label": "consumable_stock_out()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L259"}, {"id": "api_production_consumable_batch_stock_in", "label": "consumable_batch_stock_in()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L283"}, {"id": "api_production_consumable_batch_stock_out", "label": "consumable_batch_stock_out()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L301"}, {"id": "api_production_list_consumable_stock_logs", "label": "list_consumable_stock_logs()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L320"}, {"id": "api_production_list_all_consumable_stock_logs", "labLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_models_models_py", "label": "models.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L1"}, {"id": "models_models_userrole", "label": "UserRole", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L10"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "models_models_sysuser", "label": "SysUser", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L22"}, {"id": "base", "label": "Base", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "models_models_sysrole", "label": "SysRole", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L48"}, {"id": "models_models_sysuserrole", "label": "SysUserRole", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L68"}, {"id": "models_models_sysrolemenu", "label": "SysRoleMenu", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L87"}, {"id": "models_models_sysroledatascopedetail", "label": "SysRoleDataScopeDetail", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L106"}, {"id": "models_models_sysmenu", "label": "SysMenu", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L126"}, {"id": "models_models_sysdept", "label": "SysDept", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L152"}, {"id": "models_models_systenant", "label": "SysTenant", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L175"}, {"id": "models_models_syssite", "label": "SysSite", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L195"}, {"id": "models_models_syspost", "label": "SysPost", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L221"}, {"id": "models_models_sysuserpost", "label": "SysUserPost", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L241"}, {"id": "models_models_bizitemunit", "label": "BizItemUnit", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L260"}, {"id": "models_models_bizexpresscompany", "label": "BizExpressCompany", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L279"}, {"id": "models_models_bizcustomer", "label": "BizCustomer", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L299"}, {"id": "models_models_bizcellsampletype", "label": "BizCellSampleType", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L322"}, {"id": "models_models_bizcollectionregion", "label": "BizCollectionRegion", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L345"}, {"id": "models_models_bizcollectionhospital", "label": "BizCollectionHospital", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L363"}, {"id": "models_models_bizcollectionprotocolno", "label": "BizCollectionProtocolNo", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L382"}, {"id": "models_models_bizbiznosequence", "label": "BizBizNoSequence", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L404"}, {"id": "models_models_bizsamplecode", "label": "BizSampleCode", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L424"}, {"id": "models_models_sysdicttype", "label": "SysDictType", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L452"}, {"id": "models_models_sysethnic", "label": "SysEthnic", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L471"}, {"id": "models_models_sysdictdata", "label": "SysDictData", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location"Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_crud_production_task_step_assignee_py", "label": "production_task_step_assignee.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task_step_assignee.py", "source_location": "L1"}, {"id": "crud_production_task_step_assignee_active_users_in_tenant", "label": "_active_users_in_tenant()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task_step_assignee.py", "source_location": "L14"}, {"id": "crud_production_task_step_assignee_validate_assignee_user_ids", "label": "validate_assignee_user_ids()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task_step_assignee.py", "source_location": "L33"}, {"id": "crud_production_task_step_assignee_sync_assignees_for_step", "label": "sync_assignees_for_step()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task_step_assignee.py", "source_location": "L54"}, {"id": "crud_production_task_step_assignee_enrich_step_assignees", "label": "enrich_step_assignees()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task_step_assignee.py", "source_location": "L108"}, {"id": "crud_production_task_step_assignee_enrich_step_completed_by_names", "label": "enrich_step_completed_by_names()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task_step_assignee.py", "source_location": "L150"}, {"id": "crud_production_task_step_assignee_user_is_step_assignee", "label": "user_is_step_assignee()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task_step_assignee.py", "source_location": "L169"}, {"id": "crud_production_task_step_assignee_user_is_any_step_assignee_of_task", "label": "user_is_any_step_assignee_of_task()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task_step_assignee.py", "source_location": "L188"}, {"id": "crud_production_task_step_assignee_rationale_63", "label": "\u5168\u91cf\u66ff\u6362\u6b65\u9aa4\u8d1f\u8d23\u4eba\uff08\u8f6f\u5220\u65e7\u8bb0\u5f55 + \u63d2\u5165\u65b0\u8bb0\u5f55\uff09\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task_step_assignee.py", "source_location": "L63"}, {"id": "crud_production_task_step_assignee_rationale_111", "label": "\u6279\u91cf\u586b\u5145 assigneeUserIds / assigneeNames / assignees\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task_step_assignee.py", "source_location": "L111"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_production_task_step_assignee_py", "target": "datetime", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task_step_assignee.py", "source_location": "L4", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_production_task_step_assignee_py", "target": "typing", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task_step_assignee.py", "source_location": "L5", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_production_task_step_assignee_py", "target": "sqlalchemy_orm", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task_step_assignee.py", "source_location": "L7", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_production_task_step_assignee_py", "target": "app_core_db_collation", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task_step_assignee.py", "source_location": "L9", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_production_task_step_assignee_py", "target": "app_core_utils", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task_step_assignee.py", "source_location": "L10", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_production_task_step_assignee_py", "target": "app_models_models", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task_step_assignee.py", "source_location": "L11", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_production_task_step_assignee_py", "target": "crud_production_task_step_assignee_active_users_in_tenant", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/cLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_api_production_py", "label": "production.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L1"}, {"id": "api_production_legacy_material_write_gone", "label": "_legacy_material_write_gone()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L76"}, {"id": "api_production_get_tenant_no", "label": "_get_tenant_no()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L80"}, {"id": "api_production_get_current_user_id", "label": "_get_current_user_id()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L88"}, {"id": "api_production_assert_production_task_write_access", "label": "_assert_production_task_write_access()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L92"}, {"id": "api_production_assert_production_task_step_write_access", "label": "_assert_production_task_step_write_access()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L113"}, {"id": "api_production_consumablecreatebody", "label": "ConsumableCreateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L140"}, {"id": "basemodel", "label": "BaseModel", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "api_production_consumableupdatebody", "label": "ConsumableUpdateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L154"}, {"id": "api_production_list_consumables", "label": "list_consumables()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L169"}, {"id": "api_production_list_all_consumables", "label": "list_all_consumables()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L183"}, {"id": "api_production_create_consumable", "label": "create_consumable()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L193"}, {"id": "api_production_update_consumable", "label": "update_consumable()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L202"}, {"id": "api_production_delete_consumable", "label": "delete_consumable()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L212"}, {"id": "api_production_stockinbody", "label": "StockInBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L220"}, {"id": "api_production_stockoutbody", "label": "StockOutBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L226"}, {"id": "api_production_batchstockinitem", "label": "BatchStockInItem", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L234"}, {"id": "api_production_batchstockinbody", "label": "BatchStockInBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L241"}, {"id": "api_production_batchstockoutitem", "label": "BatchStockOutItem", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L247"}, {"id": "api_production_batchstockoutbody", "label": "BatchStockOutBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L253"}, {"id": "api_production_consumable_stock_in", "label": "consumable_stock_in()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L262"}, {"id": "api_production_consumable_stock_out", "label": "consumable_stock_out()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L284"}, {"id": "api_production_consumable_batch_stock_in", "label": "consumable_batch_stock_in()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L308"}, {"id": "api_production_consumable_batch_stock_out", "label": "consumable_batch_stock_out()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L326"}, {"id": "api_production_list_conLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_api_production_py", "label": "production.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L1"}, {"id": "api_production_legacy_material_write_gone", "label": "_legacy_material_write_gone()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L76"}, {"id": "api_production_get_tenant_no", "label": "_get_tenant_no()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L80"}, {"id": "api_production_get_current_user_id", "label": "_get_current_user_id()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L88"}, {"id": "api_production_assert_production_task_write_access", "label": "_assert_production_task_write_access()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L92"}, {"id": "api_production_assert_production_task_step_write_access", "label": "_assert_production_task_step_write_access()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L113"}, {"id": "api_production_consumablecreatebody", "label": "ConsumableCreateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L140"}, {"id": "basemodel", "label": "BaseModel", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "api_production_consumableupdatebody", "label": "ConsumableUpdateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L154"}, {"id": "api_production_list_consumables", "label": "list_consumables()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L169"}, {"id": "api_production_list_all_consumables", "label": "list_all_consumables()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L183"}, {"id": "api_production_create_consumable", "label": "create_consumable()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L193"}, {"id": "api_production_update_consumable", "label": "update_consumable()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L202"}, {"id": "api_production_delete_consumable", "label": "delete_consumable()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L212"}, {"id": "api_production_stockinbody", "label": "StockInBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L220"}, {"id": "api_production_stockoutbody", "label": "StockOutBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L226"}, {"id": "api_production_batchstockinitem", "label": "BatchStockInItem", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L234"}, {"id": "api_production_batchstockinbody", "label": "BatchStockInBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L241"}, {"id": "api_production_batchstockoutitem", "label": "BatchStockOutItem", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L247"}, {"id": "api_production_batchstockoutbody", "label": "BatchStockOutBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L253"}, {"id": "api_production_consumable_stock_in", "label": "consumable_stock_in()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L262"}, {"id": "api_production_consumable_stock_out", "label": "consumable_stock_out()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L284"}, {"id": "api_production_consumable_batch_stock_in", "label": "consumable_batch_stock_in()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L308"}, {"id": "api_production_consumable_batch_stock_out", "label": "consumable_batch_stock_out()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py", "source_location": "L326"}, {"id": "api_production_list_conLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_models_models_py", "label": "models.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L1"}, {"id": "models_models_userrole", "label": "UserRole", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L10"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "models_models_sysuser", "label": "SysUser", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L22"}, {"id": "base", "label": "Base", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "models_models_sysrole", "label": "SysRole", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L48"}, {"id": "models_models_sysuserrole", "label": "SysUserRole", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L68"}, {"id": "models_models_sysrolemenu", "label": "SysRoleMenu", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L87"}, {"id": "models_models_sysroledatascopedetail", "label": "SysRoleDataScopeDetail", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L106"}, {"id": "models_models_sysmenu", "label": "SysMenu", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L126"}, {"id": "models_models_sysdept", "label": "SysDept", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L152"}, {"id": "models_models_systenant", "label": "SysTenant", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L175"}, {"id": "models_models_syssite", "label": "SysSite", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L195"}, {"id": "models_models_syspost", "label": "SysPost", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L221"}, {"id": "models_models_sysuserpost", "label": "SysUserPost", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L241"}, {"id": "models_models_bizitemunit", "label": "BizItemUnit", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L260"}, {"id": "models_models_bizexpresscompany", "label": "BizExpressCompany", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L279"}, {"id": "models_models_bizcustomer", "label": "BizCustomer", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L299"}, {"id": "models_models_bizcellsampletype", "label": "BizCellSampleType", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L322"}, {"id": "models_models_bizcollectionregion", "label": "BizCollectionRegion", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L345"}, {"id": "models_models_bizcollectionhospital", "label": "BizCollectionHospital", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L363"}, {"id": "models_models_bizcollectionprotocolno", "label": "BizCollectionProtocolNo", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L382"}, {"id": "models_models_bizbiznosequence", "label": "BizBizNoSequence", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L404"}, {"id": "models_models_bizsamplecode", "label": "BizSampleCode", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L424"}, {"id": "models_models_sysdicttype", "label": "SysDictType", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L452"}, {"id": "models_models_sysethnic", "label": "SysEthnic", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L471"}, {"id": "models_models_sysdictdata", "label": "SysDictData", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location"Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_schemas_qc_work_order_py", "label": "qc_work_order.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/qc_work_order.py", "source_location": "L1"}, {"id": "schemas_qc_work_order_outboundqcdetailapplication", "label": "OutboundQcDetailApplication", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/qc_work_order.py", "source_location": "L5"}, {"id": "basemodel", "label": "BaseModel", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "schemas_qc_work_order_outboundqcdetailpatient", "label": "OutboundQcDetailPatient", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/qc_work_order.py", "source_location": "L14"}, {"id": "schemas_qc_work_order_outboundqcdetailinfusionplan", "label": "OutboundQcDetailInfusionPlan", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/qc_work_order.py", "source_location": "L28"}, {"id": "schemas_qc_work_order_outboundqcdetailinfusionschedule", "label": "OutboundQcDetailInfusionSchedule", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/qc_work_order.py", "source_location": "L39"}, {"id": "schemas_qc_work_order_outboundqcdetailproduct", "label": "OutboundQcDetailProduct", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/qc_work_order.py", "source_location": "L51"}, {"id": "schemas_qc_work_order_outboundqcdetailresponse", "label": "OutboundQcDetailResponse", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/qc_work_order.py", "source_location": "L64"}, {"id": "schemas_qc_work_order_qcworkordercreatebody", "label": "QcWorkOrderCreateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/qc_work_order.py", "source_location": "L76"}, {"id": "schemas_qc_work_order_productionqcworkordercreatebody", "label": "ProductionQcWorkOrderCreateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/qc_work_order.py", "source_location": "L89"}, {"id": "schemas_qc_work_order_qcworkorderupdatebody", "label": "QcWorkOrderUpdateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/qc_work_order.py", "source_location": "L99"}, {"id": "schemas_qc_work_order_rationale_100", "label": "\u7f16\u8f91\u8d28\u68c0\u5de5\u5355\uff08\u5165\u5e93/\u51fa\u5e93\u53ef\u6539\u5907\u6ce8\u4e0e\u72b6\u6001\uff1b\u4e34\u65f6\u53ef\u542b\u8d28\u68c0\u5bf9\u8c61\uff09", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/qc_work_order.py", "source_location": "L100"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_qc_work_order_py", "target": "pydantic", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/qc_work_order.py", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_qc_work_order_py", "target": "typing", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/qc_work_order.py", "source_location": "L2", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_qc_work_order_py", "target": "schemas_qc_work_order_outboundqcdetailapplication", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/qc_work_order.py", "source_location": "L5", "weight": 1.0}, {"source": "schemas_qc_work_order_outboundqcdetailapplication", "target": "basemodel", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/qc_work_order.py", "source_location": "L5", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_qc_work_order_py", "target": "schemas_qc_work_order_outboundqcdetailpatient", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/qc_work_order.py", "source_location": "L14", "weight": 1.0}, {"source": "schemas_qc_work_order_outboundqcdetailpatient", "target": "basemodel", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/qc_work_order.py", "source_location": "L14", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_qc_work_order_py", "target": "schemas_qc_work_order_outboundqcdetailinfusionplan", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/qc_work_order.py", "source_location": "L28", "weight": 1.0}, {"source": "schemas_qc_work_order_outboundqcdetailinfusionplan", "target": "bLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_task_py", "label": "qc_task.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task.py", "source_location": "L1"}, {"id": "crud_qc_task_dec", "label": "_dec()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task.py", "source_location": "L36"}, {"id": "crud_qc_task_fmt_dt", "label": "_fmt_dt()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task.py", "source_location": "L42"}, {"id": "crud_qc_task_gen_task_no", "label": "_gen_task_no()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task.py", "source_location": "L46"}, {"id": "crud_qc_task_conclusion_label", "label": "_conclusion_label()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task.py", "source_location": "L59"}, {"id": "crud_qc_task_task_to_dict", "label": "_task_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task.py", "source_location": "L64"}, {"id": "crud_qc_task_task_work_order_context", "label": "_task_work_order_context()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task.py", "source_location": "L97"}, {"id": "crud_qc_task_enrich_tasks_reception_fields", "label": "_enrich_tasks_reception_fields()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task.py", "source_location": "L134"}, {"id": "crud_qc_task_task_row_to_dict", "label": "_task_row_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task.py", "source_location": "L242"}, {"id": "crud_qc_task_create_task_for_line", "label": "create_task_for_line()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task.py", "source_location": "L253"}, {"id": "crud_qc_task_list_tasks", "label": "list_tasks()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task.py", "source_location": "L298"}, {"id": "crud_qc_task_list_mine_tasks", "label": "list_mine_tasks()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task.py", "source_location": "L379"}, {"id": "crud_qc_task_assert_task_assignee", "label": "assert_task_assignee()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task.py", "source_location": "L470"}, {"id": "crud_qc_task_list_tasks_by_work_order", "label": "list_tasks_by_work_order()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task.py", "source_location": "L477"}, {"id": "crud_qc_task_get_task", "label": "get_task()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task.py", "source_location": "L495"}, {"id": "crud_qc_task_get_task_detail", "label": "get_task_detail()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task.py", "source_location": "L504"}, {"id": "crud_qc_task_start_task", "label": "start_task()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task.py", "source_location": "L537"}, {"id": "crud_qc_task_set_task_remark", "label": "_set_task_remark()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task.py", "source_location": "L554"}, {"id": "crud_qc_task_complete_task", "label": "complete_task()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task.py", "source_location": "L558"}, {"id": "crud_qc_task_apply_task_result", "label": "apply_task_result()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task.py", "source_location": "L602"}, {"id": "crud_qc_task_update_task_attachments", "label": "update_task_attachments()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task.py", "source_location": "L654"}, {"id": "crud_qc_task_create_qc_task_manual", "label": "create_qc_task_manual()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task.py", "source_location": "L684"}, {"id": "crud_qc_task_delete_qc_task", "label": "delete_qc_task()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task.py", "source_location": "L733"}, {"id": "crud_qc_task_rationale_613", "label": "\u4fdd\u5b58\u68c0\u6d4b\u7ed3\u679c\u4e0e\u7ed3\u8bba\uff1b\u4efb\u52a1\u672a\u5b8c\u6210\u65f6\u987a\u5e26\u6807\u8bb0\u4e3a\u5df2\u5b8c\u6210\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task.py", "source_location": "L613"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_taLine truncated
+22
View File
@@ -1355,6 +1355,7 @@ class BizQcWorkOrder(Base):
cell_product_id = Column(String(64), nullable=True, comment="质检对象-细胞产品ID")
production_work_order_id = Column(String(64), nullable=True, comment="关联生产工单ID(生产过程触发时)")
production_task_id = Column(String(64), nullable=True, comment="关联生产任务ID(可选)")
production_step_output_id = Column(String(64), nullable=True, comment="关联生产任务步骤产出物ID(生产过程触发时)")
work_order_status = Column(String(16), nullable=False, default="dpz", comment="工单状态")
has_lab = Column(String(1), nullable=False, default="0", comment="含实验室自检")
has_third_party = Column(String(1), nullable=False, default="0", comment="含第三方检测")
@@ -2739,8 +2740,10 @@ class BizProductionTaskStep(Base):
actual_duration = Column(String(100), nullable=True, comment="实际耗时")
sop_ids = Column(String(1000), nullable=True, comment="关联SOP文档id(逗号分隔)")
step_status = Column(String(16), nullable=True, default="dzx", comment="步骤状态 dzx待执行 jxz执行中 ywc已完成")
planned_start_time = Column(DateTime, nullable=True, comment="计划开始时间")
started_time = Column(DateTime, nullable=True, comment="开始时间")
completed_time = Column(DateTime, nullable=True, comment="完成时间")
completed_by = Column(String(64), nullable=True, comment="完成步骤的用户ID")
tenant_no = Column(String(64), nullable=False, default="zkxw", comment="租户编号")
order_num = Column(Integer, nullable=True, comment="排序号")
remark = Column(String(512), nullable=True, comment="备注")
@@ -2752,6 +2755,25 @@ class BizProductionTaskStep(Base):
del_flag = Column(SmallInteger, nullable=False, default=0, comment="删除标记")
class BizProductionTaskStepAssignee(Base):
"""生产任务步骤负责人 biz_production_task_step_assignee"""
__tablename__ = "biz_production_task_step_assignee"
id = Column(String(64), primary_key=True, comment="主键")
step_id = Column(String(64), nullable=False, comment="生产任务步骤ID")
user_id = Column(String(64), nullable=False, comment="负责人用户ID")
tenant_no = Column(String(64), nullable=False, default="zkxw", comment="租户编号")
order_num = Column(Integer, nullable=True, default=0, comment="展示顺序")
remark = Column(String(512), nullable=True, comment="备注")
create_by = Column(String(64), nullable=True, comment="创建人id")
create_dept_id = Column(String(64), nullable=False, default="", comment="创建人部门ID")
create_time = Column(DateTime, nullable=False, default=datetime.now, comment="创建时间")
update_by = Column(String(64), nullable=True, comment="更新人id")
update_time = Column(DateTime, nullable=False, default=datetime.now, onupdate=datetime.now, comment="更新时间")
del_flag = Column(SmallInteger, nullable=False, default=0, comment="删除标记")
class BizProductionTaskStepReagent(Base):
"""生产任务步骤试剂 biz_production_task_step_reagent"""
+13 -1
View File
@@ -6,7 +6,7 @@ from datetime import datetime
class ProductionTaskCreate(BaseModel):
model_config = ConfigDict(populate_by_name=True)
work_order_id: str
assignee_user_id: str = Field(..., alias="assigneeUserId")
assignee_user_id: Optional[str] = Field(None, alias="assigneeUserId")
task_type: Optional[str] = 'cell_storage'
task_name: Optional[str] = None
route_id: Optional[str] = None
@@ -140,3 +140,15 @@ class TaskStepReorderBody(BaseModel):
model_config = ConfigDict(populate_by_name=True)
task_id: str = Field(..., alias="taskId")
step_ids: List[str] = Field(..., alias="stepIds")
class ProductionTaskStepDispatchItem(BaseModel):
model_config = ConfigDict(populate_by_name=True)
step_id: str = Field(..., alias="stepId")
assignee_user_ids: List[str] = Field(..., alias="assigneeUserIds", min_length=1)
planned_start_time: Optional[datetime] = Field(None, alias="plannedStartTime")
class ProductionTaskStepDispatchBody(BaseModel):
model_config = ConfigDict(populate_by_name=True)
steps: List[ProductionTaskStepDispatchItem] = Field(..., min_length=1)
+3 -2
View File
@@ -90,8 +90,9 @@ class ProductionQcWorkOrderCreateBody(BaseModel):
"""生产过程派发质检工单"""
model_config = ConfigDict(populate_by_name=True)
cell_product_id: str = Field(..., alias="cellProductId", description="细胞产品ID")
production_task_id: Optional[str] = Field(None, alias="productionTaskId", description="关联生产任务ID(可选)")
production_step_output_id: str = Field(
..., alias="productionStepOutputId", description="步骤产出物ID"
)
remark: Optional[str] = Field(None, description="备注")
+23 -5
View File
@@ -1,7 +1,6 @@
"""库位节点编号路径与储物格完整编号计算、快照级联刷新。"""
from __future__ import annotations
import re
from datetime import datetime
from typing import Dict, List, Optional, Union
@@ -14,15 +13,32 @@ from app.models.models import (
BizStorageFacilitySlot,
)
_NODE_CODE_PATTERN = re.compile(r"^[A-Za-z0-9]+$")
SLOT_LAYOUT_ROW_STRIDE = 1000
def encode_slot_layout_order(row: int, col: int) -> int:
"""将货架行列位置编码为 order_num(row*1000+col)。"""
row_i = int(row)
col_i = int(col)
if row_i < 1 or col_i < 1:
raise ValueError("行列序号必须从 1 开始")
if col_i >= SLOT_LAYOUT_ROW_STRIDE:
raise ValueError(f"列数不能超过 {SLOT_LAYOUT_ROW_STRIDE - 1}")
return row_i * SLOT_LAYOUT_ROW_STRIDE + col_i
def decode_slot_layout_order(order_num: int) -> tuple[int, int]:
"""解析 order_num 中的行列位置;非布局编码时返回 (0, order)。"""
order = int(order_num or 0)
if order >= SLOT_LAYOUT_ROW_STRIDE:
return order // SLOT_LAYOUT_ROW_STRIDE, order % SLOT_LAYOUT_ROW_STRIDE
return 0, order
def validate_node_code(node_code: str) -> str:
code = (node_code or "").strip()
if not code:
raise ValueError("节点编号不能为空")
if not _NODE_CODE_PATTERN.match(code):
raise ValueError("节点编号只能包含字母和数字,不能包含连字符")
return ""
if len(code) > 32:
raise ValueError("节点编号长度不能超过 32 个字符")
return code
@@ -38,6 +54,8 @@ def assert_sibling_node_code_unique(
) -> None:
pid = (parent_id or "0").strip() or "0"
code = validate_node_code(node_code)
if not code:
return
q = db.query(BizStorageFacility).filter(
BizStorageFacility.parent_id == pid,
BizStorageFacility.node_code == code,
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+108 -160
View File
@@ -1,11 +1,11 @@
# Graph Report - app (2026-06-24)
# Graph Report - app (2026-06-25)
## Corpus Check
- Large corpus: 210 files · ~249,301 words. Semantic extraction will be expensive (many Claude tokens). Consider running on a subfolder, or use --no-semantic to run AST-only.
- Large corpus: 211 files · ~251,177 words. Semantic extraction will be expensive (many Claude tokens). Consider running on a subfolder, or use --no-semantic to run AST-only.
## Summary
- 4251 nodes · 22373 edges · 53 communities detected
- Extraction: 33% EXTRACTED · 67% INFERRED · 0% AMBIGUOUS · INFERRED: 15072 edges (avg confidence: 0.6)
- 4285 nodes · 22669 edges · 43 communities detected
- Extraction: 32% EXTRACTED · 68% INFERRED · 0% AMBIGUOUS · INFERRED: 15304 edges (avg confidence: 0.59)
- Token cost: 0 input · 0 output
## Community Hubs (Navigation)
@@ -46,108 +46,98 @@
- [[_COMMUNITY_Community 34|Community 34]]
- [[_COMMUNITY_Community 35|Community 35]]
- [[_COMMUNITY_Community 36|Community 36]]
- [[_COMMUNITY_Community 37|Community 37]]
- [[_COMMUNITY_Community 38|Community 38]]
- [[_COMMUNITY_Community 39|Community 39]]
- [[_COMMUNITY_Community 40|Community 40]]
- [[_COMMUNITY_Community 41|Community 41]]
- [[_COMMUNITY_Community 42|Community 42]]
- [[_COMMUNITY_Community 43|Community 43]]
- [[_COMMUNITY_Community 44|Community 44]]
- [[_COMMUNITY_Community 45|Community 45]]
- [[_COMMUNITY_Community 46|Community 46]]
- [[_COMMUNITY_Community 49|Community 49]]
- [[_COMMUNITY_Community 50|Community 50]]
- [[_COMMUNITY_Community 51|Community 51]]
- [[_COMMUNITY_Community 52|Community 52]]
- [[_COMMUNITY_Community 53|Community 53]]
- [[_COMMUNITY_Community 54|Community 54]]
## God Nodes (most connected - your core abstractions)
1. `success_response()` - 652 edges
2. `error_response()` - 538 edges
3. `SysUser` - 458 edges
4. `BizProductionMaterialUsage` - 298 edges
5. `BizProductionMaterial` - 297 edges
6. `BizQcTaskResult` - 296 edges
7. `BizQcMaterialUsage` - 295 edges
8. `BizQcMaterial` - 286 edges
9. `BizProductionMaterialStockBatch` - 285 edges
10. `BizMaterialUsage` - 282 edges
1. `success_response()` - 653 edges
2. `error_response()` - 539 edges
3. `SysUser` - 462 edges
4. `BizProductionMaterial` - 300 edges
5. `BizProductionMaterialUsage` - 300 edges
6. `BizQcTaskResult` - 299 edges
7. `BizQcMaterialUsage` - 297 edges
8. `BizQcMaterial` - 288 edges
9. `BizProductionMaterialStockBatch` - 287 edges
10. `BizMaterialUsage` - 284 edges
## Surprising Connections (you probably didn't know these)
- `统一记录 API 请求/响应出入参的中间件` --uses--> `SysApiRequestLog` [INFERRED]
core/api_request_log_middleware.py → models/models.py
- `get_menu_list()` --calls--> `success_response()` [INFERRED]
api/menu.py → core/response.py
- `list_my_qc_material_usages()` --calls--> `strip_row_fields()` [INFERRED]
api/qc.py → core/menu_guard.py
- `中试服务申请单管理 API(申请单、患者信息、输注方案、输注计划、出库联动)` --uses--> `SysUser` [INFERRED]
api/pilot_application.py → models/models.py
- `中试服务交付管理 API(交付信息、交付物料、交付方式)` --uses--> `SysUser` [INFERRED]
api/pilot_delivery.py → models/models.py
- `保留匹配节点及其全部上级,供关键词检索后仍能正确组树。` --uses--> `SysDept` [INFERRED]
crud/dept.py → models/models.py
- `中试服务体检管理 API(体检信息、预约项目、实际项目)` --uses--> `SysUser` [INFERRED]
api/pilot_checkup.py → models/models.py
## Communities
### Community 0 - "Community 0"
Cohesion: 0.01
Nodes (804): attachment_download_url(), create_dept(), create_dict_data(), create_dict_type(), create_express_company(), create_item_unit(), create_menu(), create_post() (+796 more)
Nodes (862): attachment_download_url(), create_dept(), create_dict_data(), create_dict_type(), create_express_company(), create_item_unit(), create_menu(), create_post() (+854 more)
### Community 1 - "Community 1"
Cohesion: 0.01
Nodes (609): AttachmentDownloadUrlBody, DeptCreateBody, DeptUpdateBody, DictDataCreateBody, DictDataUpdateBody, DictTypeCreateBody, DictTypeUpdateBody, ExpressCompanyCreateBody (+601 more)
Nodes (605): AttachmentDownloadUrlBody, DeptCreateBody, DeptUpdateBody, DictDataCreateBody, DictDataUpdateBody, DictTypeCreateBody, DictTypeUpdateBody, ExpressCompanyCreateBody (+597 more)
### Community 2 - "Community 2"
Cohesion: 0.06
Nodes (519): 细胞存储 — 采集协议单 / 审核 / 财务确认 / 分期预警 API, If mother info exists but no customerId, create a customer record and link it., 保险公司及险种价格管理 API(保险公司 + 开票信息 + 险种价格), 审批意见:前端传 commentText,兼容 comment。, TaskActionBody, Base, 细胞产品 CRUD(biz_cell_product), 解析任务样本投入物,返回祖先链单段(样本终止节点)。 (+511 more)
Cohesion: 0.01
Nodes (432): generate_uuid(), ApiRequestLogCRUD, delete(), get_multi_with_filters(), create_cell_sample_type(), get_cell_sample_type(), list_cell_sample_types(), 细胞样本类型 CRUD(biz_cell_sample_type) (+424 more)
### Community 3 - "Community 3"
Cohesion: 0.01
Nodes (312): _normalize_date_bound(), _persist_protocol_sub_resources(), list_ethnics_for_select(), apply_data_scope(), _get_child_dept_ids(), get_user_dept_id(), _resolve_user_role_scopes(), format_date() (+304 more)
Cohesion: 0.08
Nodes (367): 环境检测 — 检测项维护 + 登记管理 API, 保险公司及险种价格管理 API(保险公司 + 开票信息 + 险种价格), AccessLogCreateBody, AccessLogUpdateBody, CleanRoomCreateBody, CleanRoomUpdateBody, ExhibitionAppointmentCreateBody, ExhibitionAppointmentUpdateBody (+359 more)
### Community 4 - "Community 4"
Cohesion: 0.01
Nodes (288): generate_uuid(), create_cell_sample_type(), get_cell_sample_type(), list_cell_sample_types(), 细胞样本类型 CRUD(biz_cell_sample_type), _row_to_dict(), soft_delete_cell_sample_type(), type_code_exists() (+280 more)
Nodes (300): _normalize_date_bound(), _persist_protocol_sub_resources(), list_ethnics_for_select(), apply_data_scope(), _get_child_dept_ids(), get_user_dept_id(), _resolve_user_role_scopes(), format_date() (+292 more)
### Community 5 - "Community 5"
Cohesion: 0.02
Nodes (214): batch_discard(), batch_options(), _batch_row_to_dict(), batch_stock_in(), batch_stock_out(), _create_batch_and_usage(), delete_batch(), _discard_batch() (+206 more)
Cohesion: 0.07
Nodes (231): CellSampleTypeCreateBody, CellSampleTypeUpdateBody, CollectionRegionCreateBody, CollectionRegionUpdateBody, CustomerCreateBody, CustomerUpdateBody, HospitalCreateBody, HospitalUpdateBody (+223 more)
### Community 6 - "Community 6"
Cohesion: 0.02
Nodes (116): get_designer_data(), _error_json(), lifespan(), 数据库异常:返回 JSON 并保留 CORS 头,避免浏览器只报跨域错误, 未捕获异常:统一 JSON 500,确保跨域场景下仍有 Access-Control-Allow-Origin, sqlalchemy_exception_handler(), unhandled_exception_handler(), BaseHTTPMiddleware (+108 more)
Nodes (191): eq_unicode(), like_unicode(), MySQL 字符串比较/模糊匹配时统一排序规则,避免 Illegal mix of collations。, 跨表字符串等值比较(JOIN / WHERE)时统一为 utf8mb4_unicode_ci。, 将列显式设为 utf8mb4_unicode_ci。, unicode_ci(), batch_review(), _dec() (+183 more)
### Community 7 - "Community 7"
Cohesion: 0.02
Nodes (165): api_save_application(), get_protocol_nos_used_in_protocols(), create_dict_type(), dict_type_code_exists(), get_dict_type(), list_dict_types(), 字典类型 CRUD(sys_dict_type), _row_to_dict() (+157 more)
Nodes (167): _code_exists(), create_material(), _dec(), _fmt_dt(), get_material(), get_material_entity(), get_my_usages(), _is_enabled_status() (+159 more)
### Community 8 - "Community 8"
Cohesion: 0.03
Nodes (164): eq_unicode(), like_unicode(), MySQL 字符串比较/模糊匹配时统一排序规则,避免 Illegal mix of collations。, 跨表字符串等值比较(JOIN / WHERE)时统一为 utf8mb4_unicode_ci。, 将列显式设为 utf8mb4_unicode_ci。, unicode_ci(), batch_review(), _dec() (+156 more)
Cohesion: 0.02
Nodes (110): get_designer_data(), _error_json(), 数据库异常:返回 JSON 并保留 CORS 头,避免浏览器只报跨域错误, 未捕获异常:统一 JSON 500,确保跨域场景下仍有 Access-Control-Allow-Origin, sqlalchemy_exception_handler(), unhandled_exception_handler(), BaseHTTPMiddleware, parse_admin_request_json() (+102 more)
### Community 9 - "Community 9"
Cohesion: 0.05
Nodes (93): AccessLogCreateBody, AccessLogUpdateBody, CleanRoomCreateBody, CleanRoomUpdateBody, ExhibitionAppointmentCreateBody, ExhibitionAppointmentUpdateBody, OccupancyCreateBody, 运营支撑 API(洁净室、进出记录、仓储设施、储位、展厅预约) (+85 more)
Cohesion: 0.02
Nodes (135): get_role_menus(), list_storage_slots(), get_protocol_nos_used_in_protocols(), create_dict_type(), dict_type_code_exists(), get_dict_type(), list_dict_types(), 字典类型 CRUD(sys_dict_type) (+127 more)
### Community 10 - "Community 10"
Cohesion: 0.05
Nodes (75): batch_delete_research_donations(), change_donation_status(), convert_from_storage(), convert_to_storage(), create_research_donation(), delete_research_donation(), _get_current_user_id(), get_research_donation_detail() (+67 more)
Cohesion: 0.04
Nodes (75): list_roles_for_select(), _avatar_url_field(), _build_current_user_payload(), _build_user_info_payload(), change_current_user_password(), get_current_user_profile(), _jwt_user_payload(), logout() (+67 more)
### Community 11 - "Community 11"
Cohesion: 0.36
Nodes (67): BatchStockInBody, BatchStockInItem, BatchStockOutBody, BatchStockOutItem, ConsumableCreateBody, ConsumablePackCreateBody, ConsumablePackUpdateBody, ConsumableUpdateBody (+59 more)
Cohesion: 0.08
Nodes (60): batch_delete_research_donations(), change_donation_status(), convert_from_storage(), convert_to_storage(), create_research_donation(), delete_research_donation(), _get_current_user_id(), get_research_donation_detail() (+52 more)
### Community 12 - "Community 12"
Cohesion: 0.06
Nodes (52): _avatar_url_field(), _build_user_info_payload(), _jwt_user_payload(), logout(), 公共接口(Web 登录、刷新 Token、登出;用户模型对齐 docs/sql/zkxw.sql 基线), Web 端登录用 RSA 公钥(PKCS#1 v1.5,与 JSEncrypt 一致), Web 端登录:账号为 sys_user.user_no,密码为 sys_user.password(bcrypt)。, 登出:吊销 Redis 会话(若已配置 REDIS_HOST)。 (+44 more)
Cohesion: 0.07
Nodes (57): _ancestor_link_from_cell_product_input(), _ancestor_sample_chain_link(), _apply_cell_product_status_change(), _build_ancestor_chain(), _build_descendant_chain(), _build_facility_path_label(), build_split_product_codes(), create_cell_product() (+49 more)
### Community 13 - "Community 13"
Cohesion: 0.08
Nodes (53): get_next_cell_product_code(), _ancestor_link_from_cell_product_input(), _ancestor_sample_chain_link(), _apply_cell_product_status_change(), _build_ancestor_chain(), _build_descendant_chain(), _build_facility_path_label(), build_split_product_codes() (+45 more)
Nodes (58): api_save_application(), auto_generate_schedules(), complete_application(), create_application(), create_basic_experiment_sale_schedules(), create_infusion_plan(), create_schedule(), _create_schedule_products() (+50 more)
### Community 14 - "Community 14"
Cohesion: 0.11
Nodes (36): batch_create_sterilize_logs(), batch_create_wash_logs(), batch_discard_garments(), batch_soft_delete_garments(), _count_map(), create_garment(), create_sterilize_log(), create_wash_log() (+28 more)
Nodes (35): batch_create_sterilize_logs(), batch_create_wash_logs(), batch_discard_garments(), batch_soft_delete_garments(), _count_map(), create_garment(), create_sterilize_log(), create_wash_log() (+27 more)
### Community 15 - "Community 15"
Cohesion: 0.12
@@ -158,189 +148,147 @@ Cohesion: 0.19
Nodes (27): _amount_for_row(), _batch_map(), deduct_for_task(), _deduct_task_usage_legacy_batch(), _display_remark(), _fmt_date_val(), _linked_qc_task_no_map(), list_linked_task_usages() (+19 more)
### Community 17 - "Community 17"
Cohesion: 0.16
Nodes (19): get_by_schedule(), get_method(), _get_tenant_no(), list_by_application(), list_materials(), 中试服务交付管理 API(交付信息、交付物料、交付方式), soft_delete(), create_delivery() (+11 more)
Cohesion: 0.15
Nodes (17): create_customer(), get_customer(), id_card_taken(), list_customers(), 客户 CRUD(biz_customer), _row_to_dict(), soft_delete_customer(), update_customer() (+9 more)
### Community 18 - "Community 18"
Cohesion: 0.18
Nodes (18): company_code_exists(), create_company(), get_company(), list_companies(), list_company_options(), _row_to_dict(), soft_delete_company(), update_company() (+10 more)
Cohesion: 0.15
Nodes (18): delete_hospital(), create_collection_region(), create_hospital(), get_collection_region(), get_hospital(), list_hospitals(), soft_delete_collection_region(), soft_delete_hospital() (+10 more)
### Community 19 - "Community 19"
Cohesion: 0.2
Nodes (19): _code_exists(), create_material(), _dec(), _fmt_dt(), get_material(), get_material_entity(), get_my_usages(), _is_enabled_status() (+11 more)
Cohesion: 0.22
Nodes (14): batch_generate_sample_codes(), batch_issue_sample_codes(), batch_revoke_sample_codes(), create_sample_code(), get_sample_code(), list_sample_codes(), _parse_issued_time(), 样本编码池 CRUD(biz_sample_code) (+6 more)
### Community 20 - "Community 20"
Cohesion: 0.26
Nodes (12): batch_issue_sample_codes(), batch_revoke_sample_codes(), create_sample_code(), get_sample_code(), list_sample_codes(), _parse_issued_time(), 样本编码池 CRUD(biz_sample_code), _resolve_issued_user_display_name() (+4 more)
### Community 21 - "Community 21"
Cohesion: 0.23
Nodes (12): create_collection_region(), create_hospital(), get_collection_region(), get_hospital(), _hospital_to_dict(), list_hospitals(), 按医院名称模糊检索,返回医院及所属采集地区信息。, search_hospitals_by_keyword() (+4 more)
### Community 22 - "Community 22"
Cohesion: 0.24
Nodes (9): 微信消息通知服务 支持微信小程序模板消息推送 使用方式: from app.core.wechat_message import Wech, 发送模板消息 Args: openid: 用户openid templ, send_delivery_notice(), send_delivery_warning(), send_order_status(), send_payment_success(), send_refund_result(), send_urge_notification() (+1 more)
### Community 21 - "Community 21"
Cohesion: 0.23
Nodes (12): Config, ProductBase, ProductCategoryBase, ProductCategoryCreate, ProductCategoryResponse, ProductCreate, ProductResponse, ProductUnitBase (+4 more)
### Community 22 - "Community 22"
Cohesion: 0.24
Nodes (12): appointment_no_exists(), create_exhibition_appointment(), _format_visit_date(), _generate_appointment_no(), get_exhibition_appointment(), list_exhibition_appointments(), 展览预约 CRUD(biz_exhibition_appointment), 生成预约编号:YYYYMMDD + 6位序号(如 20260515000001) (+4 more)
### Community 23 - "Community 23"
Cohesion: 0.31
Nodes (11): create_equipment(), _dec(), equipment_code_exists(), equipment_is_referenced(), get_equipment(), list_equipments(), _norm_status(), _parse_date() (+3 more)
### Community 24 - "Community 24"
Cohesion: 0.27
Nodes (11): create_consumable_pack(), get_consumable_pack(), get_pack_detail(), _item_row_to_dict(), list_all_packs(), list_consumable_packs(), pack_code_exists(), 耗材包 CRUD(biz_consumable_pack + biz_consumable_pack_item) (+3 more)
### Community 25 - "Community 25"
Cohesion: 0.24
Nodes (11): deduct_for_step(), _display_remark(), list_stock_logs(), _parse_receiver_purpose_from_remark(), 试剂变动记录 CRUD(biz_reagent_stock_log), 撤销步骤此前 step_save 扣减,便于重新填报。, 扣减试剂库存并生成变动记录。调用方控制事务,本函数不 commit。, _resolve_receiver_purpose() (+3 more)
### Community 26 - "Community 26"
Cohesion: 0.32
Nodes (11): create_env_detect_record(), _generate_record_no(), get_env_detect_record(), _join_csv(), list_env_detect_records(), _resolve_inspector_snapshots(), _resolve_item_snapshot(), _row_to_dict() (+3 more)
### Community 27 - "Community 27"
Cohesion: 0.24
Nodes (11): deduct_for_step(), _display_remark(), list_stock_logs(), _parse_receiver_purpose_from_remark(), 耗材变动记录 CRUD(biz_consumable_stock_log), 撤销步骤此前 step_save 扣减,便于重新填报。, 扣减耗材库存并生成变动记录。调用方控制事务,本函数不 commit。, _resolve_receiver_purpose() (+3 more)
### Community 28 - "Community 28"
Cohesion: 0.27
Nodes (11): _build_tree(), _cascade_soft_delete(), _collect_with_ancestors(), create_dept(), get_dept(), list_depts(), list_depts_tree(), 保留匹配节点及其全部上级,供关键词检索后仍能正确组树。 (+3 more)
### Community 29 - "Community 29"
### Community 24 - "Community 24"
Cohesion: 0.24
Nodes (11): deduct_for_step(), _display_remark(), list_stock_logs(), _parse_receiver_purpose_from_remark(), 试剂变动记录 CRUD(biz_reagent_stock_log), 撤销步骤此前 step_save 扣减,便于重新填报。, 扣减试剂库存并生成变动记录。调用方控制事务,本函数不 commit。, _resolve_receiver_purpose() (+3 more)
### Community 25 - "Community 25"
Cohesion: 0.32
Nodes (11): create_env_detect_record(), _generate_record_no(), get_env_detect_record(), _join_csv(), list_env_detect_records(), _resolve_inspector_snapshots(), _resolve_item_snapshot(), _row_to_dict() (+3 more)
### Community 26 - "Community 26"
Cohesion: 0.24
Nodes (11): deduct_for_step(), _display_remark(), list_stock_logs(), _parse_receiver_purpose_from_remark(), 耗材变动记录 CRUD(biz_consumable_stock_log), 撤销步骤此前 step_save 扣减,便于重新填报。, 扣减耗材库存并生成变动记录。调用方控制事务,本函数不 commit。, _resolve_receiver_purpose() (+3 more)
### Community 27 - "Community 27"
Cohesion: 0.29
Nodes (9): _build_tree(), _cascade_soft_delete(), create_menu(), get_menu(), list_menus(), list_menus_tree(), _row_to_dict(), soft_delete_menu() (+1 more)
### Community 30 - "Community 30"
Cohesion: 0.27
Nodes (8): create_customer(), get_customer(), id_card_taken(), list_customers(), 客户 CRUD(biz_customer), _row_to_dict(), soft_delete_customer(), update_customer()
### Community 31 - "Community 31"
### Community 28 - "Community 28"
Cohesion: 0.31
Nodes (8): create_express_company(), express_code_exists(), get_express_company(), list_express_companies(), 快递公司 CRUD(biz_express_company), _row_to_dict(), soft_delete_express_company(), update_express_company()
Nodes (8): create_sop_document(), get_sop_document(), list_sop_documents(), SOP文档 CRUD(biz_sop_document), _row_to_dict(), soft_delete_sop_document(), sop_code_exists(), update_sop_document()
### Community 32 - "Community 32"
### Community 29 - "Community 29"
Cohesion: 0.38
Nodes (9): create_check_item(), get_check_item(), item_code_exists(), list_active_check_items(), list_check_items(), _order_query(), _row_to_dict(), soft_delete_check_item() (+1 more)
### Community 33 - "Community 33"
Cohesion: 0.38
Nodes (9): create_env_detect_item(), get_env_detect_item(), item_code_exists(), list_active_env_detect_items(), list_env_detect_items(), _order_query(), _row_to_dict(), soft_delete_env_detect_item() (+1 more)
### Community 34 - "Community 34"
Cohesion: 0.31
Nodes (8): category_code_exists(), create_process_category(), get_process_category(), list_process_categories(), 工艺工序分类 CRUD(biz_process_category), _row_to_dict(), soft_delete_process_category(), update_process_category()
### Community 35 - "Community 35"
Cohesion: 0.31
Nodes (8): create_item_unit(), get_item_unit(), list_item_units(), 物品单位 CRUD(biz_item_unit), _row_to_dict(), soft_delete_item_unit(), unit_code_exists(), update_item_unit()
### Community 36 - "Community 36"
### Community 30 - "Community 30"
Cohesion: 0.22
Nodes (8): 菜单 code 权限守卫(V0.3:通过 sys_user_role → sys_role_menu → sys_menu.perms), 运营端角色守卫:admin / super_admin 可访问。, 接口菜单权限守卫(已统一放行 menuCode 校验,仅保留运营端登录)。, 接口菜单权限守卫(已统一放行 menuCode 校验,仅保留运营端登录)。, require_admin_or_super_admin(), require_any_menu_code(), require_menu_code(), strip_row_fields()
### Community 37 - "Community 37"
Cohesion: 0.36
Nodes (7): create_post(), get_post(), list_posts(), post_code_exists(), _row_to_dict(), soft_delete_post(), update_post()
### Community 38 - "Community 38"
Cohesion: 0.36
Nodes (7): create_tenant(), get_tenant(), list_tenants(), _row_to_dict(), soft_delete_tenant(), tenant_no_exists(), update_tenant()
### Community 39 - "Community 39"
Cohesion: 0.36
Nodes (7): create_site(), get_site(), list_sites(), _row_to_dict(), site_code_exists(), soft_delete_site(), update_site()
### Community 40 - "Community 40"
### Community 31 - "Community 31"
Cohesion: 0.29
Nodes (2): BaseModel, SoftDeleteModel
### Community 41 - "Community 41"
### Community 32 - "Community 32"
Cohesion: 0.38
Nodes (6): derive_legacy_status_from_three(), derive_three_status_from_legacy(), 阶段A兼容:从旧 status 推导三字段。 约束:旧 status 本身混合了支付/配送/订单生命周期,因此只能尽力推断; 若业务侧已有更精确, 阶段A兼容:从三字段反推旧 status(用于旧前端/旧筛选参数兼容)。, ThreeStatus, _to_decimal()
### Community 42 - "Community 42"
### Community 33 - "Community 33"
Cohesion: 0.5
Nodes (3): BaseSettings, Config, Settings
### Community 43 - "Community 43"
### Community 34 - "Community 34"
Cohesion: 0.5
Nodes (2): _auto_fill_create_dept_id(), ORM before_insert 钩子:当 create_dept_id 为空时,自动从请求上下文填充当前用户部门 ID
### Community 44 - "Community 44"
Cohesion: 0.67
Nodes (2): get_menu_list(), 运营端菜单接口(与 web-admin 前端静态路由 sqxp.ts 对齐,5 个一级菜单分组)
### Community 45 - "Community 45"
### Community 35 - "Community 35"
Cohesion: 1.0
Nodes (1): 请求级别的用户上下文变量 通过 contextvars 在请求处理链路中传递当前用户信息, 供 SQLAlchemy 事件监听器等底层机制使用,避免逐层手动传
### Community 46 - "Community 46"
### Community 36 - "Community 36"
Cohesion: 1.0
Nodes (1): API 路由包(骨架:公共鉴权 + 静态菜单 + 运营端占位)
### Community 49 - "Community 49"
### Community 39 - "Community 39"
Cohesion: 1.0
Nodes (1): 发送订单状态通知 Args: openid: 用户openid ord
### Community 50 - "Community 50"
### Community 40 - "Community 40"
Cohesion: 1.0
Nodes (1): 发送支付成功通知 Args: openid: 用户openid ord
### Community 51 - "Community 51"
### Community 41 - "Community 41"
Cohesion: 1.0
Nodes (1): 发送配送通知 Args: openid: 配送员openid task
### Community 52 - "Community 52"
### Community 42 - "Community 42"
Cohesion: 1.0
Nodes (1): 发送配送时效预警 Args: openid: 配送员openid ta
### Community 53 - "Community 53"
### Community 43 - "Community 43"
Cohesion: 1.0
Nodes (1): 发送退款结果通知 Args: openid: 用户openid ord
### Community 54 - "Community 54"
### Community 44 - "Community 44"
Cohesion: 1.0
Nodes (1): 发送催单通知给配送员 Args: deliver_openid: 配送员openid
## Knowledge Gaps
- **129 isolated node(s):** `刷新会话 TTL(refreshToken 续期;已登录用户每次 API 校验通过后滑动续期)`, `写入 refreshToken(用于 one-time 使用/吊销)`, `根据 token 获取当前用户(包含 Redis 会话校验;会话滑动续期)`, `登录凭据 RSA 解密(与前端 JSEncrypt PKCS#1 v1.5 兼容)。`, `加载 RSA 密钥对:优先环境变量,否则使用内置固定默认密钥。` (+124 more)
These have ≤1 connection - possible missing edges or undocumented components.
- **Thin community `Community 40`** (8 nodes): `BaseModel`, `.to_dict()`, `get_active()`, `base.py`, `SoftDeleteModel`, `.restore()`, `.soft_delete()`, `__tablename__()`
- **Thin community `Community 31`** (8 nodes): `BaseModel`, `.to_dict()`, `get_active()`, `base.py`, `SoftDeleteModel`, `.restore()`, `.soft_delete()`, `__tablename__()`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 43`** (4 nodes): `_auto_fill_create_dept_id()`, `get_db()`, `database.py`, `ORM before_insert 钩子:当 create_dept_id 为空时,自动从请求上下文填充当前用户部门 ID`
- **Thin community `Community 34`** (4 nodes): `_auto_fill_create_dept_id()`, `get_db()`, `database.py`, `ORM before_insert 钩子:当 create_dept_id 为空时,自动从请求上下文填充当前用户部门 ID`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 44`** (3 nodes): `get_menu_list()`, `menu.py`, `运营端菜单接口(与 web-admin 前端静态路由 sqxp.ts 对齐,5 个一级菜单分组)`
- **Thin community `Community 35`** (2 nodes): `context.py`, `请求级别的用户上下文变量 通过 contextvars 在请求处理链路中传递当前用户信息, 供 SQLAlchemy 事件监听器等底层机制使用,避免逐层手动传`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 45`** (2 nodes): `context.py`, `请求级别的用户上下文变量 通过 contextvars 在请求处理链路中传递当前用户信息, 供 SQLAlchemy 事件监听器等底层机制使用,避免逐层手动传`
- **Thin community `Community 36`** (2 nodes): `__init__.py`, `API 路由包(骨架:公共鉴权 + 静态菜单 + 运营端占位)`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 46`** (2 nodes): `__init__.py`, `API 路由包(骨架:公共鉴权 + 静态菜单 + 运营端占位)`
- **Thin community `Community 39`** (1 nodes): `发送订单状态通知 Args: openid: 用户openid ord`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 49`** (1 nodes): `发送订单状态通知 Args: openid: 用户openid ord`
- **Thin community `Community 40`** (1 nodes): `发送支付成功通知 Args: openid: 用户openid ord`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 50`** (1 nodes): `发送支付成功通知 Args: openid: 用户openid ord`
- **Thin community `Community 41`** (1 nodes): `发送配送通知 Args: openid: 配送员openid task`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 51`** (1 nodes): `发送配送通知 Args: openid: 配送员openid task`
- **Thin community `Community 42`** (1 nodes): `发送配送时效预警 Args: openid: 配送员openid ta`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 52`** (1 nodes): `发送配送时效预警 Args: openid: 配送员openid ta`
- **Thin community `Community 43`** (1 nodes): `发送退款结果通知 Args: openid: 用户openid ord`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 53`** (1 nodes): `发送退款结果通知 Args: openid: 用户openid ord`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 54`** (1 nodes): `发送催单通知给配送员 Args: deliver_openid: 配送员openid`
- **Thin community `Community 44`** (1 nodes): `发送催单通知给配送员 Args: deliver_openid: 配送员openid`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
## Suggested Questions
_Questions this graph is uniquely positioned to answer:_
- **Why does `SysUser` connect `Community 1` to `Community 0`, `Community 2`, `Community 3`, `Community 5`, `Community 6`, `Community 7`, `Community 8`, `Community 9`, `Community 10`, `Community 11`, `Community 12`, `Community 17`, `Community 20`, `Community 25`, `Community 27`?**
_High betweenness centrality (0.108) - this node is a cross-community bridge._
- **Why does `success_response()` connect `Community 0` to `Community 1`, `Community 3`, `Community 6`, `Community 7`, `Community 10`, `Community 12`, `Community 44`, `Community 13`, `Community 17`?**
_High betweenness centrality (0.068) - this node is a cross-community bridge._
- **Why does `generate_uuid()` connect `Community 4` to `Community 0`, `Community 1`, `Community 2`, `Community 3`, `Community 5`, `Community 6`, `Community 7`, `Community 8`, `Community 9`, `Community 10`, `Community 12`, `Community 13`, `Community 14`, `Community 16`, `Community 18`, `Community 19`, `Community 20`, `Community 21`, `Community 23`, `Community 24`, `Community 25`, `Community 26`, `Community 27`, `Community 28`, `Community 29`, `Community 30`, `Community 31`, `Community 32`, `Community 33`, `Community 34`, `Community 35`, `Community 37`, `Community 38`, `Community 39`?**
_High betweenness centrality (0.060) - this node is a cross-community bridge._
- **Are the 703 inferred relationships involving `str` (e.g. with `unhandled_exception_handler()` and `get_current_user_by_token()`) actually correct?**
_`str` has 703 INFERRED edges - model-reasoned connections that need verification._
- **Are the 651 inferred relationships involving `success_response()` (e.g. with `list_storage_release_reviews()` and `get_storage_release_review_detail()`) actually correct?**
_`success_response()` has 651 INFERRED edges - model-reasoned connections that need verification._
- **Are the 537 inferred relationships involving `error_response()` (e.g. with `_json_error_response()` and `list_storage_release_reviews()`) actually correct?**
_`error_response()` has 537 INFERRED edges - model-reasoned connections that need verification._
- **Are the 456 inferred relationships involving `SysUser` (e.g. with `数据权限过滤(V0.3:基于角色 data_scope 的查询级行级过滤) data_scope 值: 1 = 全部数据 2 = 本部门数据 3` and `从 SysUser 读取用户的 dept_id。`) actually correct?**
_`SysUser` has 456 INFERRED edges - model-reasoned connections that need verification._
- **Why does `SysUser` connect `Community 1` to `Community 0`, `Community 2`, `Community 3`, `Community 4`, `Community 5`, `Community 6`, `Community 7`, `Community 9`, `Community 10`, `Community 11`, `Community 19`, `Community 24`, `Community 26`?**
_High betweenness centrality (0.141) - this node is a cross-community bridge._
- **Why does `generate_uuid()` connect `Community 2` to `Community 0`, `Community 3`, `Community 4`, `Community 5`, `Community 6`, `Community 7`, `Community 8`, `Community 9`, `Community 10`, `Community 11`, `Community 12`, `Community 14`, `Community 16`, `Community 17`, `Community 18`, `Community 19`, `Community 22`, `Community 23`, `Community 24`, `Community 25`, `Community 26`, `Community 27`, `Community 28`, `Community 29`?**
_High betweenness centrality (0.076) - this node is a cross-community bridge._
- **Why does `success_response()` connect `Community 0` to `Community 1`, `Community 4`, `Community 8`, `Community 9`, `Community 10`, `Community 11`, `Community 13`, `Community 18`?**
_High betweenness centrality (0.061) - this node is a cross-community bridge._
- **Are the 707 inferred relationships involving `str` (e.g. with `unhandled_exception_handler()` and `get_current_user_by_token()`) actually correct?**
_`str` has 707 INFERRED edges - model-reasoned connections that need verification._
- **Are the 652 inferred relationships involving `success_response()` (e.g. with `list_storage_release_reviews()` and `get_storage_release_review_detail()`) actually correct?**
_`success_response()` has 652 INFERRED edges - model-reasoned connections that need verification._
- **Are the 538 inferred relationships involving `error_response()` (e.g. with `_json_error_response()` and `list_storage_release_reviews()`) actually correct?**
_`error_response()` has 538 INFERRED edges - model-reasoned connections that need verification._
- **Are the 460 inferred relationships involving `SysUser` (e.g. with `数据权限过滤(V0.3:基于角色 data_scope 的查询级行级过滤) data_scope 值: 1 = 全部数据 2 = 本部门数据 3` and `从 SysUser 读取用户的 dept_id。`) actually correct?**
_`SysUser` has 460 INFERRED edges - model-reasoned connections that need verification._
+4 -4
View File
@@ -57,12 +57,12 @@
</div>
<div id="legend"></div>
</div>
<div id="stats">4251 nodes &middot; 22373 edges &middot; 59 communities</div>
<div id="stats">4285 nodes &middot; 22669 edges &middot; 49 communities</div>
</div>
<script>
const RAW_NODES = [{"id": "init_py", "label": "__init__.py", "color": {"background": "#FF9DA7", "border": "#FF9DA7", "highlight": {"background": "#ffffff", "border": "#FF9DA7"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "__init__.py", "community": 47, "community_name": "Community 47", "source_file": "__init__.py", "file_type": "code", "degree": 0}, {"id": "main_py", "label": "main.py", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "main.py", "community": 6, "community_name": "Community 6", "source_file": "main.py", "file_type": "code", "degree": 7}, {"id": "app_main_lifespan", "label": "lifespan()", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "lifespan()", "community": 6, "community_name": "Community 6", "source_file": "main.py", "file_type": "code", "degree": 4}, {"id": "app_main_error_json", "label": "_error_json()", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "_error_json()", "community": 6, "community_name": "Community 6", "source_file": "main.py", "file_type": "code", "degree": 3}, {"id": "app_main_sqlalchemy_exception_handler", "label": "sqlalchemy_exception_handler()", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "sqlalchemy_exception_handler()", "community": 6, "community_name": "Community 6", "source_file": "main.py", "file_type": "code", "degree": 4}, {"id": "app_main_unhandled_exception_handler", "label": "unhandled_exception_handler()", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "unhandled_exception_handler()", "community": 6, "community_name": "Community 6", "source_file": "main.py", "file_type": "code", "degree": 5}, {"id": "app_main_mask_secret", "label": "_mask_secret()", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "_mask_secret()", "community": 6, "community_name": "Community 6", "source_file": "main.py", "file_type": "code", "degree": 1}, {"id": "app_main_root", "label": "root()", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "root()", "community": 6, "community_name": "Community 6", "source_file": "main.py", "file_type": "code", "degree": 1}, {"id": "app_main_health_check", "label": "health_check()", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "health_check()", "community": 6, "community_name": "Community 6", "source_file": "main.py", "file_type": "code", "degree": 1}, {"id": "app_main_rationale_81", "label": "\u6570\u636e\u5e93\u5f02\u5e38\uff1a\u8fd4\u56de JSON \u5e76\u4fdd\u7559 CORS \u5934\uff0c\u907f\u514d\u6d4f\u89c8\u5668\u53ea\u62a5\u8de8\u57df\u9519\u8bef", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "\u6570\u636e\u5e93\u5f02\u5e38\uff1a\u8fd4\u56de JSON \u5e76\u4fdd\u7559 CORS \u5934\uff0c\u907f\u514d\u6d4f\u89c8\u5668\u53ea\u62a5\u8de8\u57df\u9519\u8bef", "community": 6, "community_name": "Community 6", "source_file": "main.py", "file_type": "rationale", "degree": 3}, {"id": "app_main_rationale_92", "label": "\u672a\u6355\u83b7\u5f02\u5e38\uff1a\u7edf\u4e00 JSON 500\uff0c\u786e\u4fdd\u8de8\u57df\u573a\u666f\u4e0b\u4ecd\u6709 Access-Control-Allow-Origin", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "\u672a\u6355\u83b7\u5f02\u5e38\uff1a\u7edf\u4e00 JSON 500\uff0c\u786e\u4fdd\u8de8\u57df\u573a\u666f\u4e0b\u4ecd\u6709 Access-Control-Allow-Origin", "community": 6, "community_name": "Community 6", "source_file": "main.py", "file_type": "rationale", "degree": 3}, {"id": "tasks_init_py", "label": "__init__.py", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "__init__.py", "community": 6, "community_name": "Community 6", "source_file": "tasks/__init__.py", "file_type": "code", "degLine truncated
const RAW_EDGES = [{"from": "main_py", "to": "app_main_lifespan", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_py", "to": "app_main_error_json", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_py", "to": "app_main_sqlalchemy_exception_handler", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_py", "to": "app_main_unhandled_exception_handler", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_py", "to": "app_main_mask_secret", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_py", "to": "app_main_root", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_py", "to": "app_main_health_check", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_main_lifespan", "to": "core_login_crypto_ensure_initialized", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_lifespan", "to": "tasks_init_init_scheduled_tasks", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_lifespan", "to": "tasks_init_shutdown_scheduler", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_error_json", "to": "app_main_sqlalchemy_exception_handler", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_main_error_json", "to": "app_main_unhandled_exception_handler", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_main_sqlalchemy_exception_handler", "to": "app_main_rationale_81", "label": "rationale_for", "title": "rationale_for [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_main_sqlalchemy_exception_handler", "to": "exception", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_unhandled_exception_handler", "to": "app_main_rationale_92", "label": "rationale_for", "title": "rationale_for [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_main_unhandled_exception_handler", "to": "exception", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_unhandled_exception_handler", "to": "str", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_rationale_81", "to": "core_api_request_log_middleware_apirequestlogmiddleware", "label": "uses", "title": "uses [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_rationale_81", "to": "core_api_crypto_middleware_apicryptomiddleware", "label": "uses", "title": "uses [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_rationale_92", "to": "core_api_request_log_middleware_apirequestlogmiddleware", "label": "uses", "title": "uses [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_rationale_92", "to": "core_api_crypto_middleware_apicryptomiddleware", "label": "uses", "title": "uses [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "tasks_init_py", "to": "tasks_init_init_scheduled_tasks", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "tasks_init_py", "to": "tasks_init_shutdown_scheduler", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_redis_session_enabled", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_get_redis_client", "labeLine truncated
const LEGEND = [{"cid": 0, "color": "#4E79A7", "label": "Community 0", "count": 835}, {"cid": 1, "color": "#F28E2B", "label": "Community 1", "count": 648}, {"cid": 2, "color": "#E15759", "label": "Community 2", "count": 525}, {"cid": 3, "color": "#76B7B2", "label": "Community 3", "count": 355}, {"cid": 4, "color": "#59A14F", "label": "Community 4", "count": 317}, {"cid": 5, "color": "#EDC948", "label": "Community 5", "count": 228}, {"cid": 6, "color": "#B07AA1", "label": "Community 6", "count": 186}, {"cid": 7, "color": "#FF9DA7", "label": "Community 7", "count": 181}, {"cid": 8, "color": "#9C755F", "label": "Community 8", "count": 174}, {"cid": 9, "color": "#BAB0AC", "label": "Community 9", "count": 98}, {"cid": 10, "color": "#4E79A7", "label": "Community 10", "count": 88}, {"cid": 11, "color": "#F28E2B", "label": "Community 11", "count": 74}, {"cid": 12, "color": "#E15759", "label": "Community 12", "count": 61}, {"cid": 13, "color": "#76B7B2", "label": "Community 13", "count": 56}, {"cid": 14, "color": "#59A14F", "label": "Community 14", "count": 39}, {"cid": 15, "color": "#EDC948", "label": "Community 15", "count": 31}, {"cid": 16, "color": "#B07AA1", "label": "Community 16", "count": 28}, {"cid": 17, "color": "#FF9DA7", "label": "Community 17", "count": 22}, {"cid": 18, "color": "#9C755F", "label": "Community 18", "count": 20}, {"cid": 19, "color": "#BAB0AC", "label": "Community 19", "count": 20}, {"cid": 20, "color": "#4E79A7", "label": "Community 20", "count": 14}, {"cid": 21, "color": "#F28E2B", "label": "Community 21", "count": 14}, {"cid": 22, "color": "#E15759", "label": "Community 22", "count": 13}, {"cid": 23, "color": "#76B7B2", "label": "Community 23", "count": 13}, {"cid": 24, "color": "#59A14F", "label": "Community 24", "count": 13}, {"cid": 25, "color": "#EDC948", "label": "Community 25", "count": 12}, {"cid": 26, "color": "#B07AA1", "label": "Community 26", "count": 12}, {"cid": 27, "color": "#FF9DA7", "label": "Community 27", "count": 12}, {"cid": 28, "color": "#9C755F", "label": "Community 28", "count": 12}, {"cid": 29, "color": "#BAB0AC", "label": "Community 29", "count": 11}, {"cid": 30, "color": "#4E79A7", "label": "Community 30", "count": 11}, {"cid": 31, "color": "#F28E2B", "label": "Community 31", "count": 10}, {"cid": 32, "color": "#E15759", "label": "Community 32", "count": 10}, {"cid": 33, "color": "#76B7B2", "label": "Community 33", "count": 10}, {"cid": 34, "color": "#59A14F", "label": "Community 34", "count": 10}, {"cid": 35, "color": "#EDC948", "label": "Community 35", "count": 10}, {"cid": 36, "color": "#B07AA1", "label": "Community 36", "count": 9}, {"cid": 37, "color": "#FF9DA7", "label": "Community 37", "count": 9}, {"cid": 38, "color": "#9C755F", "label": "Community 38", "count": 9}, {"cid": 39, "color": "#BAB0AC", "label": "Community 39", "count": 9}, {"cid": 40, "color": "#4E79A7", "label": "Community 40", "count": 8}, {"cid": 41, "color": "#F28E2B", "label": "Community 41", "count": 7}, {"cid": 42, "color": "#E15759", "label": "Community 42", "count": 4}, {"cid": 43, "color": "#76B7B2", "label": "Community 43", "count": 4}, {"cid": 44, "color": "#59A14F", "label": "Community 44", "count": 3}, {"cid": 45, "color": "#EDC948", "label": "Community 45", "count": 2}, {"cid": 46, "color": "#B07AA1", "label": "Community 46", "count": 2}, {"cid": 47, "color": "#FF9DA7", "label": "Community 47", "count": 1}, {"cid": 48, "color": "#9C755F", "label": "Community 48", "count": 1}, {"cid": 49, "color": "#BAB0AC", "label": "Community 49", "count": 1}, {"cid": 50, "color": "#4E79A7", "label": "Community 50", "count": 1}, {"cid": 51, "color": "#F28E2B", "label": "Community 51", "count": 1}, {"cid": 52, "color": "#E15759", "label": "Community 52", "count": 1}, {"cid": 53, "color": "#76B7B2", "label": "Community 53", "count": 1}, {"cid": 54, "color": "#59A14F", "label": "Community 54", "count": 1}, {"cid": 55, "color": "#EDC948", "label": "Community 55", "count": 1}, {"cid": 56, "color": "#B07AA1", "label": "Community 56", "count": 1}, {"cid": 57, "color": "#FF9DA7", "label": "Community 57", "count": 1}, {"cid": 58, "color": "#9C755F", "label": "Community 58", "count": 1}];
const RAW_NODES = [{"id": "init_py", "label": "__init__.py", "color": {"background": "#FF9DA7", "border": "#FF9DA7", "highlight": {"background": "#ffffff", "border": "#FF9DA7"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "__init__.py", "community": 37, "community_name": "Community 37", "source_file": "__init__.py", "file_type": "code", "degree": 0}, {"id": "main_py", "label": "main.py", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "main.py", "community": 8, "community_name": "Community 8", "source_file": "main.py", "file_type": "code", "degree": 7}, {"id": "app_main_lifespan", "label": "lifespan()", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "lifespan()", "community": 10, "community_name": "Community 10", "source_file": "main.py", "file_type": "code", "degree": 4}, {"id": "app_main_error_json", "label": "_error_json()", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "_error_json()", "community": 8, "community_name": "Community 8", "source_file": "main.py", "file_type": "code", "degree": 3}, {"id": "app_main_sqlalchemy_exception_handler", "label": "sqlalchemy_exception_handler()", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "sqlalchemy_exception_handler()", "community": 8, "community_name": "Community 8", "source_file": "main.py", "file_type": "code", "degree": 4}, {"id": "app_main_unhandled_exception_handler", "label": "unhandled_exception_handler()", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "unhandled_exception_handler()", "community": 8, "community_name": "Community 8", "source_file": "main.py", "file_type": "code", "degree": 5}, {"id": "app_main_mask_secret", "label": "_mask_secret()", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "_mask_secret()", "community": 8, "community_name": "Community 8", "source_file": "main.py", "file_type": "code", "degree": 1}, {"id": "app_main_root", "label": "root()", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "root()", "community": 8, "community_name": "Community 8", "source_file": "main.py", "file_type": "code", "degree": 1}, {"id": "app_main_health_check", "label": "health_check()", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "health_check()", "community": 8, "community_name": "Community 8", "source_file": "main.py", "file_type": "code", "degree": 1}, {"id": "app_main_rationale_81", "label": "\u6570\u636e\u5e93\u5f02\u5e38\uff1a\u8fd4\u56de JSON \u5e76\u4fdd\u7559 CORS \u5934\uff0c\u907f\u514d\u6d4f\u89c8\u5668\u53ea\u62a5\u8de8\u57df\u9519\u8bef", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "\u6570\u636e\u5e93\u5f02\u5e38\uff1a\u8fd4\u56de JSON \u5e76\u4fdd\u7559 CORS \u5934\uff0c\u907f\u514d\u6d4f\u89c8\u5668\u53ea\u62a5\u8de8\u57df\u9519\u8bef", "community": 8, "community_name": "Community 8", "source_file": "main.py", "file_type": "rationale", "degree": 3}, {"id": "app_main_rationale_92", "label": "\u672a\u6355\u83b7\u5f02\u5e38\uff1a\u7edf\u4e00 JSON 500\uff0c\u786e\u4fdd\u8de8\u57df\u573a\u666f\u4e0b\u4ecd\u6709 Access-Control-Allow-Origin", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "\u672a\u6355\u83b7\u5f02\u5e38\uff1a\u7edf\u4e00 JSON 500\uff0c\u786e\u4fdd\u8de8\u57df\u573a\u666f\u4e0b\u4ecd\u6709 Access-Control-Allow-Origin", "community": 8, "community_name": "Community 8", "source_file": "main.py", "file_type": "rationale", "degree": 3}, {"id": "tasks_init_py", "label": "__init__.py", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "__init__.py", "community": 10, "community_name": "Community 10", "source_file": "tasks/__init__.py", "file_type": "code", Line truncated
const RAW_EDGES = [{"from": "main_py", "to": "app_main_lifespan", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_py", "to": "app_main_error_json", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_py", "to": "app_main_sqlalchemy_exception_handler", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_py", "to": "app_main_unhandled_exception_handler", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_py", "to": "app_main_mask_secret", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_py", "to": "app_main_root", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_py", "to": "app_main_health_check", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_main_lifespan", "to": "core_login_crypto_ensure_initialized", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_lifespan", "to": "tasks_init_init_scheduled_tasks", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_lifespan", "to": "tasks_init_shutdown_scheduler", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_error_json", "to": "app_main_sqlalchemy_exception_handler", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_main_error_json", "to": "app_main_unhandled_exception_handler", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_main_sqlalchemy_exception_handler", "to": "app_main_rationale_81", "label": "rationale_for", "title": "rationale_for [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_main_sqlalchemy_exception_handler", "to": "exception", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_unhandled_exception_handler", "to": "app_main_rationale_92", "label": "rationale_for", "title": "rationale_for [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_main_unhandled_exception_handler", "to": "exception", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_unhandled_exception_handler", "to": "str", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_rationale_81", "to": "core_api_request_log_middleware_apirequestlogmiddleware", "label": "uses", "title": "uses [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_rationale_81", "to": "core_api_crypto_middleware_apicryptomiddleware", "label": "uses", "title": "uses [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_rationale_92", "to": "core_api_request_log_middleware_apirequestlogmiddleware", "label": "uses", "title": "uses [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_rationale_92", "to": "core_api_crypto_middleware_apicryptomiddleware", "label": "uses", "title": "uses [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "tasks_init_py", "to": "tasks_init_init_scheduled_tasks", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "tasks_init_py", "to": "tasks_init_shutdown_scheduler", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_redis_session_enabled", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_get_redis_client", "labeLine truncated
const LEGEND = [{"cid": 0, "color": "#4E79A7", "label": "Community 0", "count": 899}, {"cid": 1, "color": "#F28E2B", "label": "Community 1", "count": 648}, {"cid": 2, "color": "#E15759", "label": "Community 2", "count": 488}, {"cid": 3, "color": "#76B7B2", "label": "Community 3", "count": 372}, {"cid": 4, "color": "#59A14F", "label": "Community 4", "count": 344}, {"cid": 5, "color": "#EDC948", "label": "Community 5", "count": 233}, {"cid": 6, "color": "#B07AA1", "label": "Community 6", "count": 202}, {"cid": 7, "color": "#FF9DA7", "label": "Community 7", "count": 179}, {"cid": 8, "color": "#9C755F", "label": "Community 8", "count": 177}, {"cid": 9, "color": "#BAB0AC", "label": "Community 9", "count": 151}, {"cid": 10, "color": "#4E79A7", "label": "Community 10", "count": 86}, {"cid": 11, "color": "#F28E2B", "label": "Community 11", "count": 64}, {"cid": 12, "color": "#E15759", "label": "Community 12", "count": 61}, {"cid": 13, "color": "#76B7B2", "label": "Community 13", "count": 60}, {"cid": 14, "color": "#59A14F", "label": "Community 14", "count": 38}, {"cid": 15, "color": "#EDC948", "label": "Community 15", "count": 31}, {"cid": 16, "color": "#B07AA1", "label": "Community 16", "count": 28}, {"cid": 17, "color": "#FF9DA7", "label": "Community 17", "count": 21}, {"cid": 18, "color": "#9C755F", "label": "Community 18", "count": 21}, {"cid": 19, "color": "#BAB0AC", "label": "Community 19", "count": 16}, {"cid": 20, "color": "#4E79A7", "label": "Community 20", "count": 13}, {"cid": 21, "color": "#F28E2B", "label": "Community 21", "count": 13}, {"cid": 22, "color": "#E15759", "label": "Community 22", "count": 13}, {"cid": 23, "color": "#76B7B2", "label": "Community 23", "count": 12}, {"cid": 24, "color": "#59A14F", "label": "Community 24", "count": 12}, {"cid": 25, "color": "#EDC948", "label": "Community 25", "count": 12}, {"cid": 26, "color": "#B07AA1", "label": "Community 26", "count": 12}, {"cid": 27, "color": "#FF9DA7", "label": "Community 27", "count": 11}, {"cid": 28, "color": "#9C755F", "label": "Community 28", "count": 10}, {"cid": 29, "color": "#BAB0AC", "label": "Community 29", "count": 10}, {"cid": 30, "color": "#4E79A7", "label": "Community 30", "count": 9}, {"cid": 31, "color": "#F28E2B", "label": "Community 31", "count": 8}, {"cid": 32, "color": "#E15759", "label": "Community 32", "count": 7}, {"cid": 33, "color": "#76B7B2", "label": "Community 33", "count": 4}, {"cid": 34, "color": "#59A14F", "label": "Community 34", "count": 4}, {"cid": 35, "color": "#EDC948", "label": "Community 35", "count": 2}, {"cid": 36, "color": "#B07AA1", "label": "Community 36", "count": 2}, {"cid": 37, "color": "#FF9DA7", "label": "Community 37", "count": 1}, {"cid": 38, "color": "#9C755F", "label": "Community 38", "count": 1}, {"cid": 39, "color": "#BAB0AC", "label": "Community 39", "count": 1}, {"cid": 40, "color": "#4E79A7", "label": "Community 40", "count": 1}, {"cid": 41, "color": "#F28E2B", "label": "Community 41", "count": 1}, {"cid": 42, "color": "#E15759", "label": "Community 42", "count": 1}, {"cid": 43, "color": "#76B7B2", "label": "Community 43", "count": 1}, {"cid": 44, "color": "#59A14F", "label": "Community 44", "count": 1}, {"cid": 45, "color": "#EDC948", "label": "Community 45", "count": 1}, {"cid": 46, "color": "#B07AA1", "label": "Community 46", "count": 1}, {"cid": 47, "color": "#FF9DA7", "label": "Community 47", "count": 1}, {"cid": 48, "color": "#9C755F", "label": "Community 48", "count": 1}, {"cid": 49, "color": "#BAB0AC", "label": "Community 49", "count": 0}, {"cid": 50, "color": "#4E79A7", "label": "Community 50", "count": 0}, {"cid": 51, "color": "#F28E2B", "label": "Community 51", "count": 0}, {"cid": 52, "color": "#E15759", "label": "Community 52", "count": 0}, {"cid": 53, "color": "#76B7B2", "label": "Community 53", "count": 0}, {"cid": 54, "color": "#59A14F", "label": "Community 54", "count": 0}, {"cid": 55, "color": "#EDC948", "label": "Community 55", "count": 0}, {"cid": 56, "color": "#B07AA1", "label": "Community 56", "count": 0}, {"cid": 57, "color": "#FF9DA7", "label": "Community 57", "count": 0}, {"cid": 58, "color": "#9C755F", "label": "Community 58", "count": 0}];
// HTML-escape helper — prevents XSS when injecting graph data into innerHTML
function esc(s) {
+19461 -15603
View File
File diff suppressed because it is too large. Load diff
+13 -12
View File
@@ -28,7 +28,7 @@
"/Users/wuzilong/work/00code/zkxw/backend/app/models/material.py": 1781427174.243315,
"/Users/wuzilong/work/00code/zkxw/backend/app/models/production_material.py": 1781427178.0074935,
"/Users/wuzilong/work/00code/zkxw/backend/app/models/qc_param.py": 1779022399.0175831,
"/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py": 1781747637.7400184,
"/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py": 1782351467.7127345,
"/Users/wuzilong/work/00code/zkxw/backend/app/models/cleanroom_garment.py": 1781347080.6165943,
"/Users/wuzilong/work/00code/zkxw/backend/app/models/__init__.py": 1781344070.5899634,
"/Users/wuzilong/work/00code/zkxw/backend/app/models/research_donation.py": 1780458012.8329656,
@@ -50,7 +50,7 @@
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/product_parameter_value.py": 1778578258.0980055,
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/product_review_media.py": 1778578258.0986822,
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/product_parameter.py": 1778578258.0977957,
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/production_task.py": 1781524096.5898407,
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/production_task.py": 1782351474.1300344,
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/product_detail_media.py": 1778578258.0973988,
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/product_banner.py": 1778578258.097102,
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/region.py": 1778578258.099429,
@@ -64,7 +64,7 @@
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/category_parameter.py": 1778578258.0944862,
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/common.py": 1778578258.094703,
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/unit.py": 1778578258.0996897,
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/qc_work_order.py": 1781245309.0806582,
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/qc_work_order.py": 1782350310.733416,
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/outbound_release_material.py": 1779827058.9935315,
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/product.py": 1778578258.0968297,
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/workflow.py": 1778633845.5937889,
@@ -74,8 +74,8 @@
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/leave.py": 1778666088.7680702,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_release_review.py": 1781536745.221194,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/equipment.py": 1781524075.5310335,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task.py": 1781589822.2906754,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/process_route.py": 1781524077.6406052,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task.py": 1782350322.6331227,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/process_route.py": 1782289879.5489628,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_code.py": 1780479191.5382729,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task_result.py": 1779331731.6720955,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/outbound_release_review.py": 1781603141.940902,
@@ -97,7 +97,7 @@
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/material_usage.py": 1781427187.4456425,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_material_usage.py": 1781513732.7796721,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/sop_document.py": 1779265721.9928973,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py": 1781583485.3504221,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py": 1782360197.8427687,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_third_party_item.py": 1779265661.482352,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/reagent.py": 1779512669.9847593,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/collection_protocol.py": 1782195334.7812605,
@@ -119,6 +119,7 @@
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/role.py": 1779265616.3701816,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_material_stock_batch.py": 1781690315.173275,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/material_stock_check.py": 1781427194.5434632,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task_step_assignee.py": 1782351472.9662378,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_sms_notification.py": 1781590614.9654794,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task_assignee.py": 1781233180.3766735,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/research_donation_sample_collection.py": 1781063441.5812025,
@@ -137,11 +138,11 @@
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_item.py": 1781145689.9525824,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/process_category.py": 1779265700.0606413,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/item_unit.py": 1779265653.7504313,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_work_order.py": 1781698777.252274,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_work_order.py": 1782350337.3184292,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/outbound_handover_inventory.py": 1781598316.476379,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py": 1781145773.959546,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/outbound_release_material.py": 1781612877.0137436,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/process.py": 1781524075.6578302,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/process.py": 1782289878.7772164,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/pilot_service.py": 1781599314.0124536,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/customer.py": 1779686149.7508218,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/workflow.py": 1779265993.677624,
@@ -152,7 +153,7 @@
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_material.py": 1779284415.64655,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/collection_protocol_no.py": 1779882001.753651,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/outbound_handover.py": 1780455574.0993364,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_facility.py": 1781435014.6878881,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_facility.py": 1782290554.4607918,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception_lab_code.py": 1780462849.099601,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/cell_sample_type.py": 1781747645.1762104,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/pilot_outbound_status_sync.py": 1781595840.6955194,
@@ -184,7 +185,7 @@
"/Users/wuzilong/work/00code/zkxw/backend/app/api/pre_transit_sample.py": 1782204426.7965167,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/qc_sms_notification.py": 1780326875.7886882,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/research_donation_sample_collection.py": 1780457788.2998126,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/operations.py": 1781435016.699977,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/operations.py": 1782290496.6424515,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/menu.py": 1781749943.5843754,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/pilot_delivery.py": 1779655584.820777,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/admin.py": 1782268401.1532724,
@@ -197,13 +198,13 @@
"/Users/wuzilong/work/00code/zkxw/backend/app/api/sample_sms_notification.py": 1780325524.907156,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/sample_reception.py": 1781535825.750677,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/insurance_company.py": 1781410837.9443986,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py": 1781605168.6365004,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py": 1782362266.599568,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/protocol_sample_collection.py": 1780457788.2093296,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/leave.py": 1779266168.8693042,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/clinical_payment.py": 1781750841.074568,
"/Users/wuzilong/work/00code/zkxw/backend/app/services/workflow_callback.py": 1778727966.7277644,
"/Users/wuzilong/work/00code/zkxw/backend/app/services/qc_work_order_fee_summary.py": 1781337818.5442588,
"/Users/wuzilong/work/00code/zkxw/backend/app/services/storage_facility_code.py": 1781348051.133351,
"/Users/wuzilong/work/00code/zkxw/backend/app/services/storage_facility_code.py": 1782289322.2116506,
"/Users/wuzilong/work/00code/zkxw/backend/app/services/workflow_engine.py": 1778634664.6293883,
"/Users/wuzilong/work/00code/zkxw/backend/app/services/web_auth_roles.py": 1782122624.7941194,
"/Users/wuzilong/work/00code/zkxw/backend/app/services/protocol_payment_installment.py": 1781432208.0765035,
@@ -0,0 +1,7 @@
-- 生产过程派发质检:支持仅关联步骤产出物(无细胞产品时也可派发)
ALTER TABLE `biz_qc_work_order`
ADD COLUMN `production_step_output_id` varchar(64) DEFAULT NULL
COMMENT '关联生产任务步骤产出物ID(生产过程触发时)'
AFTER `production_task_id`;
CREATE INDEX `idx_qc_wo_prod_step_output` ON `biz_qc_work_order` (`production_step_output_id`, `del_flag`);
@@ -0,0 +1,30 @@
-- 可选:将仍有 task.current_assignee_user_id 且无步骤负责人的旧任务回填到全部步骤
--
-- 注意:MySQL 5.7 在 INSERT...SELECT 中 UUID() 可能整批只计算一次,导致主键重复。
-- 此处用 SHA2(step_id) 生成确定性主键,每步唯一,且可重复执行(已存在则跳过)。
INSERT INTO `biz_production_task_step_assignee` (
`id`, `step_id`, `user_id`, `tenant_no`, `order_num`,
`create_by`, `create_dept_id`, `create_time`, `update_by`, `update_time`, `del_flag`
)
SELECT
LOWER(SHA2(CONCAT('step-assignee-backfill:', s.id), 256)),
s.id,
t.current_assignee_user_id,
s.tenant_no,
0,
t.create_by,
IFNULL(t.create_dept_id, ''),
NOW(),
t.update_by,
NOW(),
0
FROM `biz_production_task` t
JOIN `biz_production_task_step` s ON s.task_id = t.id AND s.del_flag = 0
WHERE t.del_flag = 0
AND t.current_assignee_user_id IS NOT NULL
AND TRIM(t.current_assignee_user_id) <> ''
AND NOT EXISTS (
SELECT 1 FROM `biz_production_task_step_assignee` a
WHERE a.step_id = s.id AND a.del_flag = 0
);
@@ -0,0 +1,26 @@
-- 生产任务 · 工艺步骤级派发
-- 步骤计划开始时间、完成人 + 步骤负责人关联表
ALTER TABLE `biz_production_task_step`
ADD COLUMN `planned_start_time` datetime DEFAULT NULL COMMENT '计划开始时间' AFTER `step_status`;
ALTER TABLE `biz_production_task_step`
ADD COLUMN `completed_by` varchar(64) DEFAULT NULL COMMENT '完成步骤的用户ID' AFTER `completed_time`;
CREATE TABLE IF NOT EXISTS `biz_production_task_step_assignee` (
`id` varchar(64) NOT NULL COMMENT '主键',
`step_id` varchar(64) NOT NULL COMMENT '生产任务步骤ID',
`user_id` varchar(64) NOT NULL COMMENT '负责人用户ID',
`tenant_no` varchar(64) NOT NULL DEFAULT 'zkxw' COMMENT '租户编号',
`order_num` int DEFAULT 0 COMMENT '展示顺序',
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人id',
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建人部门ID',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人id',
`update_time` datetime NOT NULL COMMENT '更新时间',
`del_flag` tinyint NOT NULL DEFAULT 0 COMMENT '删除标记',
PRIMARY KEY (`id`),
KEY `idx_step_assignee_step` (`step_id`),
KEY `idx_step_assignee_user` (`user_id`, `tenant_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='生产任务步骤负责人';
+33 -1
View File
@@ -3522,8 +3522,10 @@ CREATE TABLE `biz_production_task_step` (
`actual_duration` varchar(100) DEFAULT NULL COMMENT '实际耗时',
`sop_ids` varchar(1000) DEFAULT NULL COMMENT 'SOP文档ID(逗号分隔)',
`step_status` varchar(16) NOT NULL DEFAULT 'dzx' COMMENT '状态(dzx待执行/jxz进行中/ywc已完成)',
`planned_start_time` datetime DEFAULT NULL COMMENT '计划开始时间',
`started_time` datetime DEFAULT NULL COMMENT '开始时间',
`completed_time` datetime DEFAULT NULL COMMENT '完成时间',
`completed_by` varchar(64) DEFAULT NULL COMMENT '完成步骤的用户ID',
`tenant_no` varchar(64) NOT NULL DEFAULT 'zkxw' COMMENT '租户编号',
`order_num` int(11) DEFAULT NULL COMMENT '排序号',
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
@@ -3538,12 +3540,40 @@ CREATE TABLE `biz_production_task_step` (
KEY `idx_pts_tenant` (`tenant_no`,`del_flag`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='生产任务工艺步骤';
-- ----------------------------
-- Table structure for biz_production_task_step_assignee
-- ----------------------------
DROP TABLE IF EXISTS `biz_production_task_step_assignee`;
CREATE TABLE `biz_production_task_step_assignee` (
`id` varchar(64) NOT NULL COMMENT '主键',
`step_id` varchar(64) NOT NULL COMMENT '生产任务步骤ID',
`user_id` varchar(64) NOT NULL COMMENT '负责人用户ID',
`tenant_no` varchar(64) NOT NULL DEFAULT 'zkxw' COMMENT '租户编号',
`order_num` int(11) DEFAULT '0' COMMENT '展示顺序',
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人id',
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建人部门ID',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人id',
`update_time` datetime NOT NULL COMMENT '更新时间',
`del_flag` tinyint(4) NOT NULL DEFAULT '0' COMMENT '删除标记:0-正常 1-已删除',
PRIMARY KEY (`id`),
KEY `idx_step_assignee_step` (`step_id`),
KEY `idx_step_assignee_user` (`user_id`,`tenant_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='生产任务步骤负责人';
-- ----------------------------
-- Records of biz_production_task_step
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Records of biz_production_task_step_assignee
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for biz_production_task_step_configured_reagent
-- ----------------------------
@@ -4647,6 +4677,7 @@ CREATE TABLE `biz_qc_work_order` (
`cell_product_id` varchar(64) DEFAULT NULL COMMENT '质检对象-细胞产品ID',
`production_work_order_id` varchar(64) DEFAULT NULL COMMENT '关联生产工单ID(生产过程触发时)',
`production_task_id` varchar(64) DEFAULT NULL COMMENT '关联生产任务ID(可选)',
`production_step_output_id` varchar(64) DEFAULT NULL COMMENT '关联生产任务步骤产出物ID(生产过程触发时)',
`work_order_status` varchar(16) NOT NULL DEFAULT 'dpz' COMMENT 'dpz待配置 ypz已配置 ywc已完成',
`has_lab` char(1) NOT NULL DEFAULT '0' COMMENT '含实验室自检',
`has_third_party` char(1) NOT NULL DEFAULT '0' COMMENT '含第三方检测',
@@ -4666,7 +4697,8 @@ CREATE TABLE `biz_qc_work_order` (
UNIQUE KEY `uk_qc_wo_reception` (`reception_id`,`del_flag`),
KEY `idx_qc_wo_tenant_status` (`tenant_no`,`work_order_status`,`del_flag`),
KEY `idx_qc_wo_sample_trigger` (`tenant_no`,`sample_code`,`trigger_source`,`del_flag`),
KEY `idx_qc_wo_prod_wo` (`production_work_order_id`,`del_flag`)
KEY `idx_qc_wo_prod_wo` (`production_work_order_id`,`del_flag`),
KEY `idx_qc_wo_prod_step_output` (`production_step_output_id`,`del_flag`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='质检工单';
-- ----------------------------
@@ -0,0 +1,926 @@
# 生产任务 · 工艺步骤级派发 Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 将生产任务派发粒度从「整条任务 + 单一负责人」改为「工艺步骤 + 多人负责人 + 计划开始时间」;「我的生产任务」按步骤负责人过滤;执行弹窗内仅可操作派给自己的步骤。
**Architecture:** 新增 `biz_production_task_step_assignee`(对齐 `biz_qc_task_assignee`);步骤表增加 `planned_start_time`、`completed_by`;`PUT /production-tasks/{id}/steps/dispatch` 批量 sync 负责人;mine 鉴权从 `assert_task_assignee` 改为「任务级任一步 assignee OR 步骤级 assignee」+ 步骤写操作 `assert_step_assignee`;前端新增 `DispatchStepDialog.vue`,移除任务级负责人表单,隐藏任务转发/改派入口。
**Tech Stack:** MySQL 5.7 · FastAPI · SQLAlchemy · Pydantic v2 · Vue 3 · Element Plus · pnpm
**Design Spec:** `docs/superpowers/specs/2026-06-25-production-step-dispatch-design.md`
---
## File Map
| 文件 | 职责 |
|------|------|
| `docs/sql/migrations/2026-06-25-production-step-dispatch.sql` | 步骤字段 + assignee 表 |
| `docs/sql/migrations/2026-06-25-production-step-dispatch-backfill.sql` | 可选:旧任务 task assignee → 全步骤 assignee |
| `docs/sql/zkxw.sql` | 同步 DDL |
| `backend/app/models/models.py` | `BizProductionTaskStep` 新列 + `BizProductionTaskStepAssignee` |
| `backend/app/models/__init__.py` | export 新模型 |
| `backend/app/crud/production_task_step_assignee.py` | **新建** sync / enrich / validate |
| `backend/app/crud/production_task.py` | mine 过滤、dispatch、assert、completed_by、create 去 assignee 必填 |
| `backend/app/schemas/production_task.py` | `ProductionTaskStepDispatchBody`;Create 去必填 assignee |
| `backend/app/api/production.py` | `PUT steps/dispatch`;`_assert_*` 改造 |
| `web-admin/src/api/production-task.ts` | `fetchDispatchProductionTaskSteps`;create 去 assigneeUserId |
| `web-admin/src/views/production/production-work-order/DispatchStepDialog.vue` | **新建** 步骤派发表格弹窗 |
| `web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue` | 移除任务负责人;B 入口打开步骤派发 |
| `web-admin/src/views/production/production-work-order/WorkOrderTaskSubList.vue` | 步骤派发按钮;隐藏转发/改派;展示步骤负责人 |
| `web-admin/src/views/production/production-work-order/TaskExecutionDialog.vue` | mine 步骤过滤 + 只读样式 |
| `web-admin/src/views/production/production-work-order/StepDetailPanel.vue` | `canEditStep` 控制按钮 |
| `web-admin/src/views/production/production-work-order/useTaskStepEditor.ts` | `canEditStep` / 完成写 completed_by 透传 |
---
## Task 1: 数据库迁移
**Files:**
- Create: `docs/sql/migrations/2026-06-25-production-step-dispatch.sql`
- Create: `docs/sql/migrations/2026-06-25-production-step-dispatch-backfill.sql`
- Modify: `docs/sql/zkxw.sql`(`biz_production_task_step` 与新建表片段)
- [ ] **Step 1: 编写主 migration**
```sql
-- docs/sql/migrations/2026-06-25-production-step-dispatch.sql
ALTER TABLE `biz_production_task_step`
ADD COLUMN `planned_start_time` datetime DEFAULT NULL COMMENT '计划开始时间' AFTER `step_status`;
ALTER TABLE `biz_production_task_step`
ADD COLUMN `completed_by` varchar(64) DEFAULT NULL COMMENT '完成步骤的用户ID' AFTER `completed_time`;
CREATE TABLE IF NOT EXISTS `biz_production_task_step_assignee` (
`id` varchar(64) NOT NULL COMMENT '主键',
`step_id` varchar(64) NOT NULL COMMENT '生产任务步骤ID',
`user_id` varchar(64) NOT NULL COMMENT '负责人用户ID',
`tenant_no` varchar(64) NOT NULL DEFAULT 'zkxw' COMMENT '租户编号',
`order_num` int DEFAULT 0 COMMENT '展示顺序',
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人id',
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建人部门ID',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人id',
`update_time` datetime NOT NULL COMMENT '更新时间',
`del_flag` tinyint NOT NULL DEFAULT 0 COMMENT '删除标记',
PRIMARY KEY (`id`),
KEY `idx_step_assignee_step` (`step_id`),
KEY `idx_step_assignee_user` (`user_id`, `tenant_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='生产任务步骤负责人';
```
- [ ] **Step 2: 编写可选 backfill(旧数据兼容)**
```sql
-- docs/sql/migrations/2026-06-25-production-step-dispatch-backfill.sql
-- 将仍有 task.current_assignee_user_id 且无步骤负责人的任务,回填到全部步骤
INSERT INTO biz_production_task_step_assignee (
id, step_id, user_id, tenant_no, order_num,
create_by, create_dept_id, create_time, update_by, update_time, del_flag
)
SELECT
REPLACE(UUID(), '-', ''),
s.id,
t.current_assignee_user_id,
s.tenant_no,
0,
t.create_by,
IFNULL(t.create_dept_id, ''),
NOW(),
t.update_by,
NOW(),
0
FROM biz_production_task t
JOIN biz_production_task_step s ON s.task_id = t.id AND s.del_flag = 0
WHERE t.del_flag = 0
AND t.current_assignee_user_id IS NOT NULL
AND TRIM(t.current_assignee_user_id) <> ''
AND NOT EXISTS (
SELECT 1 FROM biz_production_task_step_assignee a
WHERE a.step_id = s.id AND a.del_flag = 0
);
```
- [ ] **Step 3: 在开发/测试库执行主 migration**
Run:
```bash
mysql -u ... -p ... zkxw < docs/sql/migrations/2026-06-25-production-step-dispatch.sql
```
Expected: 两列新增成功;`biz_production_task_step_assignee` 表存在。
- [ ] **Step 4: 同步 `docs/sql/zkxw.sql` 对应 DDL**
---
## Task 2: Backend 模型
**Files:**
- Modify: `backend/app/models/models.py`
- Modify: `backend/app/models/__init__.py`
- [ ] **Step 1: 扩展 `BizProductionTaskStep`**
在 `step_status` 后、`started_time` 前增加:
```python
planned_start_time = Column(DateTime, nullable=True, comment="计划开始时间")
```
在 `completed_time` 后增加:
```python
completed_by = Column(String(64), nullable=True, comment="完成步骤的用户ID")
```
- [ ] **Step 2: 新增 `BizProductionTaskStepAssignee`**
放在 `BizProductionTaskStep` 附近,字段对齐 migration SQL(参照 `BizQcTaskAssignee`)。
- [ ] **Step 3: 注册 export**
`backend/app/models/__init__.py`:
```python
from app.models.models import (
# ...
BizProductionTaskStepAssignee,
)
__all__ = [
# ...
"BizProductionTaskStepAssignee",
]
```
---
## Task 3: Backend Schema
**Files:**
- Modify: `backend/app/schemas/production_task.py`
- [ ] **Step 1: 创建任务 body 去掉 assignee 必填**
```python
class ProductionTaskCreate(BaseModel):
model_config = ConfigDict(populate_by_name=True)
work_order_id: str
assignee_user_id: Optional[str] = Field(None, alias="assigneeUserId") # deprecated,可选
task_type: Optional[str] = 'cell_storage'
task_name: Optional[str] = None
route_id: Optional[str] = None
description: Optional[str] = None
remark: Optional[str] = None
```
- [ ] **Step 2: 新增步骤派发 body**
```python
class ProductionTaskStepDispatchItem(BaseModel):
model_config = ConfigDict(populate_by_name=True)
step_id: str = Field(..., alias="stepId")
assignee_user_ids: List[str] = Field(..., alias="assigneeUserIds", min_length=1)
planned_start_time: Optional[datetime] = Field(None, alias="plannedStartTime")
class ProductionTaskStepDispatchBody(BaseModel):
model_config = ConfigDict(populate_by_name=True)
steps: List[ProductionTaskStepDispatchItem] = Field(..., min_length=1)
```
---
## Task 4: 步骤负责人 CRUD 模块
**Files:**
- Create: `backend/app/crud/production_task_step_assignee.py`
- [ ] **Step 1: 创建模块(复制 QC 模式并改表名)**
```python
"""生产任务步骤负责人 CRUD"""
from __future__ import annotations
from datetime import datetime
from typing import Dict, List, Optional, Set
from sqlalchemy.orm import Session
from app.core.db_collation import eq_unicode
from app.core.utils import generate_uuid
from app.models.models import BizProductionTaskStepAssignee, SysUser
# validate_assignee_user_ids — 与 qc_task_assignee.validate_assignee_user_ids 相同逻辑
def sync_assignees_for_step(
db: Session,
step_id: str,
user_ids: List[str],
*,
tenant_no: str,
operator_id: Optional[str] = None,
create_dept_id: str = "",
) -> None:
"""全量替换步骤负责人(软删旧 + 插入新)。"""
# 实现同 qc_task_assignee.sync_assignees_for_task,表换 BizProductionTaskStepAssignee,FK 换 step_id
def enrich_step_assignees(
db: Session, steps: List[Dict], *, tenant_no: str
) -> None:
"""批量填充 assigneeUserIds / assigneeNames / assignees。"""
step_ids = [s["id"] for s in steps if s.get("id")]
if not step_ids:
return
rows = (
db.query(BizProductionTaskStepAssignee, SysUser)
.outerjoin(SysUser, eq_unicode(SysUser.id, BizProductionTaskStepAssignee.user_id))
.filter(
BizProductionTaskStepAssignee.step_id.in_(step_ids),
BizProductionTaskStepAssignee.tenant_no == tenant_no,
BizProductionTaskStepAssignee.del_flag == 0,
)
.order_by(BizProductionTaskStepAssignee.order_num.asc())
.all()
)
by_step: Dict[str, List] = {}
for assignee, user in rows:
by_step.setdefault(assignee.step_id, []).append((assignee, user))
for step in steps:
pairs = by_step.get(step["id"], [])
step["assigneeUserIds"] = [a.user_id for a, _ in pairs]
step["assigneeNames"] = [
f"{(u.nick_name or u.user_name or '')}({u.user_no or ''})".strip()
for _, u in pairs if u
]
step["assignees"] = [
{
"userId": a.user_id,
"nickName": (u.nick_name or u.user_name or "") if u else "",
"userNo": (u.user_no or "") if u else "",
}
for a, u in pairs
]
def user_is_step_assignee(
db: Session, step_id: str, user_id: str, tenant_no: str
) -> bool:
uid = (user_id or "").strip()
if not uid:
return False
return (
db.query(BizProductionTaskStepAssignee.id)
.filter(
BizProductionTaskStepAssignee.step_id == step_id,
BizProductionTaskStepAssignee.user_id == uid,
BizProductionTaskStepAssignee.tenant_no == tenant_no,
BizProductionTaskStepAssignee.del_flag == 0,
)
.first()
is not None
)
def user_is_any_step_assignee_of_task(
db: Session, task_id: str, user_id: str, tenant_no: str
) -> bool:
from app.models.models import BizProductionTaskStep
uid = (user_id or "").strip()
if not uid:
return False
return (
db.query(BizProductionTaskStepAssignee.id)
.join(
BizProductionTaskStep,
BizProductionTaskStep.id == BizProductionTaskStepAssignee.step_id,
)
.filter(
BizProductionTaskStep.task_id == task_id,
BizProductionTaskStep.del_flag == 0,
BizProductionTaskStepAssignee.user_id == uid,
BizProductionTaskStepAssignee.tenant_no == tenant_no,
BizProductionTaskStepAssignee.del_flag == 0,
)
.first()
is not None
)
```
- [ ] **Step 2: 本地 import smoke test**
Run:
```bash
cd backend && source venv/bin/activate
python -c "from app.crud import production_task_step_assignee; print('ok')"
```
Expected: `ok`
---
## Task 5: production_task.py — 步骤 dict、派发、mine 过滤
**Files:**
- Modify: `backend/app/crud/production_task.py`
- [ ] **Step 1: 扩展 `_step_to_dict`**
```python
def _step_to_dict(s: BizProductionTaskStep) -> Dict[str, Any]:
return {
# ... existing fields ...
"stepStatus": s.step_status or "dzx",
"plannedStartTime": _fmt_dt(getattr(s, "planned_start_time", None)),
"startedTime": _fmt_dt(s.started_time),
"completedTime": _fmt_dt(s.completed_time),
"completedBy": getattr(s, "completed_by", None) or "",
"completedByName": "",
"assigneeUserIds": [],
"assigneeNames": [],
"assignees": [],
"remark": s.remark or "",
}
```
- [ ] **Step 2: `create_production_task` 去掉 assignee 必填**
删除或注释:
```python
assignee_raw = (data.get("assignee_user_id") or data.get("assigneeUserId") or "").strip()
assignee_user_id = _validate_assignee_user_id(db, assignee_raw, tenant_no)
```
改为:
```python
assignee_raw = (data.get("assignee_user_id") or data.get("assigneeUserId") or "").strip()
assignee_user_id = assignee_raw.strip() or None
if assignee_raw.strip():
assignee_user_id = _validate_assignee_user_id(db, assignee_raw, tenant_no)
```
创建任务时:
```python
current_assignee_user_id=assignee_user_id, # 可为 NULL
```
- [ ] **Step 3: 新增 `dispatch_task_steps`**
```python
def dispatch_task_steps(
db: Session,
task_id: str,
*,
tenant_no: str,
steps_payload: List[Dict[str, Any]],
operator_id: Optional[str] = None,
create_dept_id: str = "",
) -> Dict[str, Any]:
from app.crud import production_task_step_assignee as step_assignee_svc
task = get_production_task_row(db, task_id, tenant_no)
if not task:
raise ValueError("任务不存在")
if not steps_payload:
raise ValueError("请至少指定一个步骤的负责人")
step_rows = {
s.id: s
for s in db.query(BizProductionTaskStep).filter(
BizProductionTaskStep.task_id == task_id,
BizProductionTaskStep.tenant_no == tenant_no,
BizProductionTaskStep.del_flag == 0,
).all()
}
for item in steps_payload:
sid = (item.get("step_id") or item.get("stepId") or "").strip()
if sid not in step_rows:
raise ValueError(f"步骤不存在或不属于该任务: {sid}")
user_ids = item.get("assignee_user_ids") or item.get("assigneeUserIds") or []
step_assignee_svc.sync_assignees_for_step(
db, sid, user_ids,
tenant_no=tenant_no,
operator_id=operator_id,
create_dept_id=create_dept_id,
)
step = step_rows[sid]
pst = item.get("planned_start_time") or item.get("plannedStartTime")
if pst is None and "plannedStartTime" in item and item.get("plannedStartTime") is None:
step.planned_start_time = None
elif pst:
from dateutil.parser import parse as _parse_dt
step.planned_start_time = _parse_dt(str(pst)) if not isinstance(pst, datetime) else pst
step.update_by = operator_id
step.update_time = datetime.now()
db.add(step)
db.flush()
detail = get_production_task_detail(db, task_id, tenant_no)
if not detail:
raise ValueError("任务不存在")
return detail
```
- [ ] **Step 4: 改造 `list_production_tasks` mine 过滤**
新增参数 `step_assignee_user_id: Optional[str] = None`。
在 `current_assignee_user_id` 过滤块 **之后** 增加:
```python
sa_uid = (step_assignee_user_id or "").strip()
if sa_uid:
step_task_ids = (
db.query(BizProductionTaskStep.task_id)
.join(
BizProductionTaskStepAssignee,
BizProductionTaskStepAssignee.step_id == BizProductionTaskStep.id,
)
.filter(
BizProductionTaskStepAssignee.user_id == sa_uid,
BizProductionTaskStepAssignee.tenant_no == tenant_no,
BizProductionTaskStepAssignee.del_flag == 0,
BizProductionTaskStep.del_flag == 0,
BizProductionTask.del_flag == 0,
)
.distinct()
.subquery()
)
q = q.filter(
or_(
BizProductionTask.id.in_(step_task_ids),
BizProductionTask.current_assignee_user_id == sa_uid, # 旧数据 fallback
)
)
```
- [ ] **Step 5: 改造 `list_mine_production_tasks`**
`scope=current` 时调用:
```python
return list_production_tasks(
# ...
step_assignee_user_id=uid,
current_assignee_user_id=None, # 不再仅用 task 级
# ...
)
```
保留 `current_assignee_user_id=uid` 作为 OR 条件已在 Step 4 合并;或直接只传 `step_assignee_user_id=uid` 并在 SQL 内 OR task assignee fallback。
- [ ] **Step 6: 新增 assert 函数**
```python
def assert_task_step_assignee_or_legacy(
db: Session, task_id: str, user_id: str, tenant_no: str
) -> BizProductionTask:
from app.crud import production_task_step_assignee as step_assignee_svc
task = get_production_task_row(db, task_id, tenant_no)
if not task:
raise ValueError("任务不存在")
if step_assignee_svc.user_is_any_step_assignee_of_task(db, task_id, user_id, tenant_no):
return task
if (task.current_assignee_user_id or "") == (user_id or ""):
return task
raise PermissionError("非任务步骤负责人,无权操作")
def assert_step_assignee_for_write(
db: Session,
step_id: str,
user_id: str,
tenant_no: str,
*,
manage: bool = False,
) -> BizProductionTaskStep:
if manage:
step = db.query(BizProductionTaskStep).filter(
BizProductionTaskStep.id == step_id,
BizProductionTaskStep.tenant_no == tenant_no,
BizProductionTaskStep.del_flag == 0,
).first()
if not step:
raise ValueError("步骤不存在")
return step
from app.crud import production_task_step_assignee as step_assignee_svc
step = db.query(BizProductionTaskStep).filter(
BizProductionTaskStep.id == step_id,
BizProductionTaskStep.tenant_no == tenant_no,
BizProductionTaskStep.del_flag == 0,
).first()
if not step:
raise ValueError("步骤不存在")
if not step_assignee_svc.user_is_step_assignee(db, step_id, user_id, tenant_no):
raise PermissionError("非当前步骤负责人,无权操作")
return step
```
- [ ] **Step 7: `update_task_step` 写 `completed_by`**
当 `data.get("step_status") == "ywc"` 且 `user_id` 存在:
```python
step.completed_by = user_id
```
- [ ] **Step 8: `get_production_task_detail` enrich 步骤 assignee**
加载 steps 后调用:
```python
from app.crud.production_task_step_assignee import enrich_step_assignees
enrich_step_assignees(db, steps, tenant_no=tenant_no)
# 可选:_enrich_completed_by_names
```
---
## Task 6: API 路由与鉴权
**Files:**
- Modify: `backend/app/api/production.py`
- [ ] **Step 1: 改造 `_assert_production_task_write_access`**
```python
def _assert_production_task_write_access(
db: Session,
user: dict,
task_id: str,
tenant_no: str,
operate_scope: Optional[str],
) -> None:
scope = (operate_scope or "manage").strip().lower()
user_id = _get_current_user_id(user)
role = user.get("role")
codes = get_user_menu_codes(db, user_id) if role != SUPER_ADMIN_ROLE else set()
if scope == "mine":
if role != SUPER_ADMIN_ROLE and "production:my-task:list" not in codes:
raise PermissionError("无菜单访问权限")
production_task_svc.assert_task_step_assignee_or_legacy(
db, task_id, user_id, tenant_no
)
elif role != SUPER_ADMIN_ROLE and "production:work-order:list" not in codes:
raise PermissionError("无菜单访问权限")
```
- [ ] **Step 2: 新增步骤写鉴权 helper**
```python
def _assert_production_task_step_write_access(
db: Session,
user: dict,
step_id: str,
tenant_no: str,
operate_scope: Optional[str],
) -> None:
scope = (operate_scope or "manage").strip().lower()
user_id = _get_current_user_id(user)
role = user.get("role")
codes = get_user_menu_codes(db, user_id) if role != SUPER_ADMIN_ROLE else set()
manage = scope != "mine"
if not manage:
if role != SUPER_ADMIN_ROLE and "production:my-task:list" not in codes:
raise PermissionError("无菜单访问权限")
elif role != SUPER_ADMIN_ROLE and "production:work-order:list" not in codes:
raise PermissionError("无菜单访问权限")
production_task_svc.assert_step_assignee_for_write(
db, step_id, user_id, tenant_no, manage=manage
)
```
- [ ] **Step 3: 注册 `PUT /production-tasks/{task_id}/steps/dispatch`**
```python
@router.put("/production-tasks/{task_id}/steps/dispatch")
async def dispatch_production_task_steps_api(
task_id: str,
body: ProductionTaskStepDispatchBody,
user: dict = Depends(require_menu_code("production:work-order:list")),
db: Session = Depends(get_db),
):
try:
tenant_no = _get_tenant_no(db, user)
payload = [
{
"step_id": it.step_id,
"assignee_user_ids": it.assignee_user_ids,
"planned_start_time": it.planned_start_time,
}
for it in body.steps
]
detail = production_task_svc.dispatch_task_steps(
db,
task_id,
tenant_no=tenant_no,
steps_payload=payload,
operator_id=user.get("user_id"),
create_dept_id=user.get("dept_id", ""),
)
db.commit()
return success_response(detail, message="步骤派发成功")
except ValueError as e:
db.rollback()
return error_response(400, str(e))
```
- [ ] **Step 4: 步骤写接口增加 step 级鉴权**
在以下 handler **内** `_assert_production_task_write_access` 之后追加 `_assert_production_task_step_write_access`:
- `update_production_task_step`
- `replace_task_step_reagents`
- `replace_task_step_consumables`
- `replace_task_step_configured_reagents`
- `replace_task_step_data_collections`
- `replace_task_step_outputs`
- `delete_production_task_step`
- `copy_production_task_step`
示例(`update_production_task_step`):
```python
_assert_production_task_write_access(db, user, task_id, tenant_no, operateScope)
_assert_production_task_step_write_access(db, user, step_id, tenant_no, operateScope)
```
任务级 inputs/outputs/attachments/status 仍只用 task 级 assert(D10:任一步 assignee 即可)。
- [ ] **Step 5: Swagger 手动验证**
Run backend,在 `/docs` 测试:
1. `PUT .../steps/dispatch` 返回 steps 含 `assigneeUserIds`
2. `GET .../production-tasks/mine?scope=current` 仅 assignee 可见
---
## Task 7: Frontend API
**Files:**
- Modify: `web-admin/src/api/production-task.ts`
- [ ] **Step 1: 新增 dispatch API**
```typescript
export function fetchDispatchProductionTaskSteps(
taskId: string,
data: {
steps: Array<{
stepId: string
assigneeUserIds: string[]
plannedStartTime?: string | null
}>
}
) {
return request.put<any>({
url: `/web/admin/production-tasks/${taskId}/steps/dispatch`,
data
})
}
```
- [ ] **Step 2: 确认 create 不再传 assigneeUserId**
`DispatchTaskDialog.vue` 将在 Task 9 移除该字段。
---
## Task 8: DispatchStepDialog.vue(新建)
**Files:**
- Create: `web-admin/src/views/production/production-work-order/DispatchStepDialog.vue`
- [ ] **Step 1: 实现弹窗骨架**
要点:
- Props: `visible`, `taskId`, `taskName?`
- 打开时 `fetchProductionTaskDetail(taskId)` 加载 `steps`
- 表格列:序号、`processName`、负责人多选(remote `fetchUserList`)、计划开始时间
- 本地 state:`rows: { stepId, processName, assigneeUserIds, plannedStartTime, userOptions }[]`
- 提交:过滤 `assigneeUserIds.length >= 1` 的行,调用 `fetchDispatchProductionTaskSteps`
- 校验:至少 1 行有负责人
- UI:`dialog-form-layout`、`label-position="top"`、footer Close/Check 图标
- [ ] **Step 2: 回显已有 assignee**
detail.steps 中若已有 `assigneeUserIds` / `plannedStartTime`,填入表单。
---
## Task 9: DispatchTaskDialog.vue 改造
**Files:**
- Modify: `web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue`
- [ ] **Step 1: 移除任务级负责人**
删除表单项 `assigneeUserId`、`rules.assigneeUserId`、`form.assigneeUserId` 及 create payload 中的 `assigneeUserId`。
- [ ] **Step 2: B 入口 — 创建后打开步骤派发**
在 `handleCreateTask` 成功、`enterEditPhase` 之后:
```typescript
emit('open-step-dispatch', { taskId, taskName: form.taskName })
```
或由父组件监听 `@success` 并根据是否选了 `routeId` 打开 `DispatchStepDialog`。
推荐:Dialog 内 `emit('needStepDispatch', taskId)`,父组件 `WorkOrderTaskSubList` / `index.vue` 统一挂载 `DispatchStepDialog`。
- [ ] **Step 3: 若选了 route 且步骤已克隆**
创建成功 + `expectSteps` 为 true 时,关闭编辑阶段或保留编辑的同时弹出步骤派发(产品:B 入口)。
---
## Task 10: WorkOrderTaskSubList.vue 改造
**Files:**
- Modify: `web-admin/src/views/production/production-work-order/WorkOrderTaskSubList.vue`
- [ ] **Step 1: 挂载 DispatchStepDialog**
```vue
<DispatchStepDialog
v-model:visible="stepDispatchVisible"
:task-id="stepDispatchTaskId"
:task-name="stepDispatchTaskName"
@success="handleTaskMutationSuccess"
/>
```
- [ ] **Step 2: 管理视角增加「步骤派发」按钮**
在 `!isMineMode` 且任务非 `yzf` 时显示;文案:无 assignee 时「步骤派发」,否则「调整派发」。
判断:任一步 `assigneeUserIds?.length` 或后端返回 `hasStepDispatch`。
- [ ] **Step 3: 隐藏 deprecated 转发/改派**
删除或 `v-if="false"`:
- 下拉「转发任务」「改派负责人」
- `ForwardTaskDialog` 引用可保留文件但不挂载
- [ ] **Step 4: 任务卡片 meta 展示步骤负责人摘要**
在任务名下方增加 muted 文本,例如:`步骤负责人:张三、李四(细胞培养 09:00)` — 取前 2 步或折叠展示。
---
## Task 11: 执行弹窗 — 步骤权限与只读
**Files:**
- Modify: `web-admin/src/views/production/production-work-order/useTaskStepEditor.ts`
- Modify: `web-admin/src/views/production/production-work-order/TaskExecutionDialog.vue`
- Modify: `web-admin/src/views/production/production-work-order/StepDetailPanel.vue`
- [ ] **Step 1: useTaskStepEditor 增加 canEditStep**
```typescript
import { useUserStore } from '@/store/modules/user'
const userStore = useUserStore()
function canEditStep(step: Record<string, any> | null | undefined): boolean {
if (!step) return false
if (operateScope.value !== 'mine') return true
const uid = userStore.info?.id || userStore.userId || ''
const ids: string[] = step.assigneeUserIds || []
return ids.includes(uid)
}
function visibleStepsForMine(steps: Record<string, any>[]) {
if (operateScope.value !== 'mine') return steps
return steps.filter((s) => (s.assigneeUserIds || []).length > 0)
}
```
导出 `canEditStep`、`visibleStepsForMine`(或 computed `displayStepList`)。
- [ ] **Step 2: TaskExecutionDialog 步骤列表**
- mine:`v-for="element in editor.displayStepList"`(仅已派步骤)
- 对 `!canEditStep(element)` 的步骤加 class `is-readonly`(降低 opacity,禁用 drag)
- 任务级「转发任务」按钮移除
- [ ] **Step 3: StepDetailPanel 按钮**
```vue
<el-button
v-if="editor.mode.value === 'execution' && editor.canEditStep(editor.selectedStep.value)"
...
/>
```
保存 / 开始 / 完成均加 `canEditStep` 条件;只读时仅展示内容。
- [ ] **Step 4: 步骤写操作前端 guard(可选双保险)**
`handleStartStep` / `handleCompleteStep` 开头:
```typescript
if (!canEditStep(step)) {
ElMessage.warning('非当前步骤负责人,无法操作')
return
}
```
---
## Task 12: 字段一致性与图谱
**Files:**
- Run: `backend/scripts/check_field_consistency.py`(若涉及 CRUD 字段变更)
- Run: `./scripts/update-graph.sh backend frontend`
- [ ] **Step 1: 字段一致性检查**
Run:
```bash
cd backend && source venv/bin/activate
python scripts/check_field_consistency.py
```
Expected: 无 production_task step 相关新增不一致;若有 `_row_to_dict` 变更则修复 schema/前端。
- [ ] **Step 2: 更新知识图谱**
Run:
```bash
./scripts/update-graph.sh backend
./scripts/update-graph.sh frontend
```
---
## Task 13: 手工测试清单
- [ ] **B 入口**:生产工单 → 派发任务 → 选工艺路线 → 创建 → 步骤派发 → 用户 A/B 在「我的生产任务」见工单,执行时仅各自步骤可操作,其他只读。
- [ ] **A 入口**:无路线创建 → 手工加 3 步 → 步骤派发 → 同上。
- [ ] **一步多人**:A、B 同为步骤 1 负责人;A 完成步骤 1 后 B 见只读已完成。
- [ ] **同人多步**:步骤 1、2 均派给 A;A 两步均可操作。
- [ ] **计划开始时间**:卡片/派发弹窗展示;未到时间仍可执行。
- [ ] **未派步骤**:mine 执行弹窗不显示该步;manage 可见可操作。
- [ ] **改派**:manage 调整派发后,旧 assignee mine 列表消失(若无其他步骤),新 assignee 出现。
- [ ] **旧数据 backfill**(若执行 backfill SQL):原 task assignee 仍能在 mine 看到任务。
- [ ] **回归**:任务输入/产出/完成整单、派发质检工单链路不受影响。
---
## Spec Coverage Self-Review
| Spec 要求 | 对应 Task |
|-----------|-----------|
| D1 工单+任务列表 | Task 5 mine 过滤不变外层结构 |
| D2 其他步骤只读 | Task 11 |
| D3 一步多人任一完成 | Task 5 Step 7 completed_by |
| D4 计划开始时间不阻断 | Task 8/11 无前端时间校验 |
| D5 A+B 双入口 | Task 8/9/10 |
| D6 未派步骤仅 manage | Task 11 visibleStepsForMine |
| D7 方案 1 数据模型 | Task 1–4 |
| D8 任务 assignee 非必填 | Task 3/5 |
| D9 转发 deprecated | Task 10 |
| D10 任务 I/O 任一步 assignee | Task 6 task 级 assert |
| D11 mine 不可改派 | Task 6 dispatch 仅 work-order 权限 |
无 TBD 占位;类型命名 `assigneeUserIds` / `plannedStartTime` 前后端一致。
---
## 建议实施顺序
```
Task 1 → 2 → 3 → 4 → 5 → 6 → 7 → 8 → 9 → 10 → 11 → 12 → 13
└─ backend 可联调 ─┘ └─ frontend ─────────────┘
```
Backend Task 1–6 完成后即可用 Swagger 验证 mine 过滤与 dispatch;Frontend Task 7–11 依赖后端 API。
@@ -0,0 +1,86 @@
# 编辑生产任务 · 步骤详情内派发字段 — 设计
> 日期: 2026-06-25
> 状态: **已评审,可实施**
> 依赖: `2026-06-25-production-step-dispatch-design.md`
---
## 1. 背景
【编辑生产任务】(`DispatchTaskDialog` 阶段二)使用三栏编辑器 + `StepDetailPanel`。步骤负责人与计划开始时间仅能通过独立弹窗 `DispatchStepDialog` 配置,步骤详情无展示与编辑入口,导致:
- 已派发数据在 API 中存在,右侧详情不可见;
- 工艺库新增步骤无法设置负责人与时间;
- 改派需切换弹窗,体验割裂。
## 2. 决策
| 项 | 决策 |
|----|------|
| 方案 | **A**:在 `StepDetailPanel` 增加负责人 + 计划开始时间 |
| 关闭校验 | **宽松**:关闭编辑弹窗时不强制每步均有负责人 |
| 保存策略 | **A1**:并入步骤「保存」按钮(`handleManualSave`) |
| 批量改派 | 保留 `DispatchStepDialog` 作批量入口 |
## 3. UI
### 3.1 展示条件
- `canEditStepDispatch()` 且 `!readonly`:可编辑控件(编辑工艺 / 任务执行,manage 与 mine 均可)
- 只读「任务详情」:展示文本 Tag / 时间,不可改
### 3.2 任务执行弹窗
- manage 与 **mine** 均在步骤详情可配置负责人与时间(保存时调 `steps/dispatch`,带 `operateScope`)
- mine 对未派发的新步骤:任务内任一步负责人可配置并保存
### 3.3 控件(对齐 `DispatchStepDialog`)
| 字段 | 控件 |
|------|------|
| 负责人 | `el-select` multiple + remote 用户搜索,`nickName(userNo)` |
| 计划开始时间 | `el-date-picker` datetime,`value-format="YYYY-MM-DDTHH:mm:00"`,可清空 |
置于步骤详情 `form-grid-4` 内(与工序名称等同区),label `label-position="top"`。
### 3.4 提示
- 无负责人时:字段下 muted 提示「未派发;保存工艺后可在本处或「步骤派发」中指定负责人」
## 4. 数据与保存
### 4.1 加载
- 任务详情 steps 已含 `assigneeUserIds`、`assignees`、`plannedStartTime`
- `normalizeStepSubTables` 补全 `assigneeUserIds`(来自 `assignees`),并记录快照 `_snapshotAssigneeUserIds`、`_snapshotPlannedStartTime` 用于脏检测
### 4.2 保存
- `handleManualSave` 在 `saveStepBasic` 等之后调用 `saveStepDispatch()`
- 仅当 `stepDispatchChanged(step)` 时调用 `PUT steps/dispatch`(单步 payload)
- 允许 `assigneeUserIds` 为空(清空负责人);`plannedStartTime` 传 `null` 表示清空
- 成功后更新快照,避免重复提交
### 4.3 关闭弹窗
- **不校验**全步骤是否已派发;未派步骤仍仅 manage 可见(沿用步骤派发设计 §1.2)
## 5. 文件
| 文件 | 变更 |
|------|------|
| `StepDetailPanel.vue` | 负责人、计划开始时间表单项 |
| `useTaskStepEditor.ts` | 用户搜索、`saveStepDispatch`、快照脏检测、并入 `handleManualSave` |
| `DispatchTaskDialog.vue` | 打开编辑时预加载用户列表(可选) |
## 6. 测试要点
1. 编辑工艺:已派发步骤在详情可见负责人与时间。
2. 修改负责人/时间后点「保存」,mine 列表与步骤派发弹窗数据一致。
3. 工艺库新增步骤:选中后可在详情设置并保存。
4. 清空负责人或计划时间后保存,DB 与再次打开一致。
5. 仅改工序名称未改派发字段时,保存不触发 dispatch API。
6. 关闭编辑弹窗:部分步骤未派发仍可关闭。
7. 工单页 · 任务执行(manage):工艺库新增步骤可在详情设负责人/时间并保存。
8. 我的生产任务 · 执行(mine):可编辑负责人/时间并保存(需为任务步骤负责人;未派发新步可由任务内负责人配置)。
@@ -0,0 +1,324 @@
# 生产任务 · 工艺步骤级派发 — 需求分析与设计
> 日期: 2026-06-25
> 状态: **已评审,可实施**
> 范围: web-admin + backend(生产工单派发、任务执行、我的生产任务)
---
## 1. 背景与目标
### 1.1 现状
| 项 | 现状 |
|----|------|
| 派发粒度 | `DispatchTaskDialog.vue` 创建 **整条生产任务** 时指定 **单一任务负责人**(`biz_production_task.current_assignee_user_id`) |
| 步骤模型 | `biz_production_task_step` 有工序与执行状态(`dzx/jxz/ywc`),**无负责人、无计划开始时间** |
| 我的生产任务 | 按 `current_assignee_user_id = 当前用户` 过滤任务 |
| 任务执行 | `TaskExecutionDialog.vue`:当前负责人对 **全部步骤** 可操作 |
| 类似实现 | 质检任务 **`biz_qc_task_assignee`**:每检测项多人负责,**任一可完成** |
### 1.2 客户需求(原文摘要)
1. **派发按工艺步骤**,而非整条生产任务整体派发。
2. **列表仍按工单 + 生产任务展示**;展开/进入执行后,**仅派给自己的步骤可操作**,其他步骤 **只读可见**。
3. 一个工艺步骤可 **同时派给多人**;**任一负责人** 可开始/完成该步骤。
4. 每个步骤派发时可设置 **计划开始时间**(展示/排序,**不限制**提前执行)。
5. 同一生产任务内,**多个步骤可派给同一人**。
6. 支持 **创建时一并派发(B)** 与 **工艺配置后单独派发(A)** 两种入口。
7. 已派给 A 的步骤,**可改派** 给其他人(更新负责人列表)。
8. **未指定负责人的步骤**:仅管理员在 **生产工单页** 可见可操作;**不出现在** 任何人的「我的生产任务」。
---
## 2. 产品决策
| # | 问题 | 决策 | 状态 |
|---|------|------|------|
| D1 | 「我的生产任务」列表粒度? | **工单 + 任务卡片**(非步骤级平铺列表) | ✅ |
| D2 | 未派给自己的步骤如何展示? | **只读可见**(可见完整工艺链路与状态,不可操作) | ✅ |
| D3 | 一步多人时的完成语义? | **任一负责人可独立完成**(与质检任务一致;`completed_by` 记实际操作人) | ✅ |
| D4 | 计划开始时间含义? | **仅展示/排序**,不阻断执行 | ✅ |
| D5 | 派发入口? | **A + B 均支持**,共用同一套步骤派发能力 | ✅ |
| D6 | 未派步骤可见性? | **仅管理员**(工单页 `operateScope=manage`);mine 不可见 | ✅ |
| D7 | 数据模型方案? | **方案 1**:`biz_production_task_step_assignee` + 步骤 `planned_start_time` | ✅ |
| D8 | 任务级负责人字段? | 创建时 **不再必填**;「我的生产任务」改按 **步骤负责人** 过滤 | ✅ |
| D9 | 任务级「转发」? | **保留代码但标记 deprecated**;协作改由 **步骤改派** 完成 | ✅ |
| D10 | 原材料/产出/纸质文档? | **任一步骤负责人** 在 mine 视角均可编辑(与步骤协作一致) | ✅ |
| D11 | mine 端能否自行改派? | **否**;改派仅 **工单管理页**(`operateScope=manage`) | ✅ |
---
## 3. 业务流程(目标态)
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 生产工单 · 派发生产任务 │
│ 阶段一:任务名称、工艺路线(可选)、描述 — 【移除任务级负责人】 │
│ 阶段二:编辑工艺步骤(现有三栏编辑器) │
└─────────────────────────────────────────────────────────────────────────┘
│ 若选了工艺路线(B) │ 未选路线 / 后补步骤(A)
▼ ▼
┌────────────────────────────┐ ┌────────────────────────────┐
│ 创建后立即打开「步骤派发」 │ │ 任务卡片 · 「步骤派发」 │
│ 每步:多人 + 计划开始时间 │ │ 同上 │
└────────────────────────────┘ └────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 我的生产任务 · 工单 + 任务卡片 │
│ 过滤:当前用户是 ≥1 个步骤的负责人 │
│ 展开 → 进入执行:自己的步骤可操作;其他步骤只读;未派步骤 mine 不可见 │
└─────────────────────────────────────────────────────────────────────────┘
│
管理员工单页 · 步骤改派
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ sync 步骤负责人列表 + 更新 planned_start_time │
│ dzx/jxz:改派后新负责人可操作;ywc:仅影响可见性,不 reopen 步骤 │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## 4. 数据模型
### 4.1 新增 / 变更
**步骤表扩展** `biz_production_task_step`:
```sql
ALTER TABLE biz_production_task_step
ADD COLUMN planned_start_time datetime DEFAULT NULL COMMENT '计划开始时间' AFTER step_status;
ALTER TABLE biz_production_task_step
ADD COLUMN completed_by varchar(64) DEFAULT NULL COMMENT '完成步骤的用户ID' AFTER completed_time;
```
**步骤负责人关联表** `biz_production_task_step_assignee`(对齐 `biz_qc_task_assignee`):
```sql
CREATE TABLE biz_production_task_step_assignee (
id varchar(64) NOT NULL COMMENT '主键',
step_id varchar(64) NOT NULL COMMENT '生产任务步骤ID',
user_id varchar(64) NOT NULL COMMENT '负责人用户ID',
tenant_no varchar(64) NOT NULL DEFAULT 'zkxw' COMMENT '租户编号',
order_num int DEFAULT 0 COMMENT '展示顺序',
remark varchar(512) DEFAULT NULL COMMENT '备注',
create_by varchar(64) DEFAULT NULL COMMENT '创建人id',
create_dept_id varchar(64) NOT NULL DEFAULT '' COMMENT '创建人部门ID',
create_time datetime NOT NULL COMMENT '创建时间',
update_by varchar(64) DEFAULT NULL COMMENT '更新人id',
update_time datetime NOT NULL COMMENT '更新时间',
del_flag tinyint NOT NULL DEFAULT 0 COMMENT '删除标记',
PRIMARY KEY (id),
KEY idx_step_assignee_step (step_id),
KEY idx_step_assignee_user (user_id, tenant_no)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='生产任务步骤负责人';
```
### 4.2 任务级字段(兼容)
| 字段 | 处理 |
|------|------|
| `biz_production_task.current_assignee_user_id` | **保留列**;新创建任务可为 NULL;API 创建 body **移除 assigneeUserId 必填** |
| `biz_production_task_handover` | **保留**;UI 不再主推「任务转发」,文档标注 deprecated |
### 4.3 历史数据迁移(可选脚本)
- 若旧任务 `current_assignee_user_id` 非空且无步骤负责人:可将该用户写入 **全部现有步骤** 的 assignee(一次性迁移),保证旧任务仍在「我的生产任务」可见。
- 无 assignee 且无步骤负责人的旧任务:行为与「未派步骤」一致,仅 manage 可见。
---
## 5. API 设计
### 5.1 步骤派发(批量)
```
PUT /api/web/admin/production-tasks/{taskId}/steps/dispatch
```
**Body**(camelCase 与现有约定一致):
```json
{
"steps": [
{
"stepId": "uuid",
"assigneeUserIds": ["u1", "u2"],
"plannedStartTime": "2026-06-25T09:00:00"
}
]
}
```
**规则:**
- 至少 **1 个 step** 在请求中且 `assigneeUserIds.length >= 1`(允许部分步骤暂不派)。
- 未出现在请求中的步骤:**不修改** 其负责人与时间。
- 出现的步骤:**全量 sync** 负责人(软删旧 + 插入新,复用 `qc_task_assignee.sync_assignees` 模式)。
- `plannedStartTime` 可选;传 null 表示清空。
- 权限:`production:work-order:list`(manage);mine 端 **403**。
**响应:** 任务详情(含 steps、assignees、plannedStartTime、assigneeNames)。
### 5.2 创建生产任务
```
POST /api/web/admin/production-tasks
```
- **移除** `assigneeUserId` 必填校验。
- 可选扩展:同 body 内嵌 `stepDispatch` 数组(B 入口一次提交);或创建成功后前端再调 §5.1。
### 5.3 我的生产任务列表
```
GET /api/web/admin/production-tasks/mine
```
**过滤逻辑变更:**
- **current**:存在 `biz_production_task_step_assignee.user_id = 当前用户` 且 `del_flag=0` 的 **至少一个步骤** 的任务。
- **history**(若保留):曾出现在步骤 assignee 但当前已不在任一步 assignee 列表的任务(可选首版简化:与 current 合并或暂不实现 history 变更)。
> 首版:**替换** 原 `current_assignee_user_id = 我` 为主过滤条件;history 沿用 handover 逻辑仅作兼容,不作为步骤协作主路径。
### 5.4 步骤写操作鉴权
| 操作 | mine | manage |
|------|------|--------|
| 开始/完成/保存 **已派给自己** 的步骤 | ✅ | ✅ |
| 操作 **未派给自己** 的步骤 | ❌ 只读 | ✅ |
| 操作 **未派任何人** 的步骤 | ❌ 不可见 | ✅ |
| 编辑任务输入/产出/附件 | ✅ 若本人是任一步 assignee | ✅ |
| 步骤派发/改派 | ❌ | ✅ |
实现:在 `production_task` CRUD 增加 `assert_step_assignee(step_id, user_id)`;manage 路径跳过。
### 5.5 任务详情 / 列表 enrichment
- `_task_to_dict` / 步骤 `_step_to_dict` 增加:
- `assigneeUserIds: string[]`
- `assigneeNames: string[]`(或 `assignees: {userId, nickName, userNo}[]`)
- `plannedStartTime`
- `completedBy` / `completedByName`
- 列表接口批量 `_enrich_step_assignees`(对齐 QC `_enrich_assignee_names`)。
---
## 6. 前端设计
### 6.1 新增组件
| 组件 | 职责 |
|------|------|
| `DispatchStepDialog.vue` | 步骤派发表格:工序名、负责人多选、计划开始时间;提交 §5.1 |
### 6.2 改造组件
| 组件 | 变更 |
|------|------|
| `DispatchTaskDialog.vue` | **移除** 任务级「负责人」表单项与校验;选工艺路线且创建成功后 **可选打开** `DispatchStepDialog`(B) |
| `WorkOrderTaskSubList.vue` | 管理视角:任务卡片 meta 展示各步负责人 Tag + 计划开始时间;按钮「步骤派发 / 调整派发」 |
| `TaskExecutionDialog.vue` | mine:`canEditStep(step)` = 当前用户在 step.assigneeUserIds;否则只读;未派步骤不渲染或 manage 才显示 |
| `StepDetailPanel.vue` | 禁用开始/完成/保存按钮当 `!canEditStep` |
| `useTaskStepEditor.ts` | 传入 `operateScope`;步骤操作前校验 assignee |
| `production-task.ts` | 新增 `fetchDispatchProductionTaskSteps`;创建 body 去掉 assigneeUserId |
| `production-mine-task-groups.ts` | mine 列表依赖后端新过滤(前端无需按 task assignee 过滤) |
### 6.3 步骤派发表格 UI
| 列 | 控件 |
|----|------|
| 序号 / 工序名称 | 只读 |
| 负责人 | `el-select multiple filterable remote`,展示 `nickName(userNo)`,≥1 |
| 计划开始时间 | `el-date-picker type="datetime"`,可选 |
- 弹窗:`dialog-form-layout`,`label-position="top"`。
- 底栏:取消 / 确定(Close / Check 图标)。
### 6.4 执行弹窗 · 步骤列表视觉
- **可操作步骤**:现有高亮样式(`is-active`)。
- **只读步骤**:降低 opacity 或 `readonly` class;隐藏开始/完成/保存;保留状态 Tag。
- **未派步骤(mine)**:不展示(manage 正常展示并可操作)。
### 6.5 Deprecated UI
- 任务级「转发」「改派」按钮:管理页 **隐藏或替换** 为「步骤派发」;API 保留不删。
---
## 7. 改派规则
| 步骤状态 | 改负责人 | 改计划开始时间 | 步骤是否 reopen |
|----------|----------|----------------|-----------------|
| `dzx` | ✅ | ✅ | — |
| `jxz` | ✅ | ✅ | 否,新负责人接手进行中步骤 |
| `ywc` | ✅(仅影响 mine 可见性) | ✅ | **否** |
---
## 8. 任务 / 工单状态联动
- **任务状态**(`dpz/jxz/ywc`)逻辑 **不变**:仍由步骤聚合 + 现有 `sync_production_work_order_status_from_tasks` 驱动。
- **未派步骤** 不参与 mine 过滤;manage 仍可执行未派步骤(管理员兜底)。
---
## 9. 权限与菜单
| 能力 | menuCode |
|------|----------|
| 生产工单(manage、步骤派发) | `production:work-order:list` |
| 我的生产任务 | `production:my-task:list` |
无需新增菜单;步骤派发挂在工单页任务操作区。
---
## 10. 测试要点
1. B 入口:选路线 → 创建 → 步骤派发 → 各 assignee 在 mine 见任务,仅自己步骤可操作。
2. A 入口:无路线创建 → 手工加步骤 → 步骤派发 → 同上。
3. 一步多人:A、B 均可见任务;A 完成后 B 见只读已完成。
4. 同人多步:mine 展开见多个可操作步骤。
5. 计划开始时间:列表/卡片展示;未到时间仍可执行。
6. 未派步骤:mine 不可见;manage 可见可操作。
7. 改派:`dzx/jxz/ywc` 按 §7;mine 不可改派。
8. 旧任务兼容(若跑迁移脚本):原 task assignee 仍可见。
---
## 11. 文件清单(实施参考)
| 层 | 文件 |
|----|------|
| SQL | `docs/sql/migrations/2026-06-25-production-step-dispatch.sql`,同步 `docs/sql/zkxw.sql` |
| Backend Model | `backend/app/models/models.py` — `BizProductionTaskStepAssignee` + 步骤新字段 |
| Backend CRUD | `backend/app/crud/production_task_step_assignee.py`(新建,对齐 qc_task_assignee) |
| Backend CRUD | `backend/app/crud/production_task.py` — mine 过滤、step assert、complete 写 completed_by |
| Backend Schema | `backend/app/schemas/production_task.py` — DispatchStepsBody |
| Backend API | `backend/app/api/production.py` — PUT steps/dispatch;调整 create/mine |
| Frontend API | `web-admin/src/api/production-task.ts` |
| Frontend | `DispatchStepDialog.vue`(新建) |
| Frontend | `DispatchTaskDialog.vue`, `WorkOrderTaskSubList.vue`, `TaskExecutionDialog.vue`, `StepDetailPanel.vue`, `useTaskStepEditor.ts` |
---
## 12. 与 OpenSpec / 既有 spec 关系
- supersede 部分:`2026-06-13-production-task-assignee-my-tasks-design.md` 中 **任务级单一负责人 + 任务转发** 产品路径;本 spec 以 **步骤级多人 + 步骤改派** 为准。
- 对齐 `api-conventions`:租户隔离、软删除、`operateScope=mine|manage`。
- 对齐 `ui-conventions`:Fresh Mint 弹窗、按钮图标、Tag 展示负责人。
---
## 13. 版本记录
| 日期 | 说明 |
|------|------|
| 2026-06-25 | 初稿:步骤级派发方案 1,产品决策 D1–D11 已确认 |
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+90 -118
View File
@@ -1,11 +1,11 @@
# Graph Report - src (2026-06-24)
# Graph Report - src (2026-06-25)
## Corpus Check
- Large corpus: 791 files · ~527,792 words. Semantic extraction will be expensive (many Claude tokens). Consider running on a subfolder, or use --no-semantic to run AST-only.
- Large corpus: 792 files · ~529,390 words. Semantic extraction will be expensive (many Claude tokens). Consider running on a subfolder, or use --no-semantic to run AST-only.
## Summary
- 2609 nodes · 3608 edges · 57 communities detected
- Extraction: 78% EXTRACTED · 22% INFERRED · 0% AMBIGUOUS · INFERRED: 782 edges (avg confidence: 0.8)
- 2624 nodes · 3636 edges · 53 communities detected
- Extraction: 78% EXTRACTED · 22% INFERRED · 0% AMBIGUOUS · INFERRED: 785 edges (avg confidence: 0.8)
- Token cost: 0 input · 0 output
## Community Hubs (Navigation)
@@ -50,22 +50,18 @@
- [[_COMMUNITY_Community 39|Community 39]]
- [[_COMMUNITY_Community 40|Community 40]]
- [[_COMMUNITY_Community 41|Community 41]]
- [[_COMMUNITY_Community 42|Community 42]]
- [[_COMMUNITY_Community 44|Community 44]]
- [[_COMMUNITY_Community 45|Community 45]]
- [[_COMMUNITY_Community 46|Community 46]]
- [[_COMMUNITY_Community 48|Community 48]]
- [[_COMMUNITY_Community 49|Community 49]]
- [[_COMMUNITY_Community 50|Community 50]]
- [[_COMMUNITY_Community 51|Community 51]]
- [[_COMMUNITY_Community 52|Community 52]]
- [[_COMMUNITY_Community 53|Community 53]]
- [[_COMMUNITY_Community 54|Community 54]]
- [[_COMMUNITY_Community 55|Community 55]]
- [[_COMMUNITY_Community 56|Community 56]]
- [[_COMMUNITY_Community 57|Community 57]]
- [[_COMMUNITY_Community 60|Community 60]]
- [[_COMMUNITY_Community 61|Community 61]]
- [[_COMMUNITY_Community 62|Community 62]]
- [[_COMMUNITY_Community 67|Community 67]]
- [[_COMMUNITY_Community 69|Community 69]]
- [[_COMMUNITY_Community 58|Community 58]]
- [[_COMMUNITY_Community 63|Community 63]]
- [[_COMMUNITY_Community 65|Community 65]]
## God Nodes (most connected - your core abstractions)
1. `get()` - 268 edges
@@ -76,8 +72,8 @@
6. `handleDynamicRoutes()` - 16 edges
7. `withFullscreenLoading()` - 15 edges
8. `VersionManager` - 15 edges
9. `set()` - 15 edges
10. `TableCache` - 14 edges
9. `scopeQuery()` - 15 edges
10. `set()` - 15 edges
## Surprising Connections (you probably didn't know these)
- `buildInboundMetaFields()` --calls--> `push()` [INFERRED]
@@ -95,15 +91,15 @@
### Community 0 - "Community 0"
Cohesion: 0.01
Nodes (142): fetchAccessLogList(), fetchActiveRegions(), fetchCategoryList(), fetchCategoryParameters(), fetchCellProductPriceList(), fetchChannelAccount(), fetchChannelAgreementPrices(), fetchChannelAgreements() (+134 more)
Nodes (134): fetchAccessLogList(), fetchActiveRegions(), fetchCategoryList(), fetchCategoryParameters(), fetchCellProductPriceList(), fetchChannelAccount(), fetchChannelAgreementPrices(), fetchChannelAgreements() (+126 more)
### Community 1 - "Community 1"
Cohesion: 0.02
Nodes (42): fetchAllConsumablePacks(), fetchAllConsumables(), fetchAllConsumableStockLogs(), fetchAllEquipments(), fetchAllOutputTypes(), fetchAllProcessCategories(), fetchAllProcesses(), fetchAllReagents() (+34 more)
Cohesion: 0.03
Nodes (70): fetchCellSampleTypeList(), fetchCheckupInstitutionInvoices(), fetchCheckupInstitutionList(), fetchCheckupInstitutionProjects(), fetchCreateCellSampleType(), fetchCreateCheckupInstitution(), fetchCreateCheckupInstitutionInvoice(), fetchCreateCheckupInstitutionProject() (+62 more)
### Community 2 - "Community 2"
Cohesion: 0.03
Nodes (76): batchSavePilotCheckup(), deletePilotCheckupActualItem(), fetchCheckupInstitutionPackageDetail(), fetchCheckupInstitutionPackages(), fetchPilotCheckupDetail(), markPilotCheckupChecked(), savePilotCheckup(), savePilotCheckupActualItems() (+68 more)
Cohesion: 0.02
Nodes (42): fetchAllConsumablePacks(), fetchAllConsumables(), fetchAllConsumableStockLogs(), fetchAllEquipments(), fetchAllOutputTypes(), fetchAllProcessCategories(), fetchAllProcesses(), fetchAllReagents() (+34 more)
### Community 3 - "Community 3"
Cohesion: 0.03
@@ -114,28 +110,28 @@ Cohesion: 0.03
Nodes (44): handleBlur(), handleFocus(), handleInput(), formatAmount(), formatAmount(), formatProtocolAmount(), fmtMoney(), buildBaseFields() (+36 more)
### Community 5 - "Community 5"
Cohesion: 0.03
Nodes (52): fetchCheckupInstitutionInvoices(), fetchCheckupInstitutionList(), fetchCheckupInstitutionProjects(), fetchCreateCheckupInstitution(), fetchCreateCheckupInstitutionInvoice(), fetchCreateCheckupInstitutionProject(), fetchUpdateCheckupInstitution(), fetchUpdateCheckupInstitutionInvoice() (+44 more)
Cohesion: 0.04
Nodes (72): batchSavePilotCheckup(), deletePilotCheckupActualItem(), fetchCheckupInstitutionPackageDetail(), fetchCheckupInstitutionPackages(), fetchPilotCheckupDetail(), markPilotCheckupChecked(), savePilotCheckup(), savePilotCheckupActualItems() (+64 more)
### Community 6 - "Community 6"
Cohesion: 0.04
Nodes (61): fetchUploadAttachment(), reload(), handleReportUpload(), handleReportUpload(), walk(), handleMenuJump(), openExternalLink(), async() (+53 more)
Nodes (53): fetchCreateCustomer(), fetchCustomerList(), fetchDeleteCustomer(), fetchUpdateCustomer(), updateInfusionScheduleStatus(), fetchProductionTaskList(), handleAdd(), handleDelete() (+45 more)
### Community 7 - "Community 7"
Cohesion: 0.04
Nodes (51): fetchCreateCustomer(), fetchCustomerList(), fetchDeleteCustomer(), fetchUpdateCustomer(), updateInfusionScheduleStatus(), fetchProductionTaskList(), handleDelete(), handleDropdownCommand() (+43 more)
Cohesion: 0.05
Nodes (57): fetchUploadAttachment(), FireworkSystem, handleReportUpload(), handleReportUpload(), walk(), async(), handleAttachmentChange(), push() (+49 more)
### Community 8 - "Community 8"
Cohesion: 0.04
Nodes (41): fetchMyProductionTasks(), fetchMyQcTaskList(), fetchQcTaskDetail(), fetchQcTaskList(), for(), set(), annotateForkEdges(), chainApiToElements() (+33 more)
Nodes (42): fetchMyProductionTasks(), fetchMyQcTaskList(), fetchQcTaskDetail(), fetchQcTaskList(), for(), set(), annotateForkEdges(), chainApiToElements() (+34 more)
### Community 9 - "Community 9"
Cohesion: 0.04
Nodes (40): fetchCreateMenu(), fetchDeleteMenu(), fetchMenuTree(), fetchRoleOptions(), fetchUpdateMenu(), collectIds(), buildInboundBasicSection(), buildInboundCellStorageViewSections() (+32 more)
Nodes (44): fetchFullmoonTodoList(), fetchUpdateFullmoonTodoInsurance(), fetchCollectionProtocolAuditList(), fetchCollectionProtocolDetail(), fetchCollectionProtocolList(), fetchCreateProtocolInvoiceRequest(), fetchInvoiceEligiblePayments(), fetchPaymentDetailList() (+36 more)
### Community 10 - "Community 10"
Cohesion: 0.04
Nodes (43): fetchFullmoonTodoList(), fetchUpdateFullmoonTodoInsurance(), fetchCollectionProtocolAuditList(), fetchCollectionProtocolDetail(), fetchCollectionProtocolList(), fetchCreateProtocolInvoiceRequest(), fetchInvoiceEligiblePayments(), fetchPaymentDetailList() (+35 more)
Nodes (37): fetchCreateMenu(), fetchDeleteMenu(), fetchMenuTree(), fetchRoleOptions(), fetchUpdateMenu(), collectIds(), buildInboundBasicSection(), buildInboundCellStorageViewSections() (+29 more)
### Community 11 - "Community 11"
Cohesion: 0.04
@@ -146,77 +142,77 @@ Cohesion: 0.05
Nodes (34): fetchClinicalPaymentDetail(), fetchClinicalPayments(), fetchClinicalReceiptConfirmed(), fetchClinicalReceiptPending(), fetchClinicalRefundConfirmed(), fetchClinicalRefundPending(), fetchOrderAdjusts(), fetchOrderDetail() (+26 more)
### Community 13 - "Community 13"
Cohesion: 0.05
Nodes (31): fetchDeleteQcMaterialBatch(), fetchQcMaterialBatchList(), fetchQcMaterialBatchOptions(), fetchQcMaterialList(), fetchQcMaterialOptions(), fetchUpdateQcMaterialBatch(), fetchMarkQcMaterialUsageFullyUsed(), fetchMyQcMaterialUsageList() (+23 more)
### Community 14 - "Community 14"
Cohesion: 0.06
Nodes (11): fetchGetLatestUpgradeLog(), getDefaultLanguage(), checkStorageCompatibility(), StorageConfig, getSystemStorage(), getSysVersion(), StorageKeyManager, StorageCompatibilityManager (+3 more)
### Community 14 - "Community 14"
Cohesion: 0.05
Nodes (26): fetchDeleteQcMaterialBatch(), fetchQcMaterialBatchList(), fetchQcMaterialBatchOptions(), fetchQcMaterialList(), fetchQcMaterialOptions(), fetchUpdateQcMaterialBatch(), fetchMarkQcMaterialUsageFullyUsed(), financeAttachmentsToUrlString() (+18 more)
### Community 15 - "Community 15"
Cohesion: 0.06
Nodes (25): useSettingsHandlers(), useSettingsPanel(), useSettingsState(), RoutePermissionValidator, useCeremony(), useTheme(), walk(), themeAnimation() (+17 more)
Cohesion: 0.05
Nodes (30): useSettingsHandlers(), useSettingsPanel(), useSettingsState(), RoutePermissionValidator, useCeremony(), useTheme(), collectAllPostBindDeptIds(), collectPostBindDeptSubtreeIds() (+22 more)
### Community 16 - "Community 16"
Cohesion: 0.07
Nodes (33): fetchUserList(), fetchAddTaskStep(), fetchCopyTaskStep(), fetchDeleteTaskStep(), fetchForwardProductionTask(), fetchProductionTaskDetail(), fetchReassignProductionTask(), fetchReorderTaskSteps() (+25 more)
Cohesion: 0.06
Nodes (36): fetchUserList(), fetchAddTaskStep(), fetchCopyTaskStep(), fetchDeleteTaskStep(), fetchDispatchProductionTaskSteps(), fetchForwardProductionTask(), fetchProductionTaskDetail(), fetchReassignProductionTask() (+28 more)
### Community 17 - "Community 17"
Cohesion: 0.04
Nodes (21): fetchAvailableMaterialBatches(), fetchMyProductionMaterialConsumptions(), fetchMyProductionMaterialUsageList(), fetchProductionMaterialBatchList(), fetchProductionMaterialBatchOptions(), fetchProductionMaterialConsumptions(), fetchProductionMaterialFormula(), fetchProductionMaterialLinkedStepUsages() (+13 more)
### Community 18 - "Community 18"
Cohesion: 0.1
Nodes (2): WebSocketClient, TableCache
### Community 18 - "Community 18"
### Community 19 - "Community 19"
Cohesion: 0.08
Nodes (32): toLogin(), base64ToBytes(), bytesToBase64(), decryptJson(), deriveKey(), encryptJson(), fromBase64Url(), hexToBytes() (+24 more)
### Community 19 - "Community 19"
### Community 20 - "Community 20"
Cohesion: 0.08
Nodes (27): fetchUserOptions(), fetchSampleReceptionCheckItemList(), fetchSampleReceptionCheckItemsActive(), fetchAllocateLabStorageCodes(), fetchSampleReceptionBySampleCode(), fetchSampleReceptionDetail(), fetchSampleReceptionPendingList(), fetchSampleReceptionReceivedList() (+19 more)
### Community 20 - "Community 20"
### Community 21 - "Community 21"
Cohesion: 0.08
Nodes (20): maskIdCard(), buildBasicExperimentApplicationFields(), buildDefaultApplicationFields(), buildOutboundPilotContextSections(), applicationStatusLabel(), applicationStatusTagType(), cellProductStatusLabel(), cellProductStatusTagType() (+12 more)
### Community 21 - "Community 21"
Cohesion: 0.09
Nodes (28): completePilotApplication(), deletePilotApplication(), fetchPilotApplicationList(), startPilotApplication(), voidPilotApplication(), clearTableSelection(), handleBatchComplete(), handleBatchStart() (+20 more)
### Community 22 - "Community 22"
Cohesion: 0.07
Nodes (17): fetchEthnicList(), applyDonationBundleToForm(), createEmptyTransport(), ensureEthnicOptionInList(), handleOpenForAdd(), loadEthnicOptions(), normalizeSampleRow(), resetForm() (+9 more)
### Community 23 - "Community 23"
Cohesion: 0.09
Nodes (27): completePilotApplication(), deletePilotApplication(), fetchPilotApplicationList(), startPilotApplication(), voidPilotApplication(), handleBatchComplete(), handleBatchStart(), handleBatchVoid() (+19 more)
### Community 24 - "Community 24"
Cohesion: 0.07
Nodes (16): fetchDeleteMaterialBatch(), fetchMaterialBatchList(), fetchMaterialBatchOptions(), fetchMaterialList(), fetchMaterialOptions(), fetchUpdateMaterialBatch(), fetchMaterialUsageList(), fetchMyMaterialUsageList() (+8 more)
### Community 24 - "Community 24"
Cohesion: 0.1
Nodes (28): fetchCellProductSpecList(), fetchCellProductTypeList(), fetchMaxPassageProductSeq(), handleAdd(), resetForm(), applyProductCodeToLines(), calcExpiryFromFreeze(), createEmptyProductLine() (+20 more)
### Community 25 - "Community 25"
Cohesion: 0.08
Nodes (12): fetchCreateProductionWorkOrder(), fetchProductionWorkOrderCellProductOptions(), fetchProductionWorkOrderDetail(), fetchProductionWorkOrderList(), fetchProductionWorkOrderQcDispatchOptions(), reload(), handleMenuJump(), openExternalLink() (+4 more)
### Community 26 - "Community 26"
Cohesion: 0.11
Nodes (21): fetchGetMenuCodes(), fetchGetUserInfo(), fetchLogin(), fetchRefreshToken(), fetchUpdateProfile(), fetchUploadProfileAvatar(), fetchUserInfo(), buildWebEncryptedLoginBody() (+13 more)
### Community 26 - "Community 26"
### Community 27 - "Community 27"
Cohesion: 0.12
Nodes (24): fetchCellProductSpecList(), fetchCellProductTypeList(), applyProductCodeToLines(), calcExpiryFromFreeze(), createEmptyProductLine(), createQuantityValue(), getLineStorageSlotOptions(), getOtherLineStorageSlotIds() (+16 more)
### Community 28 - "Community 28"
Cohesion: 0.12
Nodes (15): addCopyButton(), highlightCode(), insertLineNumbers(), isBlockProcessed(), markBlockAsProcessed(), processBlock(), setupHighlightDirective(), setupRippleDirective() (+7 more)
### Community 27 - "Community 27"
Cohesion: 0.13
Nodes (18): fetchCellSampleTypeList(), fetchCreateCellSampleType(), fetchDeleteCellSampleType(), fetchUpdateCellSampleType(), handleAdd(), handleDelete(), handleDropdownCommand(), handleEdit() (+10 more)
### Community 28 - "Community 28"
### Community 29 - "Community 29"
Cohesion: 0.11
Nodes (11): fetchOutboundReleaseMaterial(), fetchOutboundReleaseReviewDetail(), fetchOutboundReleaseReviewList(), fetchReleaseReviewList(), handleReset(), handleSearch(), handleToggleSelect(), loadData() (+3 more)
### Community 29 - "Community 29"
### Community 30 - "Community 30"
Cohesion: 0.16
Nodes (13): handleChineseDatetimePaste(), parseChineseDatetimeText(), isMaternalBloodEmpty(), matchCellSampleType(), normalizeLine(), parseDatetime(), parseDeliveryMode(), parseDonationStandardImportText() (+5 more)
### Community 30 - "Community 30"
Cohesion: 0.12
Nodes (12): fetchApproveTask(), fetchBusinessAllTasks(), fetchCompletedTasks(), fetchDesignerData(), fetchInstanceDetail(), fetchMyInstances(), fetchPendingTasks(), fetchProcessDefList() (+4 more)
### Community 31 - "Community 31"
Cohesion: 0.16
Nodes (14): fetchCreateEnvDetectItem(), fetchDeleteEnvDetectItem(), fetchEnvDetectItemDetail(), fetchEnvDetectItemList(), fetchEnvDetectItemsActive(), fetchUpdateEnvDetectItem(), handleAdd(), handleBatchDelete() (+6 more)
@@ -238,132 +234,108 @@ Cohesion: 0.19
Nodes (6): fetchServiceOrderTxnList(), handleReset(), handleSearch(), handleTabChange(), loadData(), switchTab()
### Community 37 - "Community 37"
Cohesion: 0.15
Nodes (4): fetchNextDonationSampleCode(), fetchResearchDonationAuditList(), fetchResearchDonationDetail(), fetchResearchDonationList()
### Community 38 - "Community 38"
Cohesion: 0.18
Nodes (3): normalizeStepSubTables(), parseEquipmentIds(), syncEquipmentNamesFromOptions()
### Community 38 - "Community 38"
### Community 39 - "Community 39"
Cohesion: 0.24
Nodes (6): buildInboundMetaFields(), getCardMetaConfig(), getCardMetaConfigForLegacyTab(), parseReceptionTime(), receptionOver24HoursLabel(), receptionOver24HoursTagType()
### Community 39 - "Community 39"
### Community 40 - "Community 40"
Cohesion: 0.42
Nodes (9): buildPaymentFlowSteps(), dash(), formatAmount(), hasFinanceReceipt(), hasFinanceRefund(), hasReceiptSubmit(), hasRefundSubmit(), receiptSubmitTime() (+1 more)
### Community 40 - "Community 40"
Cohesion: 0.2
Nodes (3): fetchProductionWorkOrderCellProductOptions(), fetchProductionWorkOrderDetail(), fetchProductionWorkOrderList()
### Community 41 - "Community 41"
Cohesion: 0.25
Nodes (2): handleTouchEnd(), setTransition()
### Community 42 - "Community 42"
Cohesion: 0.28
Nodes (1): FireworkSystem
### Community 45 - "Community 45"
### Community 44 - "Community 44"
Cohesion: 0.39
Nodes (6): isIgnorableScriptError(), normalizeErrorMessage(), registerPromiseErrorHandler(), registerResourceErrorHandler(), scriptErrorHandler(), setupErrorHandle()
### Community 46 - "Community 46"
### Community 45 - "Community 45"
Cohesion: 0.36
Nodes (1): TableHeightCalculator
### Community 49 - "Community 49"
### Community 48 - "Community 48"
Cohesion: 0.33
Nodes (2): logApiCryptoBootInfo(), maskSecret()
### Community 50 - "Community 50"
### Community 49 - "Community 49"
Cohesion: 0.29
Nodes (2): fetchLeaveDetail(), fetchLeaveList()
### Community 51 - "Community 51"
### Community 50 - "Community 50"
Cohesion: 0.47
Nodes (3): isPaymentDueWithinWarnWindow(), parsePaymentDueYmd(), startOfLocalDay()
### Community 53 - "Community 53"
Cohesion: 0.33
Nodes (2): fetchQcThirdPartyCompanyList(), fetchQcThirdPartyCompanyOptions()
### Community 54 - "Community 54"
Cohesion: 0.33
Nodes (2): fetchQcLabTestItemList(), fetchQcLabTestItemsActive()
### Community 55 - "Community 55"
### Community 52 - "Community 52"
Cohesion: 0.5
Nodes (2): formatProtocolDocDeleteConfirm(), formatProtocolDocSubject()
### Community 56 - "Community 56"
### Community 53 - "Community 53"
Cohesion: 0.5
Nodes (2): focusInput(), handleKeydown()
### Community 57 - "Community 57"
### Community 54 - "Community 54"
Cohesion: 0.67
Nodes (2): if(), logSidebarMenu()
### Community 60 - "Community 60"
Cohesion: 0.5
Nodes (1): fetchStorageEntrySmsList()
### Community 61 - "Community 61"
### Community 57 - "Community 57"
Cohesion: 0.5
Nodes (1): fetchSampleSmsNotificationList()
### Community 62 - "Community 62"
### Community 58 - "Community 58"
Cohesion: 0.5
Nodes (1): fetchQcSmsList()
### Community 67 - "Community 67"
### Community 63 - "Community 63"
Cohesion: 1.0
Nodes (2): getSettingDefaults(), resetToDefaults()
### Community 69 - "Community 69"
### Community 65 - "Community 65"
Cohesion: 1.0
Nodes (2): copyTextToClipboard(), copyTextWithFallback()
## Knowledge Gaps
- **Thin community `Community 17`** (42 nodes): `WebSocketClient`, `.calculateReconnectDelay()`, `.clearAllTimers()`, `.clearConnectionTimers()`, `.clearTimer()`, `.close()`, `.closeCurrentSocketForReconnect()`, `.connect()`, `.connectionStatusText()`, `.constructor()`, `.destroyInstance()`, `.flushMessageQueue()`, `.getInstance()`, `.getReadyStateText()`, `.handleClose()`, `.handleConnectionTimeout()`, `.handleError()`, `.handleMessage()`, `.handleOpen()`, `.init()`, `.isWebSocketConnected()`, `.reconnect()`, `.resetHeartbeat()`, `.send()`, `.startHeartbeat()`, `.startPing()`, `TableCache`, `.cleanupExpired()`, `.clear()`, `.clearByTags()`, `.clearCurrentSearch()`, `.clearPagination()`, `.constructor()`, `.evictLRU()`, `.generateKey()`, `.generateTags()`, `.get()`, `.getStats()`, `.log()`, `.set()`, `index.ts`, `tableCache.ts`
- **Thin community `Community 18`** (42 nodes): `WebSocketClient`, `.calculateReconnectDelay()`, `.clearAllTimers()`, `.clearConnectionTimers()`, `.clearTimer()`, `.close()`, `.closeCurrentSocketForReconnect()`, `.connect()`, `.connectionStatusText()`, `.constructor()`, `.destroyInstance()`, `.flushMessageQueue()`, `.getInstance()`, `.getReadyStateText()`, `.handleClose()`, `.handleConnectionTimeout()`, `.handleError()`, `.handleMessage()`, `.handleOpen()`, `.init()`, `.isWebSocketConnected()`, `.reconnect()`, `.resetHeartbeat()`, `.send()`, `.startHeartbeat()`, `.startPing()`, `TableCache`, `.cleanupExpired()`, `.clear()`, `.clearByTags()`, `.clearCurrentSearch()`, `.clearPagination()`, `.constructor()`, `.evictLRU()`, `.generateKey()`, `.generateTags()`, `.get()`, `.getStats()`, `.log()`, `.set()`, `index.ts`, `tableCache.ts`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 41`** (9 nodes): `cleanupEventListeners()`, `handleTouchEnd()`, `handleTouchMove()`, `handleTouchStart()`, `handleWheelScroll()`, `if()`, `setTransition()`, `setupEventListeners()`, `index.vue`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 42`** (9 nodes): `FireworkSystem`, `.constructor()`, `.createFirework()`, `.createParticle()`, `.getAvailableParticle()`, `.initializePool()`, `.preloadAllImages()`, `.preloadImage()`, `index.vue`
- **Thin community `Community 45`** (8 nodes): `TableHeightCalculator`, `.calculate()`, `.calculateOffset()`, `.calculatePaginationOffset()`, `.constructor()`, `.getHeaderHeight()`, `useTableHeight()`, `useTableHeight.ts`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 46`** (8 nodes): `TableHeightCalculator`, `.calculate()`, `.calculateOffset()`, `.calculatePaginationOffset()`, `.constructor()`, `.getHeaderHeight()`, `useTableHeight()`, `useTableHeight.ts`
- **Thin community `Community 48`** (7 nodes): `App.vue`, `main.ts`, `refreshButtonAutoIcons()`, `refreshPanelTitleIcons()`, `logApiCryptoBootInfo()`, `maskSecret()`, `console.ts`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 49`** (7 nodes): `App.vue`, `main.ts`, `refreshButtonAutoIcons()`, `refreshPanelTitleIcons()`, `logApiCryptoBootInfo()`, `maskSecret()`, `console.ts`
- **Thin community `Community 49`** (7 nodes): `fetchCreateLeave()`, `fetchDeleteLeave()`, `fetchLeaveDetail()`, `fetchLeaveList()`, `fetchSubmitLeave()`, `fetchUpdateLeave()`, `leave.ts`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 50`** (7 nodes): `fetchCreateLeave()`, `fetchDeleteLeave()`, `fetchLeaveDetail()`, `fetchLeaveList()`, `fetchSubmitLeave()`, `fetchUpdateLeave()`, `leave.ts`
- **Thin community `Community 52`** (5 nodes): `formatProtocolDocBatchDeleteConfirm()`, `formatProtocolDocDeleteConfirm()`, `formatProtocolDocSubject()`, `getProtocolDocTypeLabel()`, `protocol-doc-label.ts`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 53`** (6 nodes): `fetchCreateQcThirdPartyCompany()`, `fetchDeleteQcThirdPartyCompany()`, `fetchQcThirdPartyCompanyList()`, `fetchQcThirdPartyCompanyOptions()`, `fetchUpdateQcThirdPartyCompany()`, `qc-third-party-company.ts`
- **Thin community `Community 53`** (5 nodes): `focusInput()`, `handleKeydown()`, `if()`, `search()`, `index.vue`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 54`** (6 nodes): `fetchCreateQcLabTestItem()`, `fetchDeleteQcLabTestItem()`, `fetchQcLabTestItemList()`, `fetchQcLabTestItemsActive()`, `fetchUpdateQcLabTestItem()`, `qc-lab-test-item.ts`
- **Thin community `Community 54`** (4 nodes): `if()`, `logSidebarMenu()`, `index.vue`, `SidebarSubmenu.vue`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 55`** (5 nodes): `formatProtocolDocBatchDeleteConfirm()`, `formatProtocolDocDeleteConfirm()`, `formatProtocolDocSubject()`, `getProtocolDocTypeLabel()`, `protocol-doc-label.ts`
- **Thin community `Community 57`** (4 nodes): `fetchGenerateSms()`, `fetchSampleSmsNotificationList()`, `fetchToggleSmsNotified()`, `sample-sms-notification.ts`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 56`** (5 nodes): `focusInput()`, `handleKeydown()`, `if()`, `search()`, `index.vue`
- **Thin community `Community 58`** (4 nodes): `fetchGenerateQcSms()`, `fetchQcSmsList()`, `fetchToggleQcSmsNotified()`, `qc-sms-notification.ts`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 57`** (4 nodes): `if()`, `logSidebarMenu()`, `index.vue`, `SidebarSubmenu.vue`
- **Thin community `Community 63`** (3 nodes): `getSettingDefaults()`, `resetToDefaults()`, `setting.ts`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 60`** (4 nodes): `fetchGenerateStorageEntrySms()`, `fetchStorageEntrySmsList()`, `fetchToggleStorageEntrySmsNotified()`, `storage-entry-sms-notification.ts`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 61`** (4 nodes): `fetchGenerateSms()`, `fetchSampleSmsNotificationList()`, `fetchToggleSmsNotified()`, `sample-sms-notification.ts`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 62`** (4 nodes): `fetchGenerateQcSms()`, `fetchQcSmsList()`, `fetchToggleQcSmsNotified()`, `qc-sms-notification.ts`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 67`** (3 nodes): `getSettingDefaults()`, `resetToDefaults()`, `setting.ts`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 69`** (3 nodes): `copyTextToClipboard()`, `copyTextWithFallback()`, `copy-text.ts`
- **Thin community `Community 65`** (3 nodes): `copyTextToClipboard()`, `copyTextWithFallback()`, `copy-text.ts`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
## Suggested Questions
_Questions this graph is uniquely positioned to answer:_
- **Why does `get()` connect `Community 0` to `Community 1`, `Community 2`, `Community 5`, `Community 6`, `Community 7`, `Community 8`, `Community 9`, `Community 10`, `Community 12`, `Community 13`, `Community 14`, `Community 15`, `Community 16`, `Community 19`, `Community 21`, `Community 22`, `Community 23`, `Community 24`, `Community 25`, `Community 27`, `Community 28`, `Community 30`, `Community 31`, `Community 35`, `Community 40`, `Community 50`, `Community 53`, `Community 54`, `Community 60`, `Community 61`, `Community 62`?**
_High betweenness centrality (0.367) - this node is a cross-community bridge._
- **Why does `push()` connect `Community 6` to `Community 0`, `Community 1`, `Community 2`, `Community 4`, `Community 5`, `Community 7`, `Community 8`, `Community 9`, `Community 10`, `Community 13`, `Community 15`, `Community 16`, `Community 17`, `Community 18`, `Community 20`, `Community 21`, `Community 22`, `Community 24`, `Community 38`, `Community 42`?**
_High betweenness centrality (0.172) - this node is a cross-community bridge._
- **Why does `handleDynamicRoutes()` connect `Community 3` to `Community 25`, `Community 15`?**
_High betweenness centrality (0.036) - this node is a cross-community bridge._
- **Why does `get()` connect `Community 0` to `Community 1`, `Community 2`, `Community 5`, `Community 6`, `Community 7`, `Community 8`, `Community 9`, `Community 10`, `Community 12`, `Community 13`, `Community 14`, `Community 15`, `Community 16`, `Community 17`, `Community 20`, `Community 22`, `Community 23`, `Community 24`, `Community 25`, `Community 26`, `Community 27`, `Community 29`, `Community 31`, `Community 35`, `Community 37`, `Community 49`, `Community 57`, `Community 58`?**
_High betweenness centrality (0.322) - this node is a cross-community bridge._
- **Why does `push()` connect `Community 7` to `Community 0`, `Community 1`, `Community 2`, `Community 4`, `Community 5`, `Community 6`, `Community 8`, `Community 9`, `Community 10`, `Community 14`, `Community 15`, `Community 16`, `Community 18`, `Community 19`, `Community 21`, `Community 22`, `Community 23`, `Community 25`, `Community 27`, `Community 39`?**
_High betweenness centrality (0.154) - this node is a cross-community bridge._
- **Why does `handleDynamicRoutes()` connect `Community 3` to `Community 26`, `Community 15`?**
_High betweenness centrality (0.031) - this node is a cross-community bridge._
- **Are the 267 inferred relationships involving `get()` (e.g. with `buildNodeCodePathFromFlat()` and `collectLeafFacilityNodesInTreeOrder()`) actually correct?**
_`get()` has 267 INFERRED edges - model-reasoned connections that need verification._
- **Are the 63 inferred relationships involving `push()` (e.g. with `collectLeafFacilityNodesInTreeOrder()` and `formatDetectMethods()`) actually correct?**
+4 -4
View File
@@ -57,12 +57,12 @@
</div>
<div id="legend"></div>
</div>
<div id="stats">2609 nodes &middot; 3608 edges &middot; 324 communities</div>
<div id="stats">2624 nodes &middot; 3636 edges &middot; 318 communities</div>
</div>
<script>
const RAW_NODES = [{"id": "app_vue", "label": "App.vue", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "App.vue", "community": 49, "community_name": "Community 49", "source_file": "App.vue", "file_type": "code", "degree": 3}, {"id": "src_app_refreshbuttonautoicons", "label": "refreshButtonAutoIcons()", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "refreshButtonAutoIcons()", "community": 49, "community_name": "Community 49", "source_file": "App.vue", "file_type": "code", "degree": 1}, {"id": "src_app_refreshpaneltitleicons", "label": "refreshPanelTitleIcons()", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "refreshPanelTitleIcons()", "community": 49, "community_name": "Community 49", "source_file": "App.vue", "file_type": "code", "degree": 1}, {"id": "main_ts", "label": "main.ts", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.4, "font": {"size": 0, "color": "#ffffff"}, "title": "main.ts", "community": 49, "community_name": "Community 49", "source_file": "main.ts", "file_type": "code", "degree": 4}, {"id": "src_main_masksecret", "label": "maskSecret()", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "maskSecret()", "community": 49, "community_name": "Community 49", "source_file": "main.ts", "file_type": "code", "degree": 2}, {"id": "src_main_logapicryptobootinfo", "label": "logApiCryptoBootInfo()", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "logApiCryptoBootInfo()", "community": 49, "community_name": "Community 49", "source_file": "main.ts", "file_type": "code", "degree": 2}, {"id": "env_d_ts", "label": "env.d.ts", "color": {"background": "#FF9DA7", "border": "#FF9DA7", "highlight": {"background": "#ffffff", "border": "#FF9DA7"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "env.d.ts", "community": 107, "community_name": "Community 107", "source_file": "env.d.ts", "file_type": "code", "degree": 0}, {"id": "shims_loose_d_ts", "label": "shims-loose.d.ts", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "shims-loose.d.ts", "community": 108, "community_name": "Community 108", "source_file": "shims-loose.d.ts", "file_type": "code", "degree": 0}, {"id": "directives_index_ts", "label": "index.ts", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "index.ts", "community": 26, "community_name": "Community 26", "source_file": "directives/index.ts", "file_type": "code", "degree": 1}, {"id": "directives_index_setupglobdirectives", "label": "setupGlobDirectives()", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.5, "font": {"size": 0, "color": "#ffffff"}, "title": "setupGlobDirectives()", "community": 26, "community_name": "Community 26", "source_file": "directives/index.ts", "file_type": "code", "degree": 5}, {"id": "directives_core_roles_ts", "label": "roles.ts", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "roles.ts", "community": 26, "community_name": "Community 26", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 3}, {"id": "core_roles_checkrolepermission", "label": "checkRolePermission()", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "checkRolePermission()", "community": 26, "community_name": "Community 26", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 2}, {"id": "core_roles_removeelement", "label": "removeElement()", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "removeElement()", "community": 26, "community_name": "Community 26", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 2}, {"id": "core_roles_setuprolesdirective",Line truncated
const RAW_EDGES = [{"from": "app_vue", "to": "src_app_refreshbuttonautoicons", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_vue", "to": "src_app_refreshpaneltitleicons", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_vue", "to": "main_ts", "label": "imports_from", "title": "imports_from [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "utils_sys_console_ts", "label": "imports_from", "title": "imports_from [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "src_main_masksecret", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "src_main_logapicryptobootinfo", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "src_main_masksecret", "to": "src_main_logapicryptobootinfo", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_index_ts", "to": "directives_index_setupglobdirectives", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_index_setupglobdirectives", "to": "core_auth_setupauthdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "core_roles_setuprolesdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "business_highlight_setuphighlightdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "business_ripple_setuprippledirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_core_roles_ts", "to": "core_roles_checkrolepermission", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_roles_ts", "to": "core_roles_removeelement", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_roles_ts", "to": "core_roles_setuprolesdirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_roles_checkrolepermission", "to": "core_roles_removeelement", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_checkauthpermission", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_removeelement", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_setupauthdirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_checkauthpermission", "to": "core_auth_removeelement", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_ripple_ts", "to": "business_ripple_setuprippledirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_highlightcode", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_insertlinenumbers", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_addcopybutton", "label": "contains", "title": "contains [EXTRACTED]", "dLine truncated
const LEGEND = [{"cid": 0, "color": "#4E79A7", "label": "Community 0", "count": 404}, {"cid": 1, "color": "#F28E2B", "label": "Community 1", "count": 104}, {"cid": 2, "color": "#E15759", "label": "Community 2", "count": 98}, {"cid": 3, "color": "#76B7B2", "label": "Community 3", "count": 95}, {"cid": 4, "color": "#59A14F", "label": "Community 4", "count": 91}, {"cid": 5, "color": "#EDC948", "label": "Community 5", "count": 90}, {"cid": 6, "color": "#B07AA1", "label": "Community 6", "count": 83}, {"cid": 7, "color": "#FF9DA7", "label": "Community 7", "count": 83}, {"cid": 8, "color": "#9C755F", "label": "Community 8", "count": 74}, {"cid": 9, "color": "#BAB0AC", "label": "Community 9", "count": 71}, {"cid": 10, "color": "#4E79A7", "label": "Community 10", "count": 68}, {"cid": 11, "color": "#F28E2B", "label": "Community 11", "count": 62}, {"cid": 12, "color": "#E15759", "label": "Community 12", "count": 61}, {"cid": 13, "color": "#76B7B2", "label": "Community 13", "count": 55}, {"cid": 14, "color": "#59A14F", "label": "Community 14", "count": 54}, {"cid": 15, "color": "#EDC948", "label": "Community 15", "count": 46}, {"cid": 16, "color": "#B07AA1", "label": "Community 16", "count": 45}, {"cid": 17, "color": "#FF9DA7", "label": "Community 17", "count": 42}, {"cid": 18, "color": "#9C755F", "label": "Community 18", "count": 40}, {"cid": 19, "color": "#BAB0AC", "label": "Community 19", "count": 39}, {"cid": 20, "color": "#4E79A7", "label": "Community 20", "count": 38}, {"cid": 21, "color": "#F28E2B", "label": "Community 21", "count": 35}, {"cid": 22, "color": "#E15759", "label": "Community 22", "count": 35}, {"cid": 23, "color": "#76B7B2", "label": "Community 23", "count": 33}, {"cid": 24, "color": "#59A14F", "label": "Community 24", "count": 32}, {"cid": 25, "color": "#EDC948", "label": "Community 25", "count": 29}, {"cid": 26, "color": "#B07AA1", "label": "Community 26", "count": 22}, {"cid": 27, "color": "#FF9DA7", "label": "Community 27", "count": 22}, {"cid": 28, "color": "#9C755F", "label": "Community 28", "count": 22}, {"cid": 29, "color": "#BAB0AC", "label": "Community 29", "count": 19}, {"cid": 30, "color": "#4E79A7", "label": "Community 30", "count": 19}, {"cid": 31, "color": "#F28E2B", "label": "Community 31", "count": 19}, {"cid": 32, "color": "#E15759", "label": "Community 32", "count": 17}, {"cid": 33, "color": "#76B7B2", "label": "Community 33", "count": 16}, {"cid": 34, "color": "#59A14F", "label": "Community 34", "count": 14}, {"cid": 35, "color": "#EDC948", "label": "Community 35", "count": 14}, {"cid": 36, "color": "#B07AA1", "label": "Community 36", "count": 13}, {"cid": 37, "color": "#FF9DA7", "label": "Community 37", "count": 12}, {"cid": 38, "color": "#9C755F", "label": "Community 38", "count": 11}, {"cid": 39, "color": "#BAB0AC", "label": "Community 39", "count": 10}, {"cid": 40, "color": "#4E79A7", "label": "Community 40", "count": 10}, {"cid": 41, "color": "#F28E2B", "label": "Community 41", "count": 9}, {"cid": 42, "color": "#E15759", "label": "Community 42", "count": 9}, {"cid": 43, "color": "#76B7B2", "label": "Community 43", "count": 9}, {"cid": 44, "color": "#59A14F", "label": "Community 44", "count": 9}, {"cid": 45, "color": "#EDC948", "label": "Community 45", "count": 8}, {"cid": 46, "color": "#B07AA1", "label": "Community 46", "count": 8}, {"cid": 47, "color": "#FF9DA7", "label": "Community 47", "count": 8}, {"cid": 48, "color": "#9C755F", "label": "Community 48", "count": 8}, {"cid": 49, "color": "#BAB0AC", "label": "Community 49", "count": 7}, {"cid": 50, "color": "#4E79A7", "label": "Community 50", "count": 7}, {"cid": 51, "color": "#F28E2B", "label": "Community 51", "count": 6}, {"cid": 52, "color": "#E15759", "label": "Community 52", "count": 6}, {"cid": 53, "color": "#76B7B2", "label": "Community 53", "count": 6}, {"cid": 54, "color": "#59A14F", "label": "Community 54", "count": 6}, {"cid": 55, "color": "#EDC948", "label": "Community 55", "count": 5}, {"cid": 56, "color": "#B07AA1", "label": "Community 56", "count": 5}, {"cid": 57, "color": "#FF9DA7", "label": "Community 57", "count": 4}, {"cid": 58, "color": "#9C755F", "label": "Community 58", "count": 4}, {"cid": 59, "color": "#BAB0AC", "label": "Community 59", "count": 4}, {"cid": 60, "color": "#4E79A7", "label": "Community 60", "count": 4}, {"cid": 61, "color": "#F28E2B", "label": "Community 61", "count": 4}, {"cid": 62, "color": "#E15759", "label": "Community 62", "count": 4}, {"cid": 63, "color": "#76B7B2", "label": "Community 63", "count": 4}, {"cid": 64, "color": "#59A14F", "label": "Community 64", "count": 4}, {"cid": 65, "color": "#EDC948", "label": "Community 65", "count": 4}, {"cid": 66, "color": "#B07AA1", "label": "Community 66", "count": 4}, {"cid": 67, "color": "#FF9DA7", "label": "Community 67", "count": 3}, {"cid": 68, "color": "#9C755F", "label": "Community 68", "count": 3}, {"cid": 69, "color": "#BAB0AC", "label": "Community 69", "count": 3}, {"cid": 70, "color": "#4E79A7", "label": "Community 70", "coLine truncated
const RAW_NODES = [{"id": "app_vue", "label": "App.vue", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "App.vue", "community": 48, "community_name": "Community 48", "source_file": "App.vue", "file_type": "code", "degree": 3}, {"id": "src_app_refreshbuttonautoicons", "label": "refreshButtonAutoIcons()", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "refreshButtonAutoIcons()", "community": 48, "community_name": "Community 48", "source_file": "App.vue", "file_type": "code", "degree": 1}, {"id": "src_app_refreshpaneltitleicons", "label": "refreshPanelTitleIcons()", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "refreshPanelTitleIcons()", "community": 48, "community_name": "Community 48", "source_file": "App.vue", "file_type": "code", "degree": 1}, {"id": "main_ts", "label": "main.ts", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.4, "font": {"size": 0, "color": "#ffffff"}, "title": "main.ts", "community": 48, "community_name": "Community 48", "source_file": "main.ts", "file_type": "code", "degree": 4}, {"id": "src_main_masksecret", "label": "maskSecret()", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "maskSecret()", "community": 48, "community_name": "Community 48", "source_file": "main.ts", "file_type": "code", "degree": 2}, {"id": "src_main_logapicryptobootinfo", "label": "logApiCryptoBootInfo()", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "logApiCryptoBootInfo()", "community": 48, "community_name": "Community 48", "source_file": "main.ts", "file_type": "code", "degree": 2}, {"id": "env_d_ts", "label": "env.d.ts", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "env.d.ts", "community": 103, "community_name": "Community 103", "source_file": "env.d.ts", "file_type": "code", "degree": 0}, {"id": "shims_loose_d_ts", "label": "shims-loose.d.ts", "color": {"background": "#59A14F", "border": "#59A14F", "highlight": {"background": "#ffffff", "border": "#59A14F"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "shims-loose.d.ts", "community": 104, "community_name": "Community 104", "source_file": "shims-loose.d.ts", "file_type": "code", "degree": 0}, {"id": "directives_index_ts", "label": "index.ts", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "index.ts", "community": 28, "community_name": "Community 28", "source_file": "directives/index.ts", "file_type": "code", "degree": 1}, {"id": "directives_index_setupglobdirectives", "label": "setupGlobDirectives()", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.5, "font": {"size": 0, "color": "#ffffff"}, "title": "setupGlobDirectives()", "community": 28, "community_name": "Community 28", "source_file": "directives/index.ts", "file_type": "code", "degree": 5}, {"id": "directives_core_roles_ts", "label": "roles.ts", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "roles.ts", "community": 28, "community_name": "Community 28", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 3}, {"id": "core_roles_checkrolepermission", "label": "checkRolePermission()", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "checkRolePermission()", "community": 28, "community_name": "Community 28", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 2}, {"id": "core_roles_removeelement", "label": "removeElement()", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "removeElement()", "community": 28, "community_name": "Community 28", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 2}, {"id": "core_roles_setuprolesdirective",Line truncated
const RAW_EDGES = [{"from": "app_vue", "to": "src_app_refreshbuttonautoicons", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_vue", "to": "src_app_refreshpaneltitleicons", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_vue", "to": "main_ts", "label": "imports_from", "title": "imports_from [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "utils_sys_console_ts", "label": "imports_from", "title": "imports_from [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "src_main_masksecret", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "src_main_logapicryptobootinfo", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "src_main_masksecret", "to": "src_main_logapicryptobootinfo", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_index_ts", "to": "directives_index_setupglobdirectives", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_index_setupglobdirectives", "to": "core_auth_setupauthdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "core_roles_setuprolesdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "business_highlight_setuphighlightdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "business_ripple_setuprippledirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_core_roles_ts", "to": "core_roles_checkrolepermission", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_roles_ts", "to": "core_roles_removeelement", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_roles_ts", "to": "core_roles_setuprolesdirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_roles_checkrolepermission", "to": "core_roles_removeelement", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_checkauthpermission", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_removeelement", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_setupauthdirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_checkauthpermission", "to": "core_auth_removeelement", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_ripple_ts", "to": "business_ripple_setuprippledirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_highlightcode", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_insertlinenumbers", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_addcopybutton", "label": "contains", "title": "contains [EXTRACTED]", "dLine truncated
const LEGEND = [{"cid": 0, "color": "#4E79A7", "label": "Community 0", "count": 382}, {"cid": 1, "color": "#F28E2B", "label": "Community 1", "count": 112}, {"cid": 2, "color": "#E15759", "label": "Community 2", "count": 104}, {"cid": 3, "color": "#76B7B2", "label": "Community 3", "count": 95}, {"cid": 4, "color": "#59A14F", "label": "Community 4", "count": 91}, {"cid": 5, "color": "#EDC948", "label": "Community 5", "count": 89}, {"cid": 6, "color": "#B07AA1", "label": "Community 6", "count": 86}, {"cid": 7, "color": "#FF9DA7", "label": "Community 7", "count": 81}, {"cid": 8, "color": "#9C755F", "label": "Community 8", "count": 76}, {"cid": 9, "color": "#BAB0AC", "label": "Community 9", "count": 69}, {"cid": 10, "color": "#4E79A7", "label": "Community 10", "count": 66}, {"cid": 11, "color": "#F28E2B", "label": "Community 11", "count": 62}, {"cid": 12, "color": "#E15759", "label": "Community 12", "count": 61}, {"cid": 13, "color": "#76B7B2", "label": "Community 13", "count": 60}, {"cid": 14, "color": "#59A14F", "label": "Community 14", "count": 55}, {"cid": 15, "color": "#EDC948", "label": "Community 15", "count": 53}, {"cid": 16, "color": "#B07AA1", "label": "Community 16", "count": 50}, {"cid": 17, "color": "#FF9DA7", "label": "Community 17", "count": 48}, {"cid": 18, "color": "#9C755F", "label": "Community 18", "count": 42}, {"cid": 19, "color": "#BAB0AC", "label": "Community 19", "count": 40}, {"cid": 20, "color": "#4E79A7", "label": "Community 20", "count": 39}, {"cid": 21, "color": "#F28E2B", "label": "Community 21", "count": 38}, {"cid": 22, "color": "#E15759", "label": "Community 22", "count": 35}, {"cid": 23, "color": "#76B7B2", "label": "Community 23", "count": 34}, {"cid": 24, "color": "#59A14F", "label": "Community 24", "count": 33}, {"cid": 25, "color": "#EDC948", "label": "Community 25", "count": 29}, {"cid": 26, "color": "#B07AA1", "label": "Community 26", "count": 29}, {"cid": 27, "color": "#FF9DA7", "label": "Community 27", "count": 28}, {"cid": 28, "color": "#9C755F", "label": "Community 28", "count": 22}, {"cid": 29, "color": "#BAB0AC", "label": "Community 29", "count": 22}, {"cid": 30, "color": "#4E79A7", "label": "Community 30", "count": 19}, {"cid": 31, "color": "#F28E2B", "label": "Community 31", "count": 19}, {"cid": 32, "color": "#E15759", "label": "Community 32", "count": 17}, {"cid": 33, "color": "#76B7B2", "label": "Community 33", "count": 16}, {"cid": 34, "color": "#59A14F", "label": "Community 34", "count": 14}, {"cid": 35, "color": "#EDC948", "label": "Community 35", "count": 14}, {"cid": 36, "color": "#B07AA1", "label": "Community 36", "count": 13}, {"cid": 37, "color": "#FF9DA7", "label": "Community 37", "count": 13}, {"cid": 38, "color": "#9C755F", "label": "Community 38", "count": 12}, {"cid": 39, "color": "#BAB0AC", "label": "Community 39", "count": 11}, {"cid": 40, "color": "#4E79A7", "label": "Community 40", "count": 10}, {"cid": 41, "color": "#F28E2B", "label": "Community 41", "count": 9}, {"cid": 42, "color": "#E15759", "label": "Community 42", "count": 9}, {"cid": 43, "color": "#76B7B2", "label": "Community 43", "count": 9}, {"cid": 44, "color": "#59A14F", "label": "Community 44", "count": 8}, {"cid": 45, "color": "#EDC948", "label": "Community 45", "count": 8}, {"cid": 46, "color": "#B07AA1", "label": "Community 46", "count": 8}, {"cid": 47, "color": "#FF9DA7", "label": "Community 47", "count": 8}, {"cid": 48, "color": "#9C755F", "label": "Community 48", "count": 7}, {"cid": 49, "color": "#BAB0AC", "label": "Community 49", "count": 7}, {"cid": 50, "color": "#4E79A7", "label": "Community 50", "count": 6}, {"cid": 51, "color": "#F28E2B", "label": "Community 51", "count": 6}, {"cid": 52, "color": "#E15759", "label": "Community 52", "count": 5}, {"cid": 53, "color": "#76B7B2", "label": "Community 53", "count": 5}, {"cid": 54, "color": "#59A14F", "label": "Community 54", "count": 4}, {"cid": 55, "color": "#EDC948", "label": "Community 55", "count": 4}, {"cid": 56, "color": "#B07AA1", "label": "Community 56", "count": 4}, {"cid": 57, "color": "#FF9DA7", "label": "Community 57", "count": 4}, {"cid": 58, "color": "#9C755F", "label": "Community 58", "count": 4}, {"cid": 59, "color": "#BAB0AC", "label": "Community 59", "count": 4}, {"cid": 60, "color": "#4E79A7", "label": "Community 60", "count": 4}, {"cid": 61, "color": "#F28E2B", "label": "Community 61", "count": 4}, {"cid": 62, "color": "#E15759", "label": "Community 62", "count": 4}, {"cid": 63, "color": "#76B7B2", "label": "Community 63", "count": 3}, {"cid": 64, "color": "#59A14F", "label": "Community 64", "count": 3}, {"cid": 65, "color": "#EDC948", "label": "Community 65", "count": 3}, {"cid": 66, "color": "#B07AA1", "label": "Community 66", "count": 3}, {"cid": 67, "color": "#FF9DA7", "label": "Community 67", "count": 3}, {"cid": 68, "color": "#9C755F", "label": "Community 68", "count": 3}, {"cid": 69, "color": "#BAB0AC", "label": "Community 69", "count": 3}, {"cid": 70, "color": "#4E79A7", "label": "Community 70", "cLine truncated
// HTML-escape helper — prevents XSS when injecting graph data into innerHTML
function esc(s) {
+17 -16
View File
@@ -17,8 +17,8 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/common/index.ts": 1781272556.3597305,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/directive/directive.d.ts": 1781272556.3601494,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/api/api.d.ts": 1781272556.3606477,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/components.d.ts": 1782268511.5967362,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/auto-imports.d.ts": 1782268506.0513382,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/components.d.ts": 1782361730.3874717,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/auto-imports.d.ts": 1782361721.5556824,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/store/index.ts": 1781272556.364766,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/router/index.ts": 1781272556.3652694,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/locales/index.ts": 1781272556.3657222,
@@ -41,7 +41,7 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/enums/formEnum.ts": 1781272556.3778555,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/enums/appEnum.ts": 1781272556.3784597,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/patch-el-select-filterable.ts": 1781272556.3791182,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/storage-slot-options.ts": 1781534662.3091736,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/storage-slot-options.ts": 1782289322.9593837,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/production-work-order-tabs.ts": 1781600464.3251936,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-doc-label.ts": 1781272556.3840382,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts": 1781403605.517818,
@@ -54,7 +54,7 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/qc-mine-task-groups.ts": 1781608059.9990764,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-labels.ts": 1781272556.3878465,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts": 1781765815.259271,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/production-work-order-card-meta.ts": 1781696893.3885784,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/production-work-order-card-meta.ts": 1782358030.9320872,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/fetch-paged-list.ts": 1781272556.3903377,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/sort-order.ts": 1781272556.3913012,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/outbound-qc-pilot-view-sections.ts": 1781566346.7618802,
@@ -241,7 +241,7 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts": 1781536765.5084789,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-material.ts": 1781510846.4290047,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/workflow.ts": 1781272556.6579714,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-work-order.ts": 1781696883.6649792,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-work-order.ts": 1782350330.2414439,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-task-result.ts": 1781272556.6589992,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/protocol-sample-collection.ts": 1781272556.659353,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/material-usage.ts": 1781517319.838243,
@@ -262,7 +262,7 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-task.ts": 1781581101.6134424,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-item-param.ts": 1781272556.6680455,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/env-detect-item.ts": 1781272556.668322,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts": 1781583227.9475071,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts": 1782362264.303446,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-material-stock-check.ts": 1781424012.5471919,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-material-usage.ts": 1781424013.8349903,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-material.ts": 1781690334.9639037,
@@ -271,7 +271,7 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/material.ts": 1781346022.817811,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/sample-reception.ts": 1781496419.5874305,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts": 1782122651.4628098,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts": 1781698779.261544,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts": 1782350342.5611486,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/auth/login/index.vue": 1781272556.6735966,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/refund-confirm/index.vue": 1781272556.6742516,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/my-material-usage/index.vue": 1781518736.690082,
@@ -306,7 +306,7 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/outside/Iframe.vue": 1781272556.7246568,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/operations/demo/index.vue": 1781272556.7266567,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/operations/clean-room/index.vue": 1781585388.6729288,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/operations/storage-facility/index.vue": 1781594230.0853102,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/operations/storage-facility/index.vue": 1782290524.010253,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/operations/exhibition-appointment/index.vue": 1781600464.861937,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/audit/index.vue": 1781600355.3898988,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/sample-info/index.vue": 1781518738.227001,
@@ -356,22 +356,23 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/sop/index.vue": 1781585388.69546,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-task/index.vue": 1781272556.812188,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/reagent/index.vue": 1781563767.359068,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue": 1781592903.601546,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts": 1781588526.441707,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue": 1781528130.4755712,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue": 1782351718.6351538,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts": 1782362265.837388,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue": 1782362266.614523,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/CompleteProductionTaskDialog.vue": 1781563767.3525257,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskInputEnhancedTable.vue": 1781585388.6972075,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchQcWorkOrderDialog.vue": 1781607677.069648,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchQcWorkOrderDialog.vue": 1782359900.6139107,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/SampleReceptionDialog.vue": 1781599793.4040868,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskExecutionDialog.vue": 1781588551.0654483,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskExecutionDialog.vue": 1782351614.9751787,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/EditTaskInfoDialog.vue": 1781577178.2471955,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputList.vue": 1781585388.699347,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue": 1781593091.2341514,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue": 1782361064.4509723,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskPaperAttachmentsPanel.vue": 1781530960.805927,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/WorkOrderTaskSubList.vue": 1781585388.7005522,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/WorkOrderTaskSubList.vue": 1782351634.8770185,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue": 1781605192.0421948,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/ProductionWorkOrderCardList.vue": 1781699025.1827943,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/ProductionWorkOrderCardList.vue": 1782358112.4315796,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/ForwardTaskDialog.vue": 1781563767.3550124,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchStepDialog.vue": 1782351544.529724,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/tabs/OutboundPilotTab.vue": 1781583381.1088736,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/tabs/ProductionWorkOrderTabPanel.vue": 1781583376.390064,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/tabs/InboundCellStorageTab.vue": 1781583380.9191706,
+18
View File
@@ -69,6 +69,24 @@ export function fetchReassignProductionTask(
})
}
export function fetchDispatchProductionTaskSteps(
taskId: string,
data: {
steps: Array<{
stepId: string
assigneeUserIds: string[]
plannedStartTime?: string | null
}>
},
operateScope?: ProductionTaskOperateScope
) {
return request.put<any>({
url: `/web/admin/production-tasks/${taskId}/steps/dispatch`,
data,
params: scopeQuery(operateScope)
})
}
export function fetchProductionTaskDetail(id: string) {
return request.get<any>({ url: `/web/admin/production-tasks/${id}` })
}
+21 -10
View File
@@ -150,27 +150,38 @@ export function fetchDeleteProductionWorkOrder(id: string) {
return request.del({ url: `/web/admin/production-work-orders/${id}` })
}
export interface ProductionCellProductOption {
export interface ProductionQcDispatchOption {
id: string
cellProductId?: string
productCode?: string
productName?: string
productTypeName?: string
passage?: string
storageSlotName?: string
stepOutputId?: string
taskId?: string
taskNo?: string
taskName?: string
stepId?: string
processName?: string
outputTypeId?: string
outputTypeName?: string
outputName?: string
outputBarcode?: string
cellProductId?: string
}
export function fetchProductionWorkOrderCellProductOptions(workOrderId: string) {
return request.get<ProductionCellProductOption[]>({
/** @deprecated 使用 ProductionQcDispatchOption */
export type ProductionCellProductOption = ProductionQcDispatchOption
export function fetchProductionWorkOrderQcDispatchOptions(workOrderId: string) {
return request.get<ProductionQcDispatchOption[]>({
url: `/web/admin/production-work-orders/${workOrderId}/cell-product-options`
})
}
/** @deprecated 使用 fetchProductionWorkOrderQcDispatchOptions */
export function fetchProductionWorkOrderCellProductOptions(workOrderId: string) {
return fetchProductionWorkOrderQcDispatchOptions(workOrderId)
}
export function fetchCreateQcFromProduction(
workOrderId: string,
data: { cellProductId: string; productionTaskId?: string; remark?: string }
data: { productionStepOutputId: string; remark?: string }
) {
return request.post({
url: `/web/admin/production-work-orders/${workOrderId}/qc-work-orders`,
+1
View File
@@ -138,6 +138,7 @@ export type QcWorkOrder = {
productionWorkOrderId?: string
productionWorkOrderNo?: string
productionTaskId?: string
productionStepOutputId?: string
protocolStatus?: string
protocolStatusLabel?: string
paymentSummary?: QcProtocolPaymentSummary | null
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "label": "useTaskStepEditor.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L1"}, {"id": "production_work_order_usetaskstepeditor_parsetaskattachmentsfromurls", "label": "parseTaskAttachmentsFromUrls()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L43"}, {"id": "production_work_order_usetaskstepeditor_toapiflag", "label": "toApiFlag()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L53"}, {"id": "production_work_order_usetaskstepeditor_fromapiflag", "label": "fromApiFlag()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L57"}, {"id": "production_work_order_usetaskstepeditor_normalizestepsubtables", "label": "normalizeStepSubTables()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L60"}, {"id": "production_work_order_usetaskstepeditor_parseordernum", "label": "parseOrderNum()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L77"}, {"id": "production_work_order_usetaskstepeditor_usetaskstepeditor", "label": "useTaskStepEditor()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L84"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production_task", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L18", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_admin", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L27", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production_work_order", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L28", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_sample_reception", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L29", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_equipment_select", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L30", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_process_material_ui", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L31", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_taskexecutiondialog_vue", "label": "TaskExecutionDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskExecutionDialog.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchqcworkorderdialog_vue", "label": "DispatchQcWorkOrderDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchQcWorkOrderDialog.vue", "source_location": "L1"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchqcworkorderdialog_vue", "target": "element_plus", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchQcWorkOrderDialog.vue", "source_location": "L114", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchqcworkorderdialog_vue", "target": "element_plus", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchQcWorkOrderDialog.vue", "source_location": "L115", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchqcworkorderdialog_vue", "target": "icons_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchQcWorkOrderDialog.vue", "source_location": "L116", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchqcworkorderdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production_task", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchQcWorkOrderDialog.vue", "source_location": "L117", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchqcworkorderdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production_work_order", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchQcWorkOrderDialog.vue", "source_location": "L118", "weight": 1.0}], "raw_calls": []}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production_task_ts", "label": "production-task.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L1"}, {"id": "api_production_task_scopequery", "label": "scopeQuery()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L5"}, {"id": "api_production_task_fetchcreateproductiontask", "label": "fetchCreateProductionTask()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L9"}, {"id": "api_production_task_fetchproductiontasklist", "label": "fetchProductionTaskList()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L13"}, {"id": "api_production_task_fetchmyproductiontasks", "label": "fetchMyProductionTasks()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L31"}, {"id": "api_production_task_fetchforwardproductiontask", "label": "fetchForwardProductionTask()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L50"}, {"id": "api_production_task_fetchreassignproductiontask", "label": "fetchReassignProductionTask()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L62"}, {"id": "api_production_task_fetchdispatchproductiontasksteps", "label": "fetchDispatchProductionTaskSteps()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L72"}, {"id": "api_production_task_fetchproductiontaskdetail", "label": "fetchProductionTaskDetail()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L88"}, {"id": "api_production_task_fetchupdateproductiontask", "label": "fetchUpdateProductionTask()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L92"}, {"id": "api_production_task_fetchupdateproductiontaskstatus", "label": "fetchUpdateProductionTaskStatus()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L96"}, {"id": "api_production_task_fetchdeleteproductiontask", "label": "fetchDeleteProductionTask()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L108"}, {"id": "api_production_task_fetchaddtaskstep", "label": "fetchAddTaskStep()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L112"}, {"id": "api_production_task_fetchupdatetaskstep", "label": "fetchUpdateTaskStep()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L124"}, {"id": "api_production_task_fetchdeletetaskstep", "label": "fetchDeleteTaskStep()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L137"}, {"id": "api_production_task_fetchreplacestepreagents", "label": "fetchReplaceStepReagents()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L148"}, {"id": "api_production_task_fetchreplacestepconsumables", "label": "fetchReplaceStepConsumables()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L161"}, {"id": "api_production_task_fetchreplacestepconfiguredreagents", "label": "fetchReplaceStepConfiguredReagents()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L174"}, {"id": "api_production_task_fetchreplacestepdatacollection", "label": "fetchReplaceStepDataCollection()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L187"}, {"id": "api_production_task_fetchsettaskinputs", "label": "fetchSetTaskInputs()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L200"}, {"id": "api_production_task_fetchsettaskoutputs", "label": "fetchSetTaskOutputs()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L212"}, {"id": "api_production_task_fetchreordertasksteps", "label": "fetchReorderTaskSteps()", "file_type": "code", "source_file": "/Users/wuzilong/Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production_work_order_ts", "label": "production-work-order.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-work-order.ts", "source_location": "L1"}, {"id": "api_production_work_order_fetchproductionworkorderlist", "label": "fetchProductionWorkOrderList()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-work-order.ts", "source_location": "L83"}, {"id": "api_production_work_order_fetchproductionworkorderdetail", "label": "fetchProductionWorkOrderDetail()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-work-order.ts", "source_location": "L105"}, {"id": "api_production_work_order_fetchcompleteproductionworkorder", "label": "fetchCompleteProductionWorkOrder()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-work-order.ts", "source_location": "L109"}, {"id": "api_production_work_order_fetchcreateproductionworkorder", "label": "fetchCreateProductionWorkOrder()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-work-order.ts", "source_location": "L113"}, {"id": "api_production_work_order_fetchconfigureproductionworkorder", "label": "fetchConfigureProductionWorkOrder()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-work-order.ts", "source_location": "L126"}, {"id": "api_production_work_order_fetchupdateproductionworkorder", "label": "fetchUpdateProductionWorkOrder()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-work-order.ts", "source_location": "L142"}, {"id": "api_production_work_order_fetchdeleteproductionworkorder", "label": "fetchDeleteProductionWorkOrder()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-work-order.ts", "source_location": "L149"}, {"id": "api_production_work_order_fetchproductionworkorderqcdispatchoptions", "label": "fetchProductionWorkOrderQcDispatchOptions()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-work-order.ts", "source_location": "L171"}, {"id": "api_production_work_order_fetchproductionworkordercellproductoptions", "label": "fetchProductionWorkOrderCellProductOptions()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-work-order.ts", "source_location": "L178"}, {"id": "api_production_work_order_fetchcreateqcfromproduction", "label": "fetchCreateQcFromProduction()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-work-order.ts", "source_location": "L182"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production_work_order_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_http", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-work-order.ts", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production_work_order_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_qc_work_order", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-work-order.ts", "source_location": "L2", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production_work_order_ts", "target": "api_production_work_order_fetchproductionworkorderlist", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-work-order.ts", "source_location": "L83", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production_work_order_ts", "target": "api_production_work_order_fetchproductionworkorderdetail", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-work-order.ts", "source_location": "L105", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production_work_order_ts", "target": "api_production_work_order_fetchcompleteproductionworkorder", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-work-order.ts", "source_location": "L109", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production_work_order_ts", "target": "api_production_work_order_fetchcreateproductionworkorder", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-work-order.ts", "source_location": "L113", "weight": 1.0}, {"source": "users_wuzilong_Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_productionworkordercardlist_vue", "label": "ProductionWorkOrderCardList.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/ProductionWorkOrderCardList.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchstepdialog_vue", "label": "DispatchStepDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchStepDialog.vue", "source_location": "L1"}, {"id": "production_work_order_dispatchstepdialog_mergeduseroptions", "label": "mergedUserOptions()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchStepDialog.vue", "source_location": "L115"}, {"id": "production_work_order_dispatchstepdialog_remotesearchusers", "label": "remoteSearchUsers()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchStepDialog.vue", "source_location": "L122"}, {"id": "production_work_order_dispatchstepdialog_onuserselectvisible", "label": "onUserSelectVisible()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchStepDialog.vue", "source_location": "L139"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchstepdialog_vue", "target": "production_work_order_dispatchstepdialog_mergeduseroptions", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchStepDialog.vue", "source_location": "L115", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchstepdialog_vue", "target": "production_work_order_dispatchstepdialog_remotesearchusers", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchStepDialog.vue", "source_location": "L122", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchstepdialog_vue", "target": "production_work_order_dispatchstepdialog_onuserselectvisible", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchStepDialog.vue", "source_location": "L139", "weight": 1.0}, {"source": "production_work_order_dispatchstepdialog_onuserselectvisible", "target": "production_work_order_dispatchstepdialog_remotesearchusers", "relation": "calls", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchStepDialog.vue", "source_location": "L140", "weight": 1.0}], "raw_calls": [{"caller_nid": "production_work_order_dispatchstepdialog_mergeduseroptions", "callee": "values", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchStepDialog.vue", "source_location": "L119"}, {"caller_nid": "production_work_order_dispatchstepdialog_remotesearchusers", "callee": "fetchUserList", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchStepDialog.vue", "source_location": "L125"}, {"caller_nid": "production_work_order_dispatchstepdialog_remotesearchusers", "callee": "trim", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchStepDialog.vue", "source_location": "L128"}]}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "label": "useTaskStepEditor.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L1"}, {"id": "production_work_order_usetaskstepeditor_parsetaskattachmentsfromurls", "label": "parseTaskAttachmentsFromUrls()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L45"}, {"id": "production_work_order_usetaskstepeditor_toapiflag", "label": "toApiFlag()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L55"}, {"id": "production_work_order_usetaskstepeditor_fromapiflag", "label": "fromApiFlag()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L59"}, {"id": "production_work_order_usetaskstepeditor_normalizestepsubtables", "label": "normalizeStepSubTables()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L62"}, {"id": "production_work_order_usetaskstepeditor_parseordernum", "label": "parseOrderNum()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L90"}, {"id": "production_work_order_usetaskstepeditor_usetaskstepeditor", "label": "useTaskStepEditor()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L97"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production_task", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_admin", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L19", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L20", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_admin", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L29", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production_work_order", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L30", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_sample_reception", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L31", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_equipment_select", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L32", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_stepdetailpanel_vue", "label": "StepDetailPanel.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L1"}, {"id": "production_work_order_stepdetailpanel_for", "label": "for()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L990"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_stepdetailpanel_vue", "target": "production_work_order_stepdetailpanel_for", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L990", "weight": 1.0}], "raw_calls": [{"caller_nid": "production_work_order_stepdetailpanel_for", "callee": "push", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L992"}, {"caller_nid": "production_work_order_stepdetailpanel_for", "callee": "decodeURIComponent", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L995"}, {"caller_nid": "production_work_order_stepdetailpanel_for", "callee": "pop", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L995"}, {"caller_nid": "production_work_order_stepdetailpanel_for", "callee": "split", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L995"}, {"caller_nid": "production_work_order_stepdetailpanel_for", "callee": "String", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L995"}]}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_types_import_components_d_ts", "label": "components.d.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/components.d.ts", "source_location": "L1"}], "edges": [], "raw_calls": []}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_productionworkordercardlist_vue", "label": "ProductionWorkOrderCardList.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/ProductionWorkOrderCardList.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchtaskdialog_vue", "label": "DispatchTaskDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L1"}, {"id": "production_work_order_dispatchtaskdialog_resolvecreatedtaskid", "label": "resolveCreatedTaskId()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L264"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchtaskdialog_vue", "target": "vue_draggable_plus", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L208", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchtaskdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production_task", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L209", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchtaskdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_production_labels", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L210", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchtaskdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L211", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchtaskdialog_vue", "target": "element_plus", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L212", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchtaskdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L213", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchtaskdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_stepdetailpanel_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L214", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchtaskdialog_vue", "target": "production_work_order_dispatchtaskdialog_resolvecreatedtaskid", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L264", "weight": 1.0}], "raw_calls": [{"caller_nid": "production_work_order_dispatchtaskdialog_resolvecreatedtaskid", "callee": "trim", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L266"}, {"caller_nid": "production_work_order_dispatchtaskdialog_resolvecreatedtaskid", "callee": "String", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L266"}]}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_stepdetailpanel_vue", "label": "StepDetailPanel.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L1"}, {"id": "production_work_order_stepdetailpanel_for", "label": "for()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L912"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_stepdetailpanel_vue", "target": "production_work_order_stepdetailpanel_for", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L912", "weight": 1.0}], "raw_calls": [{"caller_nid": "production_work_order_stepdetailpanel_for", "callee": "push", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L914"}, {"caller_nid": "production_work_order_stepdetailpanel_for", "callee": "decodeURIComponent", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L917"}, {"caller_nid": "production_work_order_stepdetailpanel_for", "callee": "pop", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L917"}, {"caller_nid": "production_work_order_stepdetailpanel_for", "callee": "split", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L917"}, {"caller_nid": "production_work_order_stepdetailpanel_for", "callee": "String", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L917"}]}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchqcworkorderdialog_vue", "label": "DispatchQcWorkOrderDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchQcWorkOrderDialog.vue", "source_location": "L1"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchqcworkorderdialog_vue", "target": "element_plus", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchQcWorkOrderDialog.vue", "source_location": "L109", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchqcworkorderdialog_vue", "target": "element_plus", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchQcWorkOrderDialog.vue", "source_location": "L110", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchqcworkorderdialog_vue", "target": "icons_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchQcWorkOrderDialog.vue", "source_location": "L111", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchqcworkorderdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production_work_order", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchQcWorkOrderDialog.vue", "source_location": "L112", "weight": 1.0}], "raw_calls": []}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L1"}, {"id": "production_work_order_index_refreshactivetab", "label": "refreshActiveTab()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L148"}, {"id": "production_work_order_index_handletabchange", "label": "handleTabChange()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L153"}, {"id": "production_work_order_index_handleaddtemporary", "label": "handleAddTemporary()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L164"}, {"id": "production_work_order_index_submitadd", "label": "submitAdd()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L169"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "vue_router", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L111", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "element_plus", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L112", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "icons_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L113", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production_work_order", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L114", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_production_work_order_tabs", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L121", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_tabs_productionworkordertabpanel_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L126", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchtaskdialog_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L127", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchstepdialog_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L128", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchqcworkorderdialog_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L129", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_vieLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_workordertasksublist_vue", "label": "WorkOrderTaskSubList.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/WorkOrderTaskSubList.vue", "source_location": "L1"}, {"id": "production_work_order_workordertasksublist_iscurrentassignee", "label": "isCurrentAssignee()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/WorkOrderTaskSubList.vue", "source_location": "L270"}, {"id": "production_work_order_workordertasksublist_isminehistoryrow", "label": "isMineHistoryRow()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/WorkOrderTaskSubList.vue", "source_location": "L278"}, {"id": "production_work_order_workordertasksublist_canenterexecution", "label": "canEnterExecution()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/WorkOrderTaskSubList.vue", "source_location": "L284"}, {"id": "production_work_order_workordertasksublist_canforwardtask", "label": "canForwardTask()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/WorkOrderTaskSubList.vue", "source_location": "L291"}, {"id": "production_work_order_workordertasksublist_canreassigntask", "label": "canReassignTask()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/WorkOrderTaskSubList.vue", "source_location": "L295"}, {"id": "production_work_order_workordertasksublist_candispatchsteps", "label": "canDispatchSteps()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/WorkOrderTaskSubList.vue", "source_location": "L299"}, {"id": "production_work_order_workordertasksublist_canedittask", "label": "canEditTask()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/WorkOrderTaskSubList.vue", "source_location": "L304"}, {"id": "production_work_order_workordertasksublist_candeletetask", "label": "canDeleteTask()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/WorkOrderTaskSubList.vue", "source_location": "L310"}, {"id": "production_work_order_workordertasksublist_hasmoretaskactions", "label": "hasMoreTaskActions()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/WorkOrderTaskSubList.vue", "source_location": "L315"}, {"id": "production_work_order_workordertasksublist_loaddata", "label": "loadData()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/WorkOrderTaskSubList.vue", "source_location": "L320"}, {"id": "production_work_order_workordertasksublist_handletaskmutationsuccess", "label": "handleTaskMutationSuccess()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/WorkOrderTaskSubList.vue", "source_location": "L352"}, {"id": "production_work_order_workordertasksublist_handledispatch", "label": "handleDispatch()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/WorkOrderTaskSubList.vue", "source_location": "L359"}, {"id": "production_work_order_workordertasksublist_handleneedstepdispatch", "label": "handleNeedStepDispatch()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/WorkOrderTaskSubList.vue", "source_location": "L373"}, {"id": "production_work_order_workordertasksublist_openstepdispatch", "label": "openStepDispatch()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/WorkOrderTaskSubList.vue", "source_location": "L379"}, {"id": "production_work_order_workordertasksublist_handleenterexecution", "label": "handleEnterExecution()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/WorkOrderTaskSubList.vue", "source_location": "L390"}, {"id": "production_work_order_workordertasksublist_handleviewdetail", "label": "handleViewDetail()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/WorkOrderTaskSubList.vue", "source_location": "L397"}, {"id": "production_work_order_workordertasksublist_handledropdowncommand", "label": "handleDropdownCommand()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-adLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_operations_storage_facility_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/operations/storage-facility/index.vue", "source_location": "L1"}, {"id": "storage_facility_index_if", "label": "if()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/operations/storage-facility/index.vue", "source_location": "L945"}, {"id": "storage_facility_index_push", "label": "push()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/operations/storage-facility/index.vue", "source_location": "L948"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_operations_storage_facility_index_vue", "target": "storage_facility_index_if", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/operations/storage-facility/index.vue", "source_location": "L945", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_operations_storage_facility_index_vue", "target": "storage_facility_index_push", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/operations/storage-facility/index.vue", "source_location": "L948", "weight": 1.0}], "raw_calls": [{"caller_nid": "storage_facility_index_if", "callee": "set", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/operations/storage-facility/index.vue", "source_location": "L946"}]}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchtaskdialog_vue", "label": "DispatchTaskDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L1"}, {"id": "production_work_order_dispatchtaskdialog_resolvecreatedtaskid", "label": "resolveCreatedTaskId()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L264"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchtaskdialog_vue", "target": "vue_draggable_plus", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L208", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchtaskdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production_task", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L209", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchtaskdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_production_labels", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L210", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchtaskdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L211", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchtaskdialog_vue", "target": "element_plus", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L212", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchtaskdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L213", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchtaskdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_stepdetailpanel_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L214", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchtaskdialog_vue", "target": "production_work_order_dispatchtaskdialog_resolvecreatedtaskid", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L264", "weight": 1.0}], "raw_calls": [{"caller_nid": "production_work_order_dispatchtaskdialog_resolvecreatedtaskid", "callee": "trim", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L266"}, {"caller_nid": "production_work_order_dispatchtaskdialog_resolvecreatedtaskid", "callee": "String", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L266"}]}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_api_qc_work_order_ts", "label": "qc-work-order.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts", "source_location": "L1"}, {"id": "api_qc_work_order_fetchqcworkorderlist", "label": "fetchQcWorkOrderList()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts", "source_location": "L181"}, {"id": "api_qc_work_order_fetchqcworkorderdetail", "label": "fetchQcWorkOrderDetail()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts", "source_location": "L201"}, {"id": "api_qc_work_order_fetchconfigureqcworkorder", "label": "fetchConfigureQcWorkOrder()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts", "source_location": "L207"}, {"id": "api_qc_work_order_fetchcreateqcworkorder", "label": "fetchCreateQcWorkOrder()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts", "source_location": "L224"}, {"id": "api_qc_work_order_fetchupdateqcworkorder", "label": "fetchUpdateQcWorkOrder()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts", "source_location": "L239"}, {"id": "api_qc_work_order_fetchdeleteqcworkorder", "label": "fetchDeleteQcWorkOrder()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts", "source_location": "L249"}, {"id": "api_qc_work_order_fetchoutboundqcdetail", "label": "fetchOutboundQcDetail()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts", "source_location": "L322"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_qc_work_order_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_http", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_qc_work_order_ts", "target": "api_qc_work_order_fetchqcworkorderlist", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts", "source_location": "L181", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_qc_work_order_ts", "target": "api_qc_work_order_fetchqcworkorderdetail", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts", "source_location": "L201", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_qc_work_order_ts", "target": "api_qc_work_order_fetchconfigureqcworkorder", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts", "source_location": "L207", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_qc_work_order_ts", "target": "api_qc_work_order_fetchcreateqcworkorder", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts", "source_location": "L224", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_qc_work_order_ts", "target": "api_qc_work_order_fetchupdateqcworkorder", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts", "source_location": "L239", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_qc_work_order_ts", "target": "api_qc_work_order_fetchdeleteqcworkorder", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts", "source_location": "L249", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_qc_work_order_ts", "target": "api_qc_work_order_fetchoutboundqcdetail", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts", "source_location": "L322", "weight": 1.0}], "raw_calls": [{"caller_nid": "api_qc_work_order_fetchqcworkorderlist", "callee": "get", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts", "source_location": "L195"}, {"caller_nid": "api_qc_work_order_fetchqcworkorderdetail", "callee": "get", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts", "source_location": "L202"}, {"caller_nid": "api_qc_work_order_fetchconfigureqcworkorder", "callee": "post", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts", "source_location": "L218"}, {"caller_nid": "api_qc_work_order_fetchcreateqcworkoLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "label": "useTaskStepEditor.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L1"}, {"id": "production_work_order_usetaskstepeditor_parsetaskattachmentsfromurls", "label": "parseTaskAttachmentsFromUrls()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L45"}, {"id": "production_work_order_usetaskstepeditor_toapiflag", "label": "toApiFlag()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L55"}, {"id": "production_work_order_usetaskstepeditor_fromapiflag", "label": "fromApiFlag()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L59"}, {"id": "production_work_order_usetaskstepeditor_normalizestepsubtables", "label": "normalizeStepSubTables()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L62"}, {"id": "production_work_order_usetaskstepeditor_parseordernum", "label": "parseOrderNum()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L90"}, {"id": "production_work_order_usetaskstepeditor_usetaskstepeditor", "label": "useTaskStepEditor()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L97"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production_task", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_admin", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L19", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L20", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_admin", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L29", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production_work_order", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L30", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_sample_reception", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L31", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_equipment_select", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L32", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_productionworkordercardlist_vue", "label": "ProductionWorkOrderCardList.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/ProductionWorkOrderCardList.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_stepdetailpanel_vue", "label": "StepDetailPanel.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L1"}, {"id": "production_work_order_stepdetailpanel_for", "label": "for()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L987"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_stepdetailpanel_vue", "target": "production_work_order_stepdetailpanel_for", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L987", "weight": 1.0}], "raw_calls": [{"caller_nid": "production_work_order_stepdetailpanel_for", "callee": "push", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L989"}, {"caller_nid": "production_work_order_stepdetailpanel_for", "callee": "decodeURIComponent", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L992"}, {"caller_nid": "production_work_order_stepdetailpanel_for", "callee": "pop", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L992"}, {"caller_nid": "production_work_order_stepdetailpanel_for", "callee": "split", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L992"}, {"caller_nid": "production_work_order_stepdetailpanel_for", "callee": "String", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L992"}]}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "label": "useTaskStepEditor.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L1"}, {"id": "production_work_order_usetaskstepeditor_parsetaskattachmentsfromurls", "label": "parseTaskAttachmentsFromUrls()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L45"}, {"id": "production_work_order_usetaskstepeditor_toapiflag", "label": "toApiFlag()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L55"}, {"id": "production_work_order_usetaskstepeditor_fromapiflag", "label": "fromApiFlag()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L59"}, {"id": "production_work_order_usetaskstepeditor_normalizestepsubtables", "label": "normalizeStepSubTables()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L62"}, {"id": "production_work_order_usetaskstepeditor_parseordernum", "label": "parseOrderNum()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L90"}, {"id": "production_work_order_usetaskstepeditor_usetaskstepeditor", "label": "useTaskStepEditor()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L97"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production_task", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_admin", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L19", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L20", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_admin", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L29", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production_work_order", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L30", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_sample_reception", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L31", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_equipment_select", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L32", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchtaskdialog_vue", "label": "DispatchTaskDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L1"}, {"id": "production_work_order_dispatchtaskdialog_resolvecreatedtaskid", "label": "resolveCreatedTaskId()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L264"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchtaskdialog_vue", "target": "vue_draggable_plus", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L208", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchtaskdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production_task", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L209", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchtaskdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_production_labels", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L210", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchtaskdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L211", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchtaskdialog_vue", "target": "element_plus", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L212", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchtaskdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L213", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchtaskdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_stepdetailpanel_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L214", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchtaskdialog_vue", "target": "production_work_order_dispatchtaskdialog_resolvecreatedtaskid", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L264", "weight": 1.0}], "raw_calls": [{"caller_nid": "production_work_order_dispatchtaskdialog_resolvecreatedtaskid", "callee": "trim", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L266"}, {"caller_nid": "production_work_order_dispatchtaskdialog_resolvecreatedtaskid", "callee": "String", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L266"}]}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_production_work_order_card_meta_ts", "label": "production-work-order-card-meta.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/production-work-order-card-meta.ts", "source_location": "L1"}, {"id": "utils_production_work_order_card_meta_formatoutboundschedulesortno", "label": "formatOutboundScheduleSortNo()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/production-work-order-card-meta.ts", "source_location": "L40"}, {"id": "utils_production_work_order_card_meta_formatoutboundscheduletotalcount", "label": "formatOutboundScheduleTotalCount()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/production-work-order-card-meta.ts", "source_location": "L46"}, {"id": "utils_production_work_order_card_meta_parsereceptiontime", "label": "parseReceptionTime()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/production-work-order-card-meta.ts", "source_location": "L52"}, {"id": "utils_production_work_order_card_meta_receptionover24hourslabel", "label": "receptionOver24HoursLabel()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/production-work-order-card-meta.ts", "source_location": "L59"}, {"id": "utils_production_work_order_card_meta_receptionover24hourstagtype", "label": "receptionOver24HoursTagType()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/production-work-order-card-meta.ts", "source_location": "L66"}, {"id": "utils_production_work_order_card_meta_protocolstatuslabelfortab", "label": "protocolStatusLabelForTab()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/production-work-order-card-meta.ts", "source_location": "L73"}, {"id": "utils_production_work_order_card_meta_protocolstatustagtypefortab", "label": "protocolStatusTagTypeForTab()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/production-work-order-card-meta.ts", "source_location": "L82"}, {"id": "utils_production_work_order_card_meta_buildinboundmetafields", "label": "buildInboundMetaFields()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/production-work-order-card-meta.ts", "source_location": "L91"}, {"id": "utils_production_work_order_card_meta_getcardmetaconfig", "label": "getCardMetaConfig()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/production-work-order-card-meta.ts", "source_location": "L263"}, {"id": "utils_production_work_order_card_meta_getcardmetaconfigforlegacytab", "label": "getCardMetaConfigForLegacyTab()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/production-work-order-card-meta.ts", "source_location": "L267"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_production_work_order_card_meta_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production_work_order", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/production-work-order-card-meta.ts", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_production_work_order_card_meta_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_qc_labels", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/production-work-order-card-meta.ts", "source_location": "L2", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_production_work_order_card_meta_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_status_tag_registry", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/production-work-order-card-meta.ts", "source_location": "L3", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_production_work_order_card_meta_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_status_tag_registry", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/production-work-order-card-meta.ts", "source_location": "L9", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_production_work_order_card_meta_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_production_work_order_tabs", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/production-work-order-card-meta.ts", "source_location": "L10", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_storage_slot_options_ts", "label": "storage-slot-options.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/storage-slot-options.ts", "source_location": "L1"}, {"id": "utils_storage_slot_options_getstoragefacilityordernum", "label": "getStorageFacilityOrderNum()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/storage-slot-options.ts", "source_location": "L16"}, {"id": "utils_storage_slot_options_getstorageslotordernum", "label": "getStorageSlotOrderNum()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/storage-slot-options.ts", "source_location": "L22"}, {"id": "utils_storage_slot_options_decodeslotlayoutorder", "label": "decodeSlotLayoutOrder()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/storage-slot-options.ts", "source_location": "L31"}, {"id": "utils_storage_slot_options_parselegacyrowcolslotcode", "label": "parseLegacyRowColSlotCode()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/storage-slot-options.ts", "source_location": "L40"}, {"id": "utils_storage_slot_options_parsestorageslotcode", "label": "parseStorageSlotCode()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/storage-slot-options.ts", "source_location": "L47"}, {"id": "utils_storage_slot_options_buildnodecodepathfromflat", "label": "buildNodeCodePathFromFlat()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/storage-slot-options.ts", "source_location": "L54"}, {"id": "utils_storage_slot_options_buildfullslotcodefromflat", "label": "buildFullSlotCodeFromFlat()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/storage-slot-options.ts", "source_location": "L78"}, {"id": "utils_storage_slot_options_comparestoragefacilityslots", "label": "compareStorageFacilitySlots()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/storage-slot-options.ts", "source_location": "L90"}, {"id": "utils_storage_slot_options_sortstoragefacilityslots", "label": "sortStorageFacilitySlots()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/storage-slot-options.ts", "source_location": "L118"}, {"id": "utils_storage_slot_options_isstorageslotavailable", "label": "isStorageSlotAvailable()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/storage-slot-options.ts", "source_location": "L122"}, {"id": "utils_storage_slot_options_collectleaffacilitynodesintreeorder", "label": "collectLeafFacilityNodesInTreeOrder()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/storage-slot-options.ts", "source_location": "L131"}, {"id": "utils_storage_slot_options_mapslotstoselectoptions", "label": "mapSlotsToSelectOptions()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/storage-slot-options.ts", "source_location": "L183"}, {"id": "utils_storage_slot_options_loadstorageslotselectoptions", "label": "loadStorageSlotSelectOptions()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/storage-slot-options.ts", "source_location": "L211"}, {"id": "utils_storage_slot_options_remotesearchstorageslotselectoptions", "label": "remoteSearchStorageSlotSelectOptions()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/storage-slot-options.ts", "source_location": "L249"}, {"id": "utils_storage_slot_options_ensurestorageslotsinoptions", "label": "ensureStorageSlotsInOptions()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/storage-slot-options.ts", "source_location": "L274"}, {"id": "utils_storage_slot_options_filterstorageslotselectoptions", "label": "filterStorageSlotSelectOptions()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/storage-slot-options.ts", "source_location": "L293"}, {"id": "utils_storage_slot_options_validateuniquestorageslotids", "label": "validateUniqueStorageSlotIds()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/storage-slot-options.ts", "source_location": "L307"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_storage_slot_options_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_tree_path", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/storage-slot-options.ts", "source_location": "L4", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_storage_slot_options_ts", "target": "users_wuzilong_work_00code_Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production_task_ts", "label": "production-task.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L1"}, {"id": "api_production_task_scopequery", "label": "scopeQuery()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L5"}, {"id": "api_production_task_fetchcreateproductiontask", "label": "fetchCreateProductionTask()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L9"}, {"id": "api_production_task_fetchproductiontasklist", "label": "fetchProductionTaskList()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L13"}, {"id": "api_production_task_fetchmyproductiontasks", "label": "fetchMyProductionTasks()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L31"}, {"id": "api_production_task_fetchforwardproductiontask", "label": "fetchForwardProductionTask()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L50"}, {"id": "api_production_task_fetchreassignproductiontask", "label": "fetchReassignProductionTask()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L62"}, {"id": "api_production_task_fetchdispatchproductiontasksteps", "label": "fetchDispatchProductionTaskSteps()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L72"}, {"id": "api_production_task_fetchproductiontaskdetail", "label": "fetchProductionTaskDetail()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L90"}, {"id": "api_production_task_fetchupdateproductiontask", "label": "fetchUpdateProductionTask()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L94"}, {"id": "api_production_task_fetchupdateproductiontaskstatus", "label": "fetchUpdateProductionTaskStatus()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L98"}, {"id": "api_production_task_fetchdeleteproductiontask", "label": "fetchDeleteProductionTask()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L110"}, {"id": "api_production_task_fetchaddtaskstep", "label": "fetchAddTaskStep()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L114"}, {"id": "api_production_task_fetchupdatetaskstep", "label": "fetchUpdateTaskStep()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L126"}, {"id": "api_production_task_fetchdeletetaskstep", "label": "fetchDeleteTaskStep()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L139"}, {"id": "api_production_task_fetchreplacestepreagents", "label": "fetchReplaceStepReagents()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L150"}, {"id": "api_production_task_fetchreplacestepconsumables", "label": "fetchReplaceStepConsumables()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L163"}, {"id": "api_production_task_fetchreplacestepconfiguredreagents", "label": "fetchReplaceStepConfiguredReagents()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L176"}, {"id": "api_production_task_fetchreplacestepdatacollection", "label": "fetchReplaceStepDataCollection()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L189"}, {"id": "api_production_task_fetchsettaskinputs", "label": "fetchSetTaskInputs()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L202"}, {"id": "api_production_task_fetchsettaskoutputs", "label": "fetchSetTaskOutputs()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-task.ts", "source_location": "L214"}, {"id": "api_production_task_fetchreordertasksteps", "label": "fetchReorderTaskSteps()", "file_type": "code", "source_file": "/Users/wuzilong/Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_stepdetailpanel_vue", "label": "StepDetailPanel.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L1"}, {"id": "production_work_order_stepdetailpanel_for", "label": "for()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L987"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_stepdetailpanel_vue", "target": "production_work_order_stepdetailpanel_for", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L987", "weight": 1.0}], "raw_calls": [{"caller_nid": "production_work_order_stepdetailpanel_for", "callee": "push", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L989"}, {"caller_nid": "production_work_order_stepdetailpanel_for", "callee": "decodeURIComponent", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L992"}, {"caller_nid": "production_work_order_stepdetailpanel_for", "callee": "pop", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L992"}, {"caller_nid": "production_work_order_stepdetailpanel_for", "callee": "split", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L992"}, {"caller_nid": "production_work_order_stepdetailpanel_for", "callee": "String", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L992"}]}
@@ -89,10 +89,7 @@ function protocolStatusTagTypeForTab(
}
function buildInboundMetaFields(tabKey: ProductionWorkOrderTabKey): CardMetaField[] {
const fields: CardMetaField[] = [
{ label: '样本编码', getValue: (row) => row.sampleCode || '—' },
{ label: '实验室入库编码', getValue: (row) => row.labStorageCode || '—' }
]
const fields: CardMetaField[] = []
if (tabKey === 'inbound_cell_storage') {
fields.push({
label: '来源类型',
@@ -120,6 +117,9 @@ function buildInboundMetaFields(tabKey: ProductionWorkOrderTabKey): CardMetaFiel
},
{ label: '接收时间', getValue: (row) => row.receptionTime || '—' },
{ label: '创建时间', getValue: (row) => row.createTime || '—' },
{ label: '工单号', getValue: (row) => row.workOrderNo || '—' },
{ label: '样本编码', getValue: (row) => row.sampleCode || '—' },
{ label: '实验室入库编码', getValue: (row) => row.labStorageCode || '—' },
{
label: '备注',
getValue: (row) => row.remark || '—',
+32 -6
View File
@@ -25,11 +25,29 @@ export function getStorageSlotOrderNum(slot: Record<string, unknown>): number {
return Number.isFinite(parsed) ? parsed : 0
}
/** 解析 slotCode「{prefix}{row}-{col}」 */
export function parseStorageSlotCode(code: string): { row: number; col: number } | null {
/** 货架布局 order_num 步长(row * STRIDE + col) */
export const SLOT_LAYOUT_ROW_STRIDE = 1000
export function decodeSlotLayoutOrder(orderNum: number): { row: number; col: number } {
const order = Number(orderNum) || 0
if (order >= SLOT_LAYOUT_ROW_STRIDE) {
return { row: Math.trunc(order / SLOT_LAYOUT_ROW_STRIDE), col: order % SLOT_LAYOUT_ROW_STRIDE }
}
return { row: 0, col: order }
}
/** 解析旧版 slotCode「{prefix}{row}-{col}」;纯数字序号返回 null */
export function parseLegacyRowColSlotCode(code: string): { row: number; col: number; prefix: string } | null {
const match = (code || '').trim().match(/^(.*?)(\d+)-(\d+)$/)
if (!match) return null
return { row: parseInt(match[2], 10), col: parseInt(match[3], 10) }
return { prefix: match[1], row: parseInt(match[2], 10), col: parseInt(match[3], 10) }
}
/** @deprecated 兼容旧调用;新数据请用 decodeSlotLayoutOrder + 序号 slotCode */
export function parseStorageSlotCode(code: string): { row: number; col: number } | null {
const legacy = parseLegacyRowColSlotCode(code)
if (legacy) return { row: legacy.row, col: legacy.col }
return null
}
/** 从根到节点的 node_code 路径,用 `-` 连接 */
@@ -68,17 +86,25 @@ export function buildFullSlotCodeFromFlat(
return local || path
}
/** 盒内格位:order_num → 行 → 列 → 编码 */
/** 盒内格位:order_num → 布局行列 → 序号 */
export function compareStorageFacilitySlots(
a: Record<string, unknown>,
b: Record<string, unknown>
): number {
const orderDiff = getStorageSlotOrderNum(a) - getStorageSlotOrderNum(b)
if (orderDiff !== 0) return orderDiff
const oa = getStorageSlotOrderNum(a)
const ob = getStorageSlotOrderNum(b)
if (oa >= SLOT_LAYOUT_ROW_STRIDE && ob >= SLOT_LAYOUT_ROW_STRIDE) {
const da = decodeSlotLayoutOrder(oa)
const db = decodeSlotLayoutOrder(ob)
if (da.row !== db.row) return da.row - db.row
if (da.col !== db.col) return da.col - db.col
}
const codeA = String(a.slotCode ?? a.slot_code ?? '')
const codeB = String(b.slotCode ?? b.slot_code ?? '')
const pa = parseStorageSlotCode(codeA)
const pb = parseStorageSlotCode(codeB)
const pa = parseLegacyRowColSlotCode(codeA)
const pb = parseLegacyRowColSlotCode(codeB)
if (pa && pb) {
if (pa.row !== pb.row) return pa.row - pb.row
if (pa.col !== pb.col) return pa.col - pb.col
@@ -135,7 +135,7 @@
<div class="shelf-cells">
<div
class="shelf-cell shelf-cell--add"
title="在此行前面增加格子"
title="在此行末尾增加格子"
@click="handleAddSlotInRow(ri)"
>
<span class="add-icon">+</span>
@@ -215,11 +215,11 @@
<el-form-item label="节点名称" prop="nodeName">
<el-input v-model="nodeFormData.nodeName" placeholder="请输入节点名称" />
</el-form-item>
<el-form-item prop="nodeCode">
<el-form-item>
<template #label>
节点编号<span class="form-label-tip">单段字母或数字,不含连字符</span>
节点编号<span class="form-label-tip">可选</span>
</template>
<el-input v-model="nodeFormData.nodeCode" placeholder="如 1、03、A" />
<el-input v-model="nodeFormData.nodeCode" placeholder="可不填" />
</el-form-item>
<el-form-item label="节点类型">
<el-select v-model="nodeFormData.nodeType" placeholder="请选择节点类型">
@@ -278,15 +278,17 @@
<el-form
ref="slotFormRef"
:model="slotFormData"
:rules="slotFormRules"
label-position="top"
class="storage-dialog-form"
scroll-to-error
:scroll-into-view-options="{ behavior: 'smooth', block: 'center' }"
>
<div class="form-grid-2">
<el-form-item label="本地编码(行-列)" prop="slotCode">
<el-input v-model="slotFormData.slotCode" placeholder="如 1-3" />
<el-form-item>
<template #label>
本地编码(序号)<span class="form-label-tip">可选</span>
</template>
<el-input v-model="slotFormData.slotCode" placeholder="可不填" />
</el-form-item>
<el-form-item label="格子状态">
<el-select v-model="slotFormData.slotStatus" placeholder="请选择状态">
@@ -357,9 +359,9 @@
将生成 <strong>{{ batchForm.rows * batchForm.cols }}</strong> 个格子({{
batchForm.rows
}}
行 × {{ batchForm.cols }} 列)
行 × {{ batchForm.cols }} 列),本地序号从 <strong>1</strong> 起依次编号
<template v-if="batchPreviewCode">
,示例编号:<strong>{{ batchPreviewCode }}</strong>
,示例完整编号:<strong>{{ batchPreviewCode }}</strong>
</template>
</div>
</div>
@@ -625,22 +627,24 @@
<el-form
ref="occupancySlotFormRef"
:model="occupancySlotFormData"
:rules="occupancySlotFormRules"
label-position="top"
class="storage-dialog-form"
scroll-to-error
:scroll-into-view-options="{ behavior: 'smooth', block: 'center' }"
>
<div class="form-grid-2">
<el-form-item label="完整编号" class="form-col-span-2">
<div class="form-grid-3">
<el-form-item label="完整编号">
<el-input
:model-value="currentSlot?.fullSlotCode || '—'"
readonly
placeholder="保存后自动生成"
/>
</el-form-item>
<el-form-item label="本地编码(行-列)" prop="slotCode">
<el-input v-model="occupancySlotFormData.slotCode" placeholder="如 1-3" />
<el-form-item>
<template #label>
本地编码(序号)<span class="form-label-tip">可选</span>
</template>
<el-input v-model="occupancySlotFormData.slotCode" placeholder="可不填" />
</el-form-item>
<el-form-item label="格子状态">
<el-select v-model="occupancySlotFormData.slotStatus" placeholder="请选择状态">
@@ -734,7 +738,13 @@
fetchRemoveOccupancy
} from '@/api/admin'
import { buildTreePathLabelFromFlat } from '@/utils/tree-path'
import { buildNodeCodePathFromFlat } from '@/utils/storage-slot-options'
import {
buildNodeCodePathFromFlat,
decodeSlotLayoutOrder,
getStorageSlotOrderNum,
parseLegacyRowColSlotCode,
SLOT_LAYOUT_ROW_STRIDE
} from '@/utils/storage-slot-options'
defineOptions({ name: 'StorageFacility' })
@@ -877,7 +887,7 @@
}
}
// ==================== 货架行计算(按 slotCode 中的行号分组) ====================
// ==================== 货架行计算(按 order_num 布局分组) ====================
const batchForm = reactive({
rows: 4,
cols: 6
@@ -886,23 +896,51 @@
const batchPreviewCode = computed(() => {
if (!selectedNode.value?.id) return ''
const path = buildNodeCodePathFromFlat(selectedNode.value.id, flatList.value)
if (!path) return ''
return `${path}-1-1`
const total = Number(batchForm.rows) * Number(batchForm.cols)
const local = total <= 1 ? '1' : `1…${total}`
return path ? `${path}-${local}` : local
})
/** 解析 slotCode "{prefix}{row}-{col}" 格式,提取行号和列号 */
const parseSlotCode = (code: string): { prefix: string; row: number; col: number } | null => {
const match = code.match(/^(.*?)(\d+)-(\d+)$/)
if (!match) return null
return { prefix: match[1], row: parseInt(match[2], 10), col: parseInt(match[3], 10) }
}
const sortSlotsForShelf = (slots: any[]) =>
[...slots].sort((a, b) => {
const oa = getStorageSlotOrderNum(a)
const ob = getStorageSlotOrderNum(b)
if (oa !== ob) return oa - ob
const na = parseInt(String(a.slotCode || ''), 10) || 0
const nb = parseInt(String(b.slotCode || ''), 10) || 0
return na - nb
})
const shelfRows = computed(() => {
const sorted = sortSlotsForShelf(slotList.value)
const hasLayoutOrder = sorted.some((slot) => getStorageSlotOrderNum(slot) >= SLOT_LAYOUT_ROW_STRIDE)
if (hasLayoutOrder) {
const rowMap = new Map<number, any[]>()
for (const slot of sorted) {
const { row } = decodeSlotLayoutOrder(getStorageSlotOrderNum(slot))
if (!rowMap.has(row)) rowMap.set(row, [])
rowMap.get(row)!.push(slot)
}
return [...rowMap.entries()]
.sort(([a], [b]) => a - b)
.map(([rowNum, cells], index) => ({
label: String(index + 1),
rowNum,
prefix: '',
cells: cells.sort(
(a, b) =>
decodeSlotLayoutOrder(getStorageSlotOrderNum(a)).col -
decodeSlotLayoutOrder(getStorageSlotOrderNum(b)).col
)
}))
}
// 兼容旧版「行-列」本地编码
const rowMap = new Map<number, { prefix: string; cells: any[] }>()
const ungrouped: any[] = []
for (const slot of slotList.value) {
const parsed = parseSlotCode(slot.slotCode || '')
for (const slot of sorted) {
const parsed = parseLegacyRowColSlotCode(slot.slotCode || '')
if (parsed) {
if (!rowMap.has(parsed.row)) {
rowMap.set(parsed.row, { prefix: parsed.prefix, cells: [] })
@@ -913,14 +951,11 @@
}
}
// 按行号排序,每个行内按列号排序
const result: { label: string; cells: any[]; rowNum: number; prefix: string }[] = []
const sortedEntries = [...rowMap.entries()].sort(([a], [b]) => a - b)
for (const [rowNum, data] of sortedEntries) {
for (const [rowNum, data] of [...rowMap.entries()].sort(([a], [b]) => a - b)) {
data.cells.sort((a, b) => {
const pa = parseSlotCode(a.slotCode || '')
const pb = parseSlotCode(b.slotCode || '')
const pa = parseLegacyRowColSlotCode(a.slotCode || '')
const pb = parseLegacyRowColSlotCode(b.slotCode || '')
return (pa?.col ?? 0) - (pb?.col ?? 0)
})
result.push({
@@ -931,7 +966,6 @@
})
}
// 无法解析编码的格子归入末尾
if (ungrouped.length > 0) {
result.push({
label: String(result.length + 1),
@@ -962,22 +996,8 @@
remark: ''
})
const validateNodeCode = (_rule: unknown, value: string, callback: (err?: Error) => void) => {
const v = (value || '').trim()
if (!v) {
callback(new Error('请输入节点编号'))
return
}
if (!/^[A-Za-z0-9]+$/.test(v)) {
callback(new Error('编号只能包含字母和数字,不能含连字符'))
return
}
callback()
}
const nodeFormRules = {
nodeName: [{ required: true, message: '请输入节点名称', trigger: 'blur' }],
nodeCode: [{ required: true, validator: validateNodeCode, trigger: 'blur' }]
nodeName: [{ required: true, message: '请输入节点名称', trigger: 'blur' }]
}
const handleAddRoot = () => {
@@ -1106,10 +1126,6 @@
remark: ''
})
const slotFormRules = {
slotCode: [{ required: true, message: '请输入格子编码', trigger: 'blur' }]
}
const handleEditSlot = (row: any) => {
isSlotEdit.value = true
slotDialogTitle.value = '编辑格子'
@@ -1218,25 +1234,17 @@
// ==================== 单独新增行/格子 ====================
const addSlotLoading = ref(false)
/** 在指定行前面追加一个格子 */
/** 在指定行末尾追加一个格子 */
const handleAddSlotInRow = async (rowIndex: number) => {
if (!selectedNode.value?.id || addSlotLoading.value) return
const row = shelfRows.value[rowIndex]
if (!row) return
// 计算该行下一个列号
let nextCol = 1
for (const cell of row.cells) {
const parsed = parseSlotCode(cell.slotCode || '')
if (parsed && parsed.col >= nextCol) nextCol = parsed.col + 1
}
const slotCode = `${row.rowNum}-${nextCol}`
addSlotLoading.value = true
try {
await fetchCreateStorageSlot({
facilityNodeId: selectedNode.value.id,
slotCode,
atRowNum: row.rowNum,
slotStatus: '空'
})
ElMessage.success('新增格子成功')
@@ -1356,9 +1364,6 @@
slotStatus: '空',
remark: ''
})
const occupancySlotFormRules = {
slotCode: [{ required: true, message: '请输入格子编码', trigger: 'blur' }]
}
const handleOccupancySlotSave = async () => {
await occupancySlotFormRef.value?.validate?.()
@@ -30,23 +30,55 @@
v-loading="optionsLoading"
>
<div class="form-grid-2">
<el-form-item label="细胞产品" prop="cellProductId" class="form-col-span-2">
<el-form-item label="生产任务" prop="taskId" class="form-col-span-2">
<el-select
v-model="form.cellProductId"
placeholder="请选择细胞产品"
v-model="form.taskId"
placeholder="请选择生产任务"
filterable
style="width: 100%"
:disabled="!productOptions.length"
:disabled="!productionTasks.length"
@change="handleTaskChange"
>
<el-option
v-for="opt in productOptions"
v-for="opt in taskOptions"
:key="opt.taskId"
:label="formatTaskOptionLabel(opt)"
:value="opt.taskId"
/>
</el-select>
</el-form-item>
<el-form-item label="步骤产出物" prop="stepOutputId" class="form-col-span-2">
<template #label
>步骤产出物<span class="form-label-tip">类型为有效物且已填写条码</span></template
>
<el-select
v-model="form.stepOutputId"
placeholder="请选择步骤产出物"
filterable
style="width: 100%"
:disabled="!form.taskId || !filteredOutputOptions.length"
>
<el-option
v-for="opt in filteredOutputOptions"
:key="opt.id"
:label="formatProductOptionLabel(opt)"
:label="formatStepOutputOptionLabel(opt)"
:value="opt.id"
/>
</el-select>
<p v-if="!optionsLoading && !productOptions.length" class="hint-muted">
暂无产出细胞产品,请先完成生产任务并录入输出。
<p v-if="!optionsLoading && !productionTasks.length" class="hint-muted">
该生产工单下暂无生产任务,请先派发生产任务。
</p>
<p
v-else-if="!optionsLoading && productionTasks.length && !dispatchOptions.length"
class="hint-muted"
>
暂无可派发产出物,请先在生产任务执行中录入类型为「有效物」且含条码的步骤产出。
</p>
<p
v-else-if="!optionsLoading && form.taskId && !filteredOutputOptions.length"
class="hint-muted"
>
该任务下暂无符合条件的步骤产出物。
</p>
</el-form-item>
<el-form-item label="备注" class="form-col-span-2">
@@ -69,7 +101,6 @@
class="btn-main"
type="primary"
:loading="submitting"
:disabled="!productOptions.length"
@click="handleSubmit"
>
<el-icon><Check /></el-icon>确定
@@ -83,13 +114,20 @@
import type { FormInstance, FormRules } from 'element-plus'
import { ElMessage } from 'element-plus'
import { Close, Check } from '@element-plus/icons-vue'
import { fetchProductionTaskList } from '@/api/production-task'
import {
fetchProductionWorkOrderCellProductOptions,
fetchProductionWorkOrderQcDispatchOptions,
fetchCreateQcFromProduction,
type ProductionWorkOrderItem,
type ProductionCellProductOption
type ProductionQcDispatchOption
} from '@/api/production-work-order'
interface ProductionTaskOption {
taskId: string
taskNo?: string
taskName?: string
}
defineOptions({ name: 'DispatchQcWorkOrderDialog' })
const props = defineProps<{
@@ -108,41 +146,70 @@
})
const formRef = ref<FormInstance>()
const form = reactive({ cellProductId: '', remark: '' })
const form = reactive({ taskId: '', stepOutputId: '', remark: '' })
const rules: FormRules = {
cellProductId: [{ required: true, message: '请选择细胞产品', trigger: 'change' }]
taskId: [{ required: true, message: '请选择生产任务', trigger: 'change' }],
stepOutputId: [{ required: true, message: '请选择步骤产出物', trigger: 'change' }]
}
const productOptions = ref<ProductionCellProductOption[]>([])
const dispatchOptions = ref<ProductionQcDispatchOption[]>([])
const productionTasks = ref<ProductionTaskOption[]>([])
const optionsLoading = ref(false)
const submitting = ref(false)
function formatProductOptionLabel(opt: ProductionCellProductOption) {
const taskOptions = computed(() => productionTasks.value)
const filteredOutputOptions = computed(() =>
dispatchOptions.value.filter((opt) => opt.taskId === form.taskId)
)
function formatTaskOptionLabel(opt: ProductionTaskOption) {
const parts = [opt.taskNo, opt.taskName].filter(Boolean)
return parts.join(' · ') || opt.taskId || '—'
}
function formatStepOutputOptionLabel(opt: ProductionQcDispatchOption) {
const parts = [
opt.productCode,
opt.productName,
opt.productTypeName ? `(${opt.productTypeName})` : '',
opt.passage ? `P${opt.passage}` : '',
opt.storageSlotName ? `@${opt.storageSlotName}` : ''
opt.processName,
opt.outputName,
opt.outputBarcode ? `条码 ${opt.outputBarcode}` : ''
].filter(Boolean)
const base = parts.join(' ') || opt.id
return opt.taskNo ? `${base} · ${opt.taskNo}` : base
return parts.join(' · ') || opt.id
}
function handleTaskChange() {
form.stepOutputId = ''
formRef.value?.clearValidate(['stepOutputId'])
}
async function loadOptions() {
if (!props.workOrderRow?.id) {
productOptions.value = []
dispatchOptions.value = []
productionTasks.value = []
return
}
optionsLoading.value = true
try {
productOptions.value = (await fetchProductionWorkOrderCellProductOptions(props.workOrderRow.id)) || []
const [dispatchRes, tasksRes] = await Promise.all([
fetchProductionWorkOrderQcDispatchOptions(props.workOrderRow.id),
fetchProductionTaskList({ workOrderId: props.workOrderRow.id, pageSize: 100 })
])
dispatchOptions.value = dispatchRes || []
productionTasks.value = (tasksRes?.list || []).map((task: Record<string, any>) => ({
taskId: task.id,
taskNo: task.taskNo,
taskName: task.taskName
}))
if (productionTasks.value.length === 1) {
form.taskId = productionTasks.value[0].taskId
}
} finally {
optionsLoading.value = false
}
}
function resetForm() {
form.cellProductId = ''
form.taskId = ''
form.stepOutputId = ''
form.remark = ''
formRef.value?.clearValidate()
}
@@ -157,10 +224,8 @@
await formRef.value?.validate()
submitting.value = true
try {
const selected = productOptions.value.find((o) => o.id === form.cellProductId)
await fetchCreateQcFromProduction(props.workOrderRow.id, {
cellProductId: form.cellProductId,
productionTaskId: selected?.taskId,
productionStepOutputId: form.stepOutputId,
remark: form.remark || undefined
})
ElMessage.success(
@@ -0,0 +1,220 @@
<!-- 生产任务 · 工艺步骤派发 / 调整派发 -->
<template>
<el-dialog
v-model="dialogVisible"
:title="dialogTitle"
width="860px"
class="dialog-form-layout dispatch-step-dialog"
align-center
modal-class="protocol-dialog-mask"
append-to-body
destroy-on-close
:close-on-click-modal="false"
@close="handleClose"
>
<p v-if="taskName" class="dispatch-step-dialog__task-name">任务:{{ taskName }}</p>
<el-table v-loading="loading" :data="rows" stripe class="list-table dispatch-step-table">
<el-table-column label="序号" min-width="64" align="center">
<template #default="{ $index }">{{ $index + 1 }}</template>
</el-table-column>
<el-table-column prop="processName" label="工序名称" min-width="140" show-overflow-tooltip />
<el-table-column label="负责人" min-width="220">
<template #default="{ row }">
<el-select
v-model="row.assigneeUserIds"
multiple
filterable
remote
reserve-keyword
collapse-tags
collapse-tags-tooltip
placeholder="选择负责人"
style="width: 100%"
:loading="userLoading"
:remote-method="remoteSearchUsers"
@visible-change="onUserSelectVisible"
>
<el-option
v-for="u in mergedUserOptions(row)"
:key="u.id"
:label="`${u.nickName}(${u.userNo})`"
:value="u.id"
/>
</el-select>
</template>
</el-table-column>
<el-table-column label="计划开始时间" min-width="200">
<template #default="{ row }">
<el-date-picker
v-model="row.plannedStartTime"
type="datetime"
placeholder="可选"
format="YYYY-MM-DD HH:mm"
value-format="YYYY-MM-DDTHH:mm:00"
style="width: 100%"
clearable
/>
</template>
</el-table-column>
</el-table>
<p class="form-label-tip dispatch-step-dialog__hint">至少为一个步骤指定负责人;未派发的步骤仅管理员可见。</p>
<template #footer>
<el-button class="btn-ghost" @click="handleClose">
<el-icon><Close /></el-icon>取消
</el-button>
<el-button class="btn-main" type="primary" :loading="submitting" @click="handleSubmit">
<el-icon><Check /></el-icon>确定
</el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import { ElMessage } from 'element-plus'
import { Close, Check } from '@element-plus/icons-vue'
import { fetchUserList } from '@/api/admin'
import { fetchProductionTaskDetail, fetchDispatchProductionTaskSteps } from '@/api/production-task'
defineOptions({ name: 'DispatchStepDialog' })
const props = defineProps<{
visible: boolean
taskId: string
taskName?: string
}>()
const emit = defineEmits<{
(e: 'update:visible', val: boolean): void
(e: 'success'): void
}>()
const dialogVisible = computed({
get: () => props.visible,
set: (val) => emit('update:visible', val)
})
const dialogTitle = computed(() => (hasAnyAssignee.value ? '调整步骤派发' : '步骤派发'))
const loading = ref(false)
const submitting = ref(false)
const userLoading = ref(false)
const userOptions = ref<Api.User.UserItem[]>([])
type StepDispatchRow = {
stepId: string
processName: string
assigneeUserIds: string[]
plannedStartTime: string | null
presetUsers: Api.User.UserItem[]
}
const rows = ref<StepDispatchRow[]>([])
const hasAnyAssignee = ref(false)
function mergedUserOptions(row: StepDispatchRow) {
const map = new Map<string, Api.User.UserItem>()
for (const u of userOptions.value) map.set(u.id, u)
for (const u of row.presetUsers) map.set(u.id, u)
return [...map.values()]
}
async function remoteSearchUsers(query: string) {
userLoading.value = true
try {
const res = await fetchUserList({
page: 1,
pageSize: 50,
keyword: query?.trim() || undefined,
status: '1'
})
userOptions.value = res.list || []
} catch {
userOptions.value = []
} finally {
userLoading.value = false
}
}
function onUserSelectVisible(visible: boolean) {
if (visible) void remoteSearchUsers('')
}
async function loadSteps() {
if (!props.taskId) {
rows.value = []
return
}
loading.value = true
try {
const detail = await fetchProductionTaskDetail(props.taskId)
const steps = (detail?.steps || []) as Record<string, any>[]
hasAnyAssignee.value = steps.some((s) => (s.assigneeUserIds || []).length > 0)
rows.value = steps.map((s) => ({
stepId: s.id,
processName: s.processName || '未命名步骤',
assigneeUserIds: [...(s.assigneeUserIds || [])],
plannedStartTime: s.plannedStartTime || null,
presetUsers: (s.assignees || []).map((a: Record<string, any>) => ({
id: a.userId,
nickName: a.nickName,
userNo: a.userNo
})) as Api.User.UserItem[]
}))
} finally {
loading.value = false
}
}
function handleClose() {
dialogVisible.value = false
rows.value = []
}
async function handleSubmit() {
if (!props.taskId) return
const payload = rows.value
.filter((r) => r.assigneeUserIds.length > 0)
.map((r) => ({
stepId: r.stepId,
assigneeUserIds: r.assigneeUserIds,
plannedStartTime: r.plannedStartTime
}))
if (!payload.length) {
ElMessage.warning('请至少为一个步骤指定负责人')
return
}
submitting.value = true
try {
await fetchDispatchProductionTaskSteps(props.taskId, { steps: payload })
ElMessage.success('步骤派发成功')
emit('success')
handleClose()
} finally {
submitting.value = false
}
}
watch(
() => props.visible,
(val) => {
if (val) {
void remoteSearchUsers('')
void loadSteps()
}
}
)
</script>
<style scoped lang="scss">
.dispatch-step-dialog__task-name {
margin: 0 0 12px;
font-size: 14px;
color: var(--mint-text-muted, #64748b);
}
.dispatch-step-dialog__hint {
display: block;
margin-top: 12px;
}
</style>
@@ -33,27 +33,6 @@
<el-form-item label="任务名称" prop="taskName" class="form-col-span-2">
<el-input v-model="form.taskName" placeholder="请输入任务名称" maxlength="200" />
</el-form-item>
<el-form-item label="负责人" prop="assigneeUserId" class="form-col-span-2">
<el-select
v-model="form.assigneeUserId"
filterable
remote
reserve-keyword
clearable
placeholder="输入姓名检索负责人"
style="width: 100%"
:loading="userLoading"
:remote-method="remoteSearchUsers"
@visible-change="onUserSelectVisible"
>
<el-option
v-for="u in userOptions"
:key="u.id"
:label="`${u.nickName}(${u.userNo})`"
:value="u.id"
/>
</el-select>
</el-form-item>
<el-form-item label="工艺路线" class="form-col-span-2">
<template #label
>工艺路线<span class="form-label-tip">可选,不选则从零创建工艺过程</span></template
@@ -228,7 +207,6 @@
import { Rank, Plus, CopyDocument, Delete, Close, Check } from '@element-plus/icons-vue'
import { VueDraggable as draggable } from 'vue-draggable-plus'
import { fetchCreateProductionTask } from '@/api/production-task'
import { fetchUserList } from '@/api/admin'
import { productionTaskTypeForWorkOrder } from '@/utils/production-labels'
import { fetchProcessRouteList } from '@/api/production'
import type { FormInstance, FormRules } from 'element-plus'
@@ -247,6 +225,7 @@
const emit = defineEmits<{
(e: 'update:visible', val: boolean): void
(e: 'success'): void
(e: 'needStepDispatch', payload: { taskId: string; taskName: string }): void
}>()
const dialogVisible = computed({
@@ -260,19 +239,15 @@
const formRef = ref<FormInstance>()
const routeOptions = ref<any[]>([])
const userOptions = ref<Api.User.UserItem[]>([])
const userLoading = ref(false)
const routeLoading = ref(false)
const form = reactive({
taskName: '',
assigneeUserId: '',
routeId: '',
description: '',
remark: ''
})
const rules: FormRules = {
taskName: [{ required: true, message: '请输入任务名称', trigger: 'blur' }],
assigneeUserId: [{ required: true, message: '请选择负责人', trigger: 'change' }]
taskName: [{ required: true, message: '请输入任务名称', trigger: 'blur' }]
}
const editor = useTaskStepEditor()
@@ -297,27 +272,6 @@
return editor.initEditor(taskId, options)
}
async function remoteSearchUsers(query: string) {
userLoading.value = true
try {
const res = await fetchUserList({
page: 1,
pageSize: 50,
keyword: query?.trim() || undefined,
status: '1'
})
userOptions.value = res.list || []
} catch {
userOptions.value = []
} finally {
userLoading.value = false
}
}
function onUserSelectVisible(visible: boolean) {
if (visible) void remoteSearchUsers('')
}
async function remoteSearchRoutes(query: string) {
routeLoading.value = true
try {
@@ -344,7 +298,6 @@
phase.value = 'form'
createdTaskName.value = ''
form.taskName = ''
form.assigneeUserId = ''
form.routeId = ''
form.description = ''
form.remark = ''
@@ -361,7 +314,6 @@
work_order_id: props.workOrderRow.id,
task_type: productionTaskTypeForWorkOrder(props.workOrderRow.workOrderType),
task_name: form.taskName,
assigneeUserId: form.assigneeUserId,
route_id: form.routeId,
description: form.description || undefined,
remark: form.remark || undefined
@@ -379,6 +331,9 @@
} else {
ElMessage.success('任务已创建,可编辑步骤')
}
if (expectSteps && stepCount > 0) {
emit('needStepDispatch', { taskId, taskName: form.taskName })
}
emit('success')
} catch {
/* global error handling */
@@ -399,7 +354,6 @@
// 新建任务:走阶段一表单(已在编辑阶段时不重置,避免生成任务后被 watch 打回表单)
phase.value = 'form'
form.taskName = ''
form.assigneeUserId = ''
form.routeId = ''
form.description = ''
form.remark = ''
@@ -410,7 +364,7 @@
form.taskName = `${props.workOrderRow.workOrderNo}任务`
}
void remoteSearchRoutes('')
void remoteSearchUsers('')
void editor.remoteSearchStepAssigneeUsers('')
}
}
}
@@ -14,7 +14,7 @@
@change="(val) => handleCardSelect(row, Boolean(val))"
/>
<span class="production-work-order-card__index">{{ cardIndex(idx) }}</span>
<span class="production-work-order-card__no">{{ row.workOrderNo || '—' }}</span>
<span class="production-work-order-card__no">{{ cardTitleLabel(row) }}</span>
<el-tag :type="productionWoStatusTag(row.workOrderStatus).type" size="small">
{{ productionWoStatusTag(row.workOrderStatus).label }}
</el-tag>
@@ -174,18 +174,38 @@
</section>
</div>
</header>
<QcWorkOrderProtocolPaymentPanel
v-if="showProtocolFeeSummary(row)"
:summary="row.paymentSummary"
compact
class="production-work-order-card__fee-in-head"
/>
<QcWorkOrderFeeSummaryBar
v-if="showOutboundFeeSummary(row)"
variant="basic-experiment"
:fee-summary="row.feeSummary"
class="production-work-order-card__fee-in-head"
/>
<template v-if="hasFeeInfo(row)">
<div
v-if="isMineMode && !isFeeExpanded(row.id)"
class="production-work-order-card__fee-toggle-bar"
>
<el-button class="btn-ghost" @click="toggleFeeExpand(row.id)">
<el-icon><ArrowDown /></el-icon>展开费用
</el-button>
</div>
<template v-if="!isMineMode || isFeeExpanded(row.id)">
<QcWorkOrderProtocolPaymentPanel
v-if="showProtocolFeeSummary(row)"
:summary="row.paymentSummary"
compact
class="production-work-order-card__fee-in-head"
/>
<QcWorkOrderFeeSummaryBar
v-if="showOutboundFeeSummary(row)"
variant="basic-experiment"
:fee-summary="row.feeSummary"
class="production-work-order-card__fee-in-head"
/>
</template>
<div
v-if="isMineMode && isFeeExpanded(row.id)"
class="production-work-order-card__fee-toggle-bar production-work-order-card__fee-toggle-bar--after-fee"
>
<el-button class="btn-ghost" @click="toggleFeeExpand(row.id)">
<el-icon><ArrowUp /></el-icon>收起
</el-button>
</div>
</template>
<section class="production-work-order-card__tasks">
<WorkOrderTaskSubList
embedded
@@ -218,8 +238,8 @@
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { View, Position, Document, Check, More, EditPen, Delete } from '@element-plus/icons-vue'
import { computed, ref } from 'vue'
import { View, Position, Document, Check, More, EditPen, Delete, ArrowDown, ArrowUp } from '@element-plus/icons-vue'
import type { ProductionWorkOrderItem } from '@/api/production-work-order'
import type { OutboundQcDetailProduct } from '@/api/qc-work-order'
import type { ProductionWorkOrderCardMetaConfig } from '@/utils/production-work-order-card-meta'
@@ -256,12 +276,42 @@
const isMineMode = computed(() => props.pageMode === 'mine')
const expandedFeeIds = ref<Set<string>>(new Set())
function isFeeExpanded(id: string) {
return expandedFeeIds.value.has(id)
}
function toggleFeeExpand(id: string) {
const next = new Set(expandedFeeIds.value)
if (next.has(id)) {
next.delete(id)
} else {
next.add(id)
}
expandedFeeIds.value = next
}
const cardMeta = computed(
() =>
props.cardMetaConfig ??
getCardMetaConfigForLegacyTab(props.activeTypeTab || 'inbound')
)
const INBOUND_SAMPLE_TITLE_TAB_KEYS = new Set([
'inbound_cell_storage',
'inbound_research_donation'
])
function cardTitleLabel(row: ProductionWorkOrderItem) {
if (INBOUND_SAMPLE_TITLE_TAB_KEYS.has(cardMeta.value.tabKey)) {
const lab = (row.labStorageCode || '').trim() || '—'
const sample = (row.sampleCode || '').trim() || '—'
return `${lab} - ${sample}`
}
return row.workOrderNo || '—'
}
const emit = defineEmits<{
view: [row: ProductionWorkOrderItem]
dispatch: [row: ProductionWorkOrderItem]
@@ -293,6 +343,10 @@
return cardMeta.value.showOutboundFeeSummary && !!row.feeSummary
}
function hasFeeInfo(row: ProductionWorkOrderItem) {
return showProtocolFeeSummary(row) || showOutboundFeeSummary(row)
}
function cellProducts(row: ProductionWorkOrderItem): OutboundQcDetailProduct[] {
return row.products?.length ? row.products : []
}
@@ -331,9 +385,9 @@
function dispatchQcTooltip(row: ProductionWorkOrderItem) {
if (!cardMeta.value.allowDispatchQc) return ''
if (row.workOrderStatus === 'dpz') return '请先派发并完成生产任务,录入输出细胞产品后再派发质检'
if (row.workOrderStatus === 'dpz') return '请先派发生产任务,并在步骤中录入有效物产出及条码后再派发质检'
if (row.workOrderStatus === 'yzf') return '已作废工单不可派发质检'
return '请先完成生产任务并录入输出细胞产品'
return '请先在生产任务步骤中录入类型为有效物且含条码的产出物'
}
function canComplete(row: ProductionWorkOrderItem) {
@@ -447,12 +501,28 @@
.production-work-order-card__no {
font-size: 15px;
font-weight: 600;
font-weight: 700;
color: #1e293b;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
word-break: break-all;
}
.production-work-order-card__fee-toggle-bar {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: flex-start;
gap: 8px;
padding: 8px 16px;
border-top: 1px solid #e2e8f0;
background: #fff;
&--after-fee {
border-top: none;
border-bottom: 1px solid #e2e8f0;
}
}
.production-work-order-card__meta {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
@@ -476,6 +546,7 @@
dd {
margin: 0;
font-size: 13px;
font-weight: 600;
line-height: 1.4;
color: #334155;
word-break: break-word;
@@ -13,7 +13,11 @@
{{ editor.stepStatusTag(editor.selectedStep.value.stepStatus || 'dzx').label }}
</el-tag>
<el-button
v-if="editor.selectedStep.value.stepStatus === 'dzx'"
v-if="
editor.mode.value === 'execution' &&
editor.canEditStep(editor.selectedStep.value) &&
editor.selectedStep.value.stepStatus === 'dzx'
"
class="btn-main task-exec-toolbar-btn"
type="primary"
:loading="editor.stepActionLoading.value"
@@ -23,7 +27,11 @@
<el-icon><VideoPlay /></el-icon>执行此步骤
</el-button>
<el-button
v-if="editor.selectedStep.value.stepStatus === 'jxz'"
v-if="
editor.mode.value === 'execution' &&
editor.canEditStep(editor.selectedStep.value) &&
editor.selectedStep.value.stepStatus === 'jxz'
"
class="btn-main task-exec-toolbar-btn"
type="success"
:loading="editor.stepActionLoading.value"
@@ -34,6 +42,10 @@
</el-button>
</template>
<el-button
v-if="
editor.canEditStep(editor.selectedStep.value) ||
(editor.canEditStepDispatch() && !readonly)
"
class="btn-main task-exec-toolbar-btn"
type="primary"
:loading="editor.manualSaveLoading.value"
@@ -100,6 +112,81 @@
:disabled="readonly"
/>
</el-form-item>
<el-form-item
v-if="editor.canEditStepDispatch() && !readonly"
label="负责人"
class="step-form-col-half"
>
<el-select
v-model="editor.selectedStep.value.assigneeUserIds"
multiple
filterable
remote
reserve-keyword
collapse-tags
collapse-tags-tooltip
placeholder="选择负责人(可多选)"
style="width: 100%"
:loading="editor.stepAssigneeUserOptionsLoading.value"
:remote-method="editor.remoteSearchStepAssigneeUsers"
@visible-change="editor.onStepAssigneeSelectVisible"
>
<el-option
v-for="u in editor.mergedStepAssigneeUserOptions(editor.selectedStep.value)"
:key="u.id"
:label="`${u.nickName}(${u.userNo})`"
:value="u.id"
/>
</el-select>
<p
v-if="!(editor.selectedStep.value.assigneeUserIds || []).length"
class="form-tip"
>
未派发;保存后可在本处或「步骤派发」中指定负责人
</p>
</el-form-item>
<el-form-item
v-else-if="editor.mode.value === 'edit' || editor.mode.value === 'execution'"
label="负责人"
class="step-form-col-half"
>
<template v-if="(editor.selectedStep.value.assignees || []).length">
<el-tag
v-for="a in editor.selectedStep.value.assignees"
:key="a.userId"
size="small"
class="step-assignee-readonly-tag"
>
{{ a.nickName || a.userNo || a.userId }}
</el-tag>
</template>
<span v-else class="form-tip">未派发</span>
</el-form-item>
<el-form-item
v-if="editor.canEditStepDispatch() && !readonly"
label="计划开始时间"
class="step-form-col-half"
>
<template #label
>计划开始时间<span class="form-label-tip">仅展示与排序,不限制执行</span></template
>
<el-date-picker
v-model="editor.selectedStep.value.plannedStartTime"
type="datetime"
placeholder="可选"
format="YYYY-MM-DD HH:mm"
value-format="YYYY-MM-DDTHH:mm:00"
style="width: 100%"
clearable
/>
</el-form-item>
<el-form-item
v-else-if="editor.mode.value === 'edit' || editor.mode.value === 'execution'"
label="计划开始时间"
class="step-form-col-half"
>
<span>{{ editor.selectedStep.value.plannedStartTime || '—' }}</span>
</el-form-item>
<el-form-item label="描述" class="step-form-col-half">
<el-input
v-model="editor.selectedStep.value.description"
@@ -946,6 +1033,10 @@
grid-column: 1 / -1;
}
.step-assignee-readonly-tag {
margin-right: 6px;
}
.step-sop-attachment-row {
margin-top: -4px;
@@ -155,7 +155,7 @@
<div class="execution-step-list-header">
<h3>工序步骤</h3>
<span class="execution-progress-badge"
>{{ editor.completedStepCount.value }}/{{ editor.stepList.value.length }}</span
>{{ editor.completedStepCount.value }}/{{ editor.executionStepList.value.length }}</span
>
</div>
<div class="execution-step-list-body">
@@ -227,12 +227,13 @@
@end="editor.onStepDragEnd"
>
<div
v-for="(element, index) in editor.stepList.value"
v-for="(element, index) in editor.executionStepList.value"
:key="element.id"
class="route-config-step-card"
:class="{
'is-active': editor.selectedStepId.value === element.id,
'is-done': element.stepStatus === 'ywc'
'is-done': element.stepStatus === 'ywc',
'is-readonly': !editor.canEditStep(element)
}"
@click="editor.selectStep(element)"
>
@@ -277,7 +278,7 @@
</div>
</draggable>
<div v-if="!editor.stepList.value.length" class="execution-step-list-empty">
<div v-if="!editor.executionStepList.value.length" class="execution-step-list-empty">
<el-icon :size="32" color="#a8d4c4"><Plus /></el-icon>
<p>从左侧拖拽工序到此处,添加任务步骤</p>
</div>
@@ -716,6 +717,10 @@
.route-config-step-card.is-done {
opacity: 0.6;
}
.route-config-step-card.is-readonly:not(.is-active) {
opacity: 0.72;
cursor: default;
}
/* 右侧详情面板 */
.execution-detail-panel {
@@ -96,11 +96,8 @@
<el-dropdown-item v-if="canEnterExecution(row)" command="view">
<el-icon><View /></el-icon>查看
</el-dropdown-item>
<el-dropdown-item v-if="canForwardTask(row)" command="forward">
<el-icon><Share /></el-icon>转发任务
</el-dropdown-item>
<el-dropdown-item v-if="canReassignTask(row)" command="reassign">
<el-icon><User /></el-icon>改派负责人
<el-dropdown-item v-if="canDispatchSteps(row)" command="stepDispatch">
<el-icon><Position /></el-icon>步骤派发
</el-dropdown-item>
<el-dropdown-item v-if="canEditTask(row)" command="editInfo">
<el-icon><EditPen /></el-icon>编辑信息
@@ -158,24 +155,24 @@
:task-name="executionTaskName"
:readonly="executionReadonly"
:operate-scope="operateScope"
:allow-forward="canForwardInDialog"
:allow-forward="false"
append-to-body
@success="handleTaskMutationSuccess"
/>
<ForwardTaskDialog
v-model:visible="forwardVisible"
:task-id="forwardTaskId"
:mode="forwardMode"
:operate-scope="operateScope"
@success="handleTaskMutationSuccess"
/>
<DispatchTaskDialog
v-if="workOrder"
v-model:visible="dispatchVisible"
:work-order-row="workOrder"
@success="handleTaskMutationSuccess"
@need-step-dispatch="handleNeedStepDispatch"
/>
<DispatchStepDialog
v-model:visible="stepDispatchVisible"
:task-id="stepDispatchTaskId"
:task-name="stepDispatchTaskName"
@success="handleTaskMutationSuccess"
/>
<DispatchTaskDialog
@@ -204,9 +201,7 @@
Position,
EditPen,
More,
Delete,
Share,
User
Delete
} from '@element-plus/icons-vue'
import {
fetchProductionTaskList,
@@ -215,8 +210,8 @@
type ProductionTaskOperateScope
} from '@/api/production-task'
import TaskExecutionDialog from './TaskExecutionDialog.vue'
import ForwardTaskDialog from './ForwardTaskDialog.vue'
import DispatchTaskDialog from './DispatchTaskDialog.vue'
import DispatchStepDialog from './DispatchStepDialog.vue'
import EditTaskInfoDialog from './EditTaskInfoDialog.vue'
import { useUserStore } from '@/store/modules/user'
import {
@@ -274,7 +269,10 @@
function isCurrentAssignee(row: Record<string, any>) {
const uid = String(userStore.info?.id || userStore.info?.userId || '').trim()
return uid && String(row.currentAssigneeUserId || '') === uid
if (!uid) return false
if (String(row.currentAssigneeUserId || '') === uid) return true
if (isMineMode.value && props.listMode !== 'mine-history' && !row.handoverTime) return true
return false
}
function isMineHistoryRow(row: Record<string, any>) {
@@ -290,14 +288,17 @@
return row.taskStatus === 'dpz' || row.taskStatus === 'jxz'
}
function canForwardTask(row: Record<string, any>) {
if (isMineHistoryRow(row)) return false
if (isMineMode.value) return isCurrentAssignee(row)
return operateScope.value === 'manage'
function canForwardTask(_row: Record<string, any>) {
return false
}
function canReassignTask(row: Record<string, any>) {
return !isMineMode.value && operateScope.value === 'manage'
function canReassignTask(_row: Record<string, any>) {
return false
}
function canDispatchSteps(row: Record<string, any>) {
if (isMineMode.value) return false
return row.taskStatus === 'dpz' || row.taskStatus === 'jxz'
}
function canEditTask(row: Record<string, any>) {
@@ -313,7 +314,7 @@
function hasMoreTaskActions(row: Record<string, any>) {
if (canEnterExecution(row)) return true
return canEditTask(row) || canDeleteTask(row)
return canEditTask(row) || canDeleteTask(row) || canDispatchSteps(row)
}
async function loadData() {
@@ -365,17 +366,31 @@
const dispatchVisible = ref(false)
const stepDispatchVisible = ref(false)
const stepDispatchTaskId = ref('')
const stepDispatchTaskName = ref('')
function handleNeedStepDispatch(payload: { taskId: string; taskName: string }) {
stepDispatchTaskId.value = payload.taskId
stepDispatchTaskName.value = payload.taskName
stepDispatchVisible.value = true
}
function openStepDispatch(row: Record<string, any>) {
stepDispatchTaskId.value = row.id
stepDispatchTaskName.value = row.taskName || ''
stepDispatchVisible.value = true
}
const executionDialogVisible = ref(false)
const executionTaskId = ref('')
const executionTaskName = ref('')
const executionReadonly = ref(false)
const canForwardInDialog = ref(false)
function handleEnterExecution(row: Record<string, any>) {
executionTaskId.value = row.id
executionTaskName.value = row.taskName || ''
executionReadonly.value = false
canForwardInDialog.value = canForwardTask(row)
executionDialogVisible.value = true
}
@@ -383,14 +398,9 @@
executionTaskId.value = row.id
executionTaskName.value = row.taskName || ''
executionReadonly.value = true
canForwardInDialog.value = false
executionDialogVisible.value = true
}
const forwardVisible = ref(false)
const forwardTaskId = ref('')
const forwardMode = ref<'forward' | 'reassign'>('forward')
const editInfoVisible = ref(false)
const editInfoTaskId = ref('')
@@ -412,15 +422,8 @@
editRouteTaskName.value = row.taskName || ''
editRouteVisible.value = true
break
case 'forward':
forwardTaskId.value = row.id
forwardMode.value = 'forward'
forwardVisible.value = true
break
case 'reassign':
forwardTaskId.value = row.id
forwardMode.value = 'reassign'
forwardVisible.value = true
case 'stepDispatch':
openStepDispatch(row)
break
case 'delete':
handleDelete(row)
@@ -72,6 +72,14 @@
v-model:visible="dispatchVisible"
:work-order-row="dispatchRow"
@success="refreshActiveTab"
@need-step-dispatch="handleNeedStepDispatch"
/>
<DispatchStepDialog
v-model:visible="stepDispatchVisible"
:task-id="stepDispatchTaskId"
:task-name="stepDispatchTaskName"
@success="refreshActiveTab"
/>
<DispatchQcWorkOrderDialog
@@ -117,6 +125,7 @@
} from '@/utils/production-work-order-tabs'
import ProductionWorkOrderTabPanel from './tabs/ProductionWorkOrderTabPanel.vue'
import DispatchTaskDialog from './DispatchTaskDialog.vue'
import DispatchStepDialog from './DispatchStepDialog.vue'
import DispatchQcWorkOrderDialog from './DispatchQcWorkOrderDialog.vue'
import WorkOrderRemarkDialog from '@/components/business/work-order-remark-dialog/index.vue'
import SampleReceptionDialog from './SampleReceptionDialog.vue'
@@ -210,6 +219,16 @@
const dispatchVisible = ref(false)
const dispatchRow = ref<ProductionWorkOrderItem | null>(null)
const stepDispatchVisible = ref(false)
const stepDispatchTaskId = ref('')
const stepDispatchTaskName = ref('')
function handleNeedStepDispatch(payload: { taskId: string; taskName: string }) {
stepDispatchTaskId.value = payload.taskId
stepDispatchTaskName.value = payload.taskName
stepDispatchVisible.value = true
}
function handleOpenDispatch(row: ProductionWorkOrderItem) {
dispatchRow.value = row
dispatchVisible.value = true
@@ -13,8 +13,10 @@ import {
fetchUpdateProductionTaskStatus,
fetchUpdateProductionTask,
fetchSetTaskInputs,
fetchSetTaskOutputs
fetchSetTaskOutputs,
fetchDispatchProductionTaskSteps
} from '@/api/production-task'
import { fetchUserList } from '@/api/admin'
import {
fetchAllProcesses,
fetchAllProcessCategories,
@@ -37,6 +39,7 @@ import {
} from '@/api/production-material'
import type { FinanceAttachmentItem } from '@/components/business/protocol-finance-confirm-fields/finance-attachments'
import { financeAttachmentsToUrlString } from '@/components/business/protocol-finance-confirm-fields/finance-attachments'
import { useUserStore } from '@/store/modules/user'
// ── helpers ──────────────────────────────────────────────────────────
function parseTaskAttachmentsFromUrls(urls: string): FinanceAttachmentItem[] {
@@ -57,9 +60,20 @@ function fromApiFlag(val: boolean | number | string | undefined): boolean {
return val === true || val === 1 || val === '1'
}
function normalizeStepSubTables(step: Record<string, any>) {
const assigneeUserIds = (step.assigneeUserIds || []).length
? [...step.assigneeUserIds]
: (step.assignees || [])
.map((a: Record<string, any>) => a.userId)
.filter((id: string) => String(id || '').trim())
const plannedStartTime = step.plannedStartTime || null
return {
...step,
equipmentIds: parseEquipmentIds(step.equipmentIds || step.equipmentName),
assigneeUserIds,
assignees: step.assignees || [],
plannedStartTime,
_snapshotAssigneeUserIds: [...assigneeUserIds],
_snapshotPlannedStartTime: plannedStartTime || '',
reagents: (step.reagents || []).map((r: Record<string, any>) => ({
...r,
isKeyMaterial: fromApiFlag(r.isKeyMaterial)
@@ -81,11 +95,46 @@ function parseOrderNum(value: unknown): number {
// ── composable ───────────────────────────────────────────────────────
export function useTaskStepEditor() {
const userStore = useUserStore()
const operateScope = ref<import('@/api/production-task').ProductionTaskOperateScope | undefined>(
undefined
)
const taskId = ref('')
const stepList = ref<Record<string, any>[]>([])
function canEditStep(step: Record<string, any> | null | undefined): boolean {
if (!step) return false
if (operateScope.value !== 'mine') return true
const uid = String(userStore.info?.id || userStore.info?.userId || '').trim()
const ids: string[] = step.assigneeUserIds || []
if (uid && ids.includes(uid)) return true
if (!ids.length && canEditStepOnTaskForMine(uid)) return true
return false
}
/** 步骤负责人与计划开始时间是否可在详情中编辑(执行弹窗 manage / mine 均可) */
function canEditStepDispatch(): boolean {
return true
}
function canEditStepOnTaskForMine(uid: string): boolean {
if (!uid) return false
if (stepList.value.some((s) => (s.assigneeUserIds || []).includes(uid))) return true
const taskAssignee = String(taskDetail.value?.currentAssigneeUserId || '').trim()
return taskAssignee === uid
}
const executionStepList = computed(() => {
if (operateScope.value !== 'mine') return stepList.value
const uid = String(userStore.info?.id || userStore.info?.userId || '').trim()
return stepList.value.filter((s) => {
const ids: string[] = s.assigneeUserIds || []
if (uid && ids.includes(uid)) return true
// 未派发步骤:任务内任一步负责人可在执行弹窗中看见并配置(含工艺库新增)
if (!ids.length && canEditStepOnTaskForMine(uid)) return true
return false
})
})
const selectedStepId = ref('')
const stepListLoading = ref(false)
const processLibraryLoading = ref(false)
@@ -155,6 +204,9 @@ export function useTaskStepEditor() {
const sampleCodeOptions = ref<Api.BizSampleCode.SampleCodeItem[]>([])
const sampleCodeOptionsLoading = ref(false)
const stepAssigneeUserOptions = ref<Api.User.UserItem[]>([])
const stepAssigneeUserOptionsLoading = ref(false)
function mapItemUnitOption(u: { unitName?: string; unitCode?: string }) {
const name = (u.unitName || '').trim()
const code = (u.unitCode || '').trim()
@@ -862,6 +914,7 @@ export function useTaskStepEditor() {
}
}
function selectStep(step: Record<string, any>) {
ensureStepAssigneeFields(step)
selectedStepId.value = step.id
selectedVirtualStep.value = null
}
@@ -901,6 +954,77 @@ export function useTaskStepEditor() {
}
}
function stepDispatchChanged(step: Record<string, any>): boolean {
const curIds = [...(step.assigneeUserIds || [])].sort().join(',')
const snapIds = [...(step._snapshotAssigneeUserIds || [])].sort().join(',')
if (curIds !== snapIds) return true
const curTime = step.plannedStartTime || ''
const snapTime = step._snapshotPlannedStartTime || ''
return curTime !== snapTime
}
function mergedStepAssigneeUserOptions(step: Record<string, any> | null) {
const map = new Map<string, Api.User.UserItem>()
for (const u of stepAssigneeUserOptions.value) map.set(u.id, u)
for (const a of step?.assignees || []) {
const id = String(a.userId || '').trim()
if (!id) continue
map.set(id, {
id,
nickName: a.nickName || '',
userNo: a.userNo || ''
} as Api.User.UserItem)
}
return [...map.values()]
}
async function remoteSearchStepAssigneeUsers(query: string) {
stepAssigneeUserOptionsLoading.value = true
try {
const res = await fetchUserList({
page: 1,
pageSize: 50,
keyword: query?.trim() || undefined,
status: '1'
})
stepAssigneeUserOptions.value = res.list || []
} catch {
stepAssigneeUserOptions.value = []
} finally {
stepAssigneeUserOptionsLoading.value = false
}
}
function onStepAssigneeSelectVisible(visible: boolean) {
if (visible) void remoteSearchStepAssigneeUsers('')
}
function ensureStepAssigneeFields(step: Record<string, any>) {
if (!Array.isArray(step.assigneeUserIds)) step.assigneeUserIds = []
}
async function saveStepDispatch() {
if (!selectedStep.value || !canEditStepDispatch()) return
const step = selectedStep.value
if (!stepDispatchChanged(step)) return
const assigneeUserIds = [...(step.assigneeUserIds || [])]
await fetchDispatchProductionTaskSteps(
taskId.value,
{
steps: [
{
stepId: step.id,
assigneeUserIds,
plannedStartTime: step.plannedStartTime || null
}
]
},
operateScope.value
)
step._snapshotAssigneeUserIds = [...assigneeUserIds]
step._snapshotPlannedStartTime = step.plannedStartTime || ''
}
async function saveStepBasic() {
if (!selectedStep.value) return
try {
@@ -1379,16 +1503,25 @@ export function useTaskStepEditor() {
const taskActionLoading = ref(false)
async function handleManualSave() {
if (!selectedStep.value || manualSaveLoading.value) return
const step = selectedStep.value
const canBasic = canEditStep(step)
const canDispatch = canEditStepDispatch() && stepDispatchChanged(step)
if (!canBasic && !canDispatch) return
manualSaveLoading.value = true
try {
await Promise.all([
saveStepBasic(),
saveReagents(),
saveConsumables(),
saveConfiguredReagents(),
saveOutputs(),
saveDataCollections()
])
if (canBasic) {
await Promise.all([
saveStepBasic(),
saveReagents(),
saveConsumables(),
saveConfiguredReagents(),
saveOutputs(),
saveDataCollections()
])
}
if (canDispatch) {
await saveStepDispatch()
}
ElMessage.success('保存成功')
} catch {
ElMessage.error('保存失败,请重试')
@@ -1399,6 +1532,10 @@ export function useTaskStepEditor() {
async function handleStartStep(step: Record<string, any>) {
if (!taskId.value || !step.id || stepActionLoading.value) return
if (!canEditStep(step)) {
ElMessage.warning('非当前步骤负责人,无法操作')
return
}
stepActionLoading.value = true
try {
await fetchUpdateTaskStep(taskId.value, step.id, { step_status: 'jxz' }, operateScope.value)
@@ -1413,6 +1550,10 @@ export function useTaskStepEditor() {
async function handleCompleteStep(step: Record<string, any>) {
if (!taskId.value || !step.id || stepActionLoading.value) return
if (!canEditStep(step)) {
ElMessage.warning('非当前步骤负责人,无法操作')
return
}
try {
await ElMessageBox.confirm('确定完成该步骤吗?', '提示', { type: 'warning' })
} catch {
@@ -1648,6 +1789,9 @@ export function useTaskStepEditor() {
void loadConfigReferenceData().catch(() => {
/* 工艺库参考数据失败不阻断步骤展示 */
})
if (canEditStepDispatch()) {
void remoteSearchStepAssigneeUsers('')
}
} finally {
stepListLoading.value = false
await nextTick()
@@ -1659,6 +1803,9 @@ export function useTaskStepEditor() {
return {
taskId,
stepList,
executionStepList,
canEditStep,
canEditStepDispatch,
selectedStepId,
selectedStep,
stepListLoading,
@@ -1730,6 +1877,14 @@ export function useTaskStepEditor() {
taskStatusTag,
stepStatusTag,
saveStepBasic,
saveStepDispatch,
stepDispatchChanged,
stepAssigneeUserOptions,
stepAssigneeUserOptionsLoading,
mergedStepAssigneeUserOptions,
remoteSearchStepAssigneeUsers,
onStepAssigneeSelectVisible,
ensureStepAssigneeFields,
onStepEquipmentChange,
onSopIdsChange,
searchItemUnitOptions,
+70
View File
@@ -0,0 +1,70 @@
-- 生产过程派发质检:支持仅关联步骤产出物(无细胞产品时也可派发)
ALTER TABLE `biz_qc_work_order`
ADD COLUMN `production_step_output_id` varchar(64) DEFAULT NULL
COMMENT '关联生产任务步骤产出物ID(生产过程触发时)'
AFTER `production_task_id`;
CREATE INDEX `idx_qc_wo_prod_step_output` ON `biz_qc_work_order` (`production_step_output_id`, `del_flag`);
-- 生产任务 · 工艺步骤级派发
-- 步骤计划开始时间、完成人 + 步骤负责人关联表
ALTER TABLE `biz_production_task_step`
ADD COLUMN `planned_start_time` datetime DEFAULT NULL COMMENT '计划开始时间' AFTER `step_status`;
ALTER TABLE `biz_production_task_step`
ADD COLUMN `completed_by` varchar(64) DEFAULT NULL COMMENT '完成步骤的用户ID' AFTER `completed_time`;
CREATE TABLE IF NOT EXISTS `biz_production_task_step_assignee` (
`id` varchar(64) NOT NULL COMMENT '主键',
`step_id` varchar(64) NOT NULL COMMENT '生产任务步骤ID',
`user_id` varchar(64) NOT NULL COMMENT '负责人用户ID',
`tenant_no` varchar(64) NOT NULL DEFAULT 'zkxw' COMMENT '租户编号',
`order_num` int DEFAULT 0 COMMENT '展示顺序',
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人id',
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建人部门ID',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人id',
`update_time` datetime NOT NULL COMMENT '更新时间',
`del_flag` tinyint NOT NULL DEFAULT 0 COMMENT '删除标记',
PRIMARY KEY (`id`),
KEY `idx_step_assignee_step` (`step_id`),
KEY `idx_step_assignee_user` (`user_id`, `tenant_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='生产任务步骤负责人';
-- 可选:将仍有 task.current_assignee_user_id 且无步骤负责人的旧任务回填到全部步骤
--
-- 注意:MySQL 5.7 在 INSERT...SELECT 中 UUID() 可能整批只计算一次,导致主键重复。
-- 此处用 SHA2(step_id) 生成确定性主键,每步唯一,且可重复执行(已存在则跳过)。
INSERT INTO `biz_production_task_step_assignee` (
`id`, `step_id`, `user_id`, `tenant_no`, `order_num`,
`create_by`, `create_dept_id`, `create_time`, `update_by`, `update_time`, `del_flag`
)
SELECT
LOWER(SHA2(CONCAT('step-assignee-backfill:', s.id), 256)),
s.id,
t.current_assignee_user_id,
s.tenant_no,
0,
t.create_by,
IFNULL(t.create_dept_id, ''),
NOW(),
t.update_by,
NOW(),
0
FROM `biz_production_task` t
JOIN `biz_production_task_step` s ON s.task_id = t.id AND s.del_flag = 0
WHERE t.del_flag = 0
AND t.current_assignee_user_id IS NOT NULL
AND TRIM(t.current_assignee_user_id) <> ''
AND NOT EXISTS (
SELECT 1 FROM `biz_production_task_step_assignee` a
WHERE a.step_id = s.id AND a.del_flag = 0
);