This commit is contained in:
zlwu committed 2026-06-26 00:50:04 +08:00
1 parent 0b50eff3d8
commit ee364e24aa
323 files changed
+57731 -35766

No files matched your search

-1
View File
@@ -1 +0,0 @@
78910
+43 -6
View File
@@ -1,4 +1,5 @@
"""环境检测 — 检测项维护 + 登记管理 API"""
from datetime import datetime, time
from typing import List, Optional
from fastapi import APIRouter, Depends, Query
@@ -227,10 +228,31 @@ async def delete_env_detect_item(
# ---------- 环境检测登记 ----------
def _parse_detect_date(value: Optional[str]) -> Optional[datetime]:
s = (value or "").strip()
if not s:
return None
dt = parse_datetime(s)
if not dt and len(s) >= 10:
dt = parse_datetime(f"{s[:10]} 00:00:00")
return dt
def _parse_detect_date_bound(value: Optional[str], *, end_of_day: bool = False) -> Optional[datetime]:
dt = _parse_detect_date(value)
if not dt:
return None
s = (value or "").strip()
if end_of_day and " " not in s:
return datetime.combine(dt.date(), time(23, 59, 59))
return dt
class EnvDetectRecordBody(BaseModel):
model_config = ConfigDict(populate_by_name=True)
detect_time: str = Field(..., alias="detectTime")
result_time: Optional[str] = Field(None, alias="resultTime")
inspector_ids: List[str] = Field(..., alias="inspectorIds")
env_detect_item_id: str = Field(..., alias="envDetectItemId")
result_desc: str = Field(..., alias="resultDesc")
@@ -242,6 +264,7 @@ class EnvDetectRecordUpdateBody(BaseModel):
model_config = ConfigDict(populate_by_name=True)
detect_time: Optional[str] = Field(None, alias="detectTime")
result_time: Optional[str] = Field(None, alias="resultTime")
inspector_ids: Optional[List[str]] = Field(None, alias="inspectorIds")
env_detect_item_id: Optional[str] = Field(None, alias="envDetectItemId")
result_desc: Optional[str] = Field(None, alias="resultDesc")
@@ -287,8 +310,8 @@ async def list_env_detect_records(
from sqlalchemy import func
query = query.filter(func.find_in_set(insp_id, BizEnvDetectRecord.inspector_ids) > 0)
dt_start = parse_datetime(detectTimeStart) if detectTimeStart else None
dt_end = parse_datetime(detectTimeEnd) if detectTimeEnd else None
dt_start = _parse_detect_date_bound(detectTimeStart, end_of_day=False)
dt_end = _parse_detect_date_bound(detectTimeEnd, end_of_day=True)
if dt_start:
query = query.filter(BizEnvDetectRecord.detect_time >= dt_start)
if dt_end:
@@ -338,12 +361,18 @@ async def create_env_detect_record(
user_id = _get_current_user_id(user)
sys_user = db.query(SysUser).filter(SysUser.id == user_id).first()
dept_id = (sys_user.dept_id or "") if sys_user else ""
detect_time = parse_datetime(body.detect_time)
detect_time = _parse_detect_date(body.detect_time)
if not detect_time:
return error_response(400, "检测时间格式无效")
return error_response(400, "检测日期格式无效")
result_time = None
if body.result_time:
result_time = _parse_detect_date(body.result_time)
if not result_time:
return error_response(400, "结果日期格式无效")
obj = record_svc.create_env_detect_record(
db,
detect_time=detect_time,
result_time=result_time,
inspector_ids=body.inspector_ids,
env_detect_item_id=body.env_detect_item_id,
result_desc=body.result_desc,
@@ -372,13 +401,21 @@ async def update_env_detect_record(
try:
tenant_no = _get_tenant_no(db, user)
user_id = _get_current_user_id(user)
detect_time = parse_datetime(body.detect_time) if body.detect_time else None
detect_time = _parse_detect_date(body.detect_time) if body.detect_time else None
if body.detect_time and not detect_time:
return error_response(400, "检测时间格式无效")
return error_response(400, "检测日期格式无效")
update_result_time = "result_time" in body.model_fields_set
result_time = None
if update_result_time and body.result_time:
result_time = _parse_detect_date(body.result_time)
if not result_time:
return error_response(400, "结果日期格式无效")
obj = record_svc.update_env_detect_record(
db,
row_id,
detect_time=detect_time,
result_time=result_time,
update_result_time=update_result_time,
inspector_ids=body.inspector_ids,
env_detect_item_id=body.env_detect_item_id,
result_desc=body.result_desc,
+36
View File
@@ -1752,6 +1752,42 @@ async def list_production_work_orders(
return success_response({"list": items, "total": total, "page": page, "pageSize": pageSize, "totalPages": tp})
@router.get("/production-work-orders/tab-step-counts")
async def production_work_order_tab_step_counts(
user: dict = Depends(require_admin_or_super_admin),
db: Session = Depends(get_db),
):
try:
tenant_no = _get_tenant_no(db, user)
counts = production_task_svc.count_production_work_order_tab_step_badges(
db, tenant_no=tenant_no, user_info=user
)
return success_response(counts)
except PermissionError as e:
return error_response(403, str(e))
except Exception as e:
return error_response(400, str(e))
@router.get("/production-work-orders/mine-tab-step-counts")
async def production_work_order_mine_tab_step_counts(
user: dict = Depends(require_menu_code("production:my-task:list")),
db: Session = Depends(get_db),
):
try:
tenant_no = _get_tenant_no(db, user)
counts = production_task_svc.count_mine_production_work_order_tab_step_badges(
db,
tenant_no=tenant_no,
assignee_user_id=_get_current_user_id(user),
)
return success_response(counts)
except PermissionError as e:
return error_response(403, str(e))
except Exception as e:
return error_response(400, str(e))
@router.get("/production-work-orders/{wo_id}")
async def get_production_work_order(
wo_id: str,
+4
View File
@@ -54,6 +54,7 @@ class StockInBody(BaseModel):
stock_in_no: str = Field(..., alias="stockInNo")
stock_in_date: str = Field(..., alias="stockInDate")
expire_date: str = Field(..., alias="expireDate")
production_date: Optional[str] = Field(None, alias="productionDate")
catalog_no: str = Field(..., alias="catalogNo")
batch_no: str = Field(..., alias="batchNo")
manufacturer: str
@@ -71,6 +72,7 @@ class BatchStockInItem(BaseModel):
stock_in_no: str = Field(..., alias="stockInNo")
stock_in_date: str = Field(..., alias="stockInDate")
expire_date: str = Field(..., alias="expireDate")
production_date: Optional[str] = Field(None, alias="productionDate")
catalog_no: str = Field(..., alias="catalogNo")
batch_no: str = Field(..., alias="batchNo")
manufacturer: str
@@ -125,6 +127,7 @@ class ProductionMaterialBatchUpdateBody(BaseModel):
stock_in_no: Optional[str] = Field(None, alias="stockInNo")
stock_in_date: Optional[str] = Field(None, alias="stockInDate")
expire_date: Optional[str] = Field(None, alias="expireDate")
production_date: Optional[str] = Field(None, alias="productionDate")
catalog_no: Optional[str] = Field(None, alias="catalogNo")
batch_no: Optional[str] = Field(None, alias="batchNo")
manufacturer: Optional[str] = None
@@ -528,6 +531,7 @@ async def stock_in_production_material(
amount=body.amount,
qty=Decimal(str(body.qty)),
remark=body.remark,
production_date=body.production_date,
tenant_no=tenant_no,
user_id=user_id,
operate_time=operate_time,
+110 -10
View File
@@ -1,5 +1,5 @@
"""质量检测 — 第三方/实验室主数据、质检工单、质检任务 API"""
from typing import Any, Dict, List, Optional
from typing import Any, Dict, Iterable, List, Optional
from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel, ConfigDict, Field
@@ -20,6 +20,8 @@ from app.core.menu_guard import (
)
MY_QC_MATERIAL_USAGE_VIEW_AMOUNT_PERM = "biz:myQcMaterialUsage:viewAmount"
QC_MATERIAL_VIEW_AMOUNT_PERM = "biz:qcMaterial:viewAmount"
QC_MATERIAL_AMOUNT_FIELD_NAMES = ("unitPrice", "amount", "remainingAmount", "unit_price")
from app.core.response import error_response, success_response
from app.core.utils import parse_datetime
from app.crud import qc_lab_test_item as lab_svc
@@ -557,9 +559,22 @@ async def save_item_params(
# ---------- 质检工单 ----------
class WorkOrderConfigureBatchItem(BaseModel):
model_config = ConfigDict(populate_by_name=True)
id: Optional[str] = None
batch_name: Optional[str] = Field(None, alias="batchName")
assignee_user_ids: List[str] = Field(default_factory=list, alias="assigneeUserIds")
has_lab: bool = Field(False, alias="hasLab")
has_third_party: bool = Field(False, alias="hasThirdParty")
lab_item_ids: List[str] = Field(default_factory=list, alias="labItemIds")
third_party_item_ids: List[str] = Field(default_factory=list, alias="thirdPartyItemIds")
class WorkOrderConfigureBody(BaseModel):
model_config = ConfigDict(populate_by_name=True)
batches: Optional[List[WorkOrderConfigureBatchItem]] = Field(None, alias="batches")
has_lab: bool = Field(False, alias="hasLab")
has_third_party: bool = Field(False, alias="hasThirdParty")
lab_item_ids: List[str] = Field(default_factory=list, alias="labItemIds")
@@ -671,6 +686,42 @@ async def list_qc_work_orders(
return error_response(400, str(e))
@router.get("/qc-work-orders/tab-unfinished-counts")
async def qc_work_order_tab_unfinished_counts(
db: Session = Depends(get_db),
user: dict = Depends(require_menu_code("biz:qcWorkOrder:list")),
):
try:
tenant_no = _get_tenant_no(db, user)
counts = wo_svc.count_unfinished_work_orders_by_tab(
db, tenant_no=tenant_no, user_info=user
)
return success_response(counts)
except PermissionError as e:
return error_response(403, str(e))
except Exception as e:
return error_response(400, str(e))
@router.get("/qc-work-orders/mine-tab-unfinished-counts")
async def qc_work_order_mine_tab_unfinished_counts(
db: Session = Depends(get_db),
user: dict = Depends(require_menu_code("biz:myQcTask:list")),
):
try:
tenant_no = _get_tenant_no(db, user)
counts = wo_svc.count_mine_unfinished_work_orders_by_tab(
db,
tenant_no=tenant_no,
assignee_user_id=_get_current_user_id(user),
)
return success_response(counts)
except PermissionError as e:
return error_response(403, str(e))
except Exception as e:
return error_response(400, str(e))
@router.get("/qc-work-orders/{work_order_id}")
async def get_qc_work_order_detail(
work_order_id: str,
@@ -769,6 +820,7 @@ async def configure_qc_work_order(
third_party_item_ids=body.third_party_item_ids,
lab_item_assignees=body.lab_item_assignees,
third_party_item_assignees=body.third_party_item_assignees,
batches=[b.model_dump(by_alias=True) for b in body.batches] if body.batches else None,
tenant_no=tenant_no,
user_id=_get_current_user_id(user),
create_dept_id=user.get("dept_id", ""),
@@ -782,6 +834,26 @@ async def configure_qc_work_order(
return error_response(400, str(e))
@router.get("/qc-work-orders/{work_order_id}/inspection-batches")
async def list_qc_work_order_inspection_batches(
work_order_id: str,
db: Session = Depends(get_db),
user: dict = Depends(require_any_menu_code("biz:qcWorkOrder:list", "biz:myQcTask:list")),
):
try:
tenant_no = _get_tenant_no(db, user)
from app.crud import qc_inspection_batch as batch_svc
wo = wo_svc.get_work_order(db, work_order_id, tenant_no=tenant_no)
if not wo:
return error_response(404, "工单不存在")
return success_response(batch_svc.list_inspection_batches(db, work_order_id, tenant_no))
except PermissionError as e:
return error_response(403, str(e))
except Exception as e:
return error_response(400, str(e))
# ---------- 质检任务 ----------
@@ -1113,6 +1185,23 @@ async def delete_qc_task_api(
# ---------- 质检物料库 ----------
def _maybe_strip_qc_material_amount_fields(
db: Session, user: dict, rows: Iterable[Dict[str, Any]]
) -> None:
if user_has_menu_perm(db, user, QC_MATERIAL_VIEW_AMOUNT_PERM):
return
strip_row_fields(rows, *QC_MATERIAL_AMOUNT_FIELD_NAMES)
def _maybe_strip_qc_material_stock_check_detail(db: Session, user: dict, data: Dict[str, Any]) -> None:
if user_has_menu_perm(db, user, QC_MATERIAL_VIEW_AMOUNT_PERM):
return
for key in ("batchLines", "unusedLines"):
lines = data.get(key)
if isinstance(lines, list):
strip_row_fields(lines, *QC_MATERIAL_AMOUNT_FIELD_NAMES)
class QcMaterialBody(BaseModel):
model_config = ConfigDict(populate_by_name=True)
material_code: str = Field(..., alias="materialCode")
@@ -1246,6 +1335,7 @@ async def list_qc_material_batches(
tenant_no=tenant_no,
user_info=user,
)
_maybe_strip_qc_material_amount_fields(db, user, items)
return success_response(_page_payload(items, total, page, pageSize))
except PermissionError as e:
return error_response(403, str(e))
@@ -1263,15 +1353,15 @@ async def qc_material_batch_options(
):
try:
tenant_no = _get_tenant_no(db, user)
return success_response(
batch_svc.batch_options(
db,
tenant_no,
keyword=keyword,
material_id=materialId,
only_available=onlyAvailable,
)
options = batch_svc.batch_options(
db,
tenant_no,
keyword=keyword,
material_id=materialId,
only_available=onlyAvailable,
)
_maybe_strip_qc_material_amount_fields(db, user, options)
return success_response(options)
except Exception as e:
return error_response(400, str(e))
@@ -1295,8 +1385,10 @@ async def update_qc_material_batch(
**data,
)
material = mat_svc.get_material(db, obj.material_id, tenant_no=tenant_no)
payload = batch_svc._batch_row_to_dict(obj, material)
_maybe_strip_qc_material_amount_fields(db, user, [payload])
return success_response(
batch_svc._batch_row_to_dict(obj, material),
payload,
message="更新成功",
)
except ValueError as e:
@@ -1660,6 +1752,7 @@ async def list_qc_material_usages(
keyword=keyword, stock_in_no=stockInNo,
user_info=user,
)
_maybe_strip_qc_material_amount_fields(db, user, items)
return success_response(_page_payload(items, total, page, pageSize))
except PermissionError as e:
return error_response(403, str(e))
@@ -1720,6 +1813,11 @@ async def list_qc_material_linked_task_usages(
items = mu_svc.list_linked_task_usages(
db, usage_id, tenant_no, user_info=user
)
if not (
user_has_menu_perm(db, user, QC_MATERIAL_VIEW_AMOUNT_PERM)
or user_has_menu_perm(db, user, MY_QC_MATERIAL_USAGE_VIEW_AMOUNT_PERM)
):
strip_row_fields(items, *QC_MATERIAL_AMOUNT_FIELD_NAMES)
return success_response(items)
except HTTPException as e:
detail = e.detail
@@ -1750,6 +1848,7 @@ async def list_qc_material_stock_check_requisitions(
keyword=keyword,
user_info=user,
)
_maybe_strip_qc_material_amount_fields(db, user, items)
return success_response(_page_payload(items, total, page, pageSize))
except PermissionError as e:
return error_response(403, str(e))
@@ -1886,6 +1985,7 @@ async def get_qc_material_stock_check(
try:
tenant_no = _get_tenant_no(db, user)
data = stock_check_svc.get_check_detail(db, check_id, tenant_no)
_maybe_strip_qc_material_stock_check_detail(db, user, data)
return success_response(data)
except ValueError as e:
return error_response(400, str(e))
+9 -2
View File
@@ -7,7 +7,7 @@ from typing import Any, Dict, List, Optional, Tuple
from sqlalchemy import and_, func, or_
from sqlalchemy.orm import Session
from app.core.utils import generate_uuid
from app.core.utils import format_date, generate_uuid
from app.crud import env_detect_item as item_svc
from app.models.models import BizEnvDetectRecord, SysUser
@@ -26,7 +26,8 @@ def _row_to_dict(r: BizEnvDetectRecord) -> Dict[str, Any]:
return {
"id": r.id,
"recordNo": r.record_no,
"detectTime": r.detect_time.strftime("%Y-%m-%d %H:%M:%S") if r.detect_time else "",
"detectTime": format_date(r.detect_time),
"resultTime": format_date(r.result_time) if r.result_time else "",
"inspectorIds": inspector_ids,
"inspectorNames": inspector_names,
"inspectorDisplay": " / ".join(inspector_names) if inspector_names else "",
@@ -181,6 +182,7 @@ def create_env_detect_record(
db: Session,
*,
detect_time: datetime,
result_time: Optional[datetime] = None,
inspector_ids: List[str],
env_detect_item_id: str,
result_desc: str,
@@ -201,6 +203,7 @@ def create_env_detect_record(
id=generate_uuid(),
record_no=_generate_record_no(db, tenant_no),
detect_time=detect_time,
result_time=result_time,
inspector_ids=ids_csv,
inspector_names=names_csv,
env_detect_item_id=item_id,
@@ -229,6 +232,8 @@ def update_env_detect_record(
row_id: str,
*,
detect_time: Optional[datetime] = None,
result_time: Optional[datetime] = None,
update_result_time: bool = False,
inspector_ids: Optional[List[str]] = None,
env_detect_item_id: Optional[str] = None,
result_desc: Optional[str] = None,
@@ -244,6 +249,8 @@ def update_env_detect_record(
if not detect_time:
raise ValueError("检测时间不能为空")
obj.detect_time = detect_time
if update_result_time:
obj.result_time = result_time
if inspector_ids is not None:
ids_csv, names_csv = _resolve_inspector_snapshots(db, obj.tenant_no, inspector_ids)
obj.inspector_ids = ids_csv
@@ -35,6 +35,15 @@ def _parse_expire_date(v: Any) -> date:
return _parse_date(v, label="过期日期")
def _parse_optional_date(v: Any, *, label: str = "日期") -> Optional[date]:
if v is None:
return None
s = str(v).strip()
if not s:
return None
return _parse_date(s, label=label)
def _require_str(v: Any, label: str) -> str:
s = str(v or "").strip()
if not s:
@@ -96,6 +105,7 @@ def _batch_row_to_dict(b: BizProductionMaterialStockBatch, material: Optional[Bi
"stockInNo": b.stock_in_no or "",
"stockInDate": _fmt_date(b.stock_in_date),
"expireDate": _fmt_date(b.expire_date),
"productionDate": _fmt_date(b.production_date),
"catalogNo": b.catalog_no or "",
"batchNo": b.batch_no or "",
"manufacturer": b.manufacturer or "",
@@ -281,6 +291,7 @@ def batch_options(
"stockInNo": b.stock_in_no or "",
"stockInDate": _fmt_date(b.stock_in_date),
"expireDate": _fmt_date(b.expire_date),
"productionDate": _fmt_date(b.production_date),
"materialId": b.material_id,
"materialType": b.material_type or "",
"materialCode": b.material_code or "",
@@ -321,6 +332,7 @@ def _create_batch_and_usage(
commit: bool = False,
source_type: str = "manual_in",
prep_id: Optional[str] = None,
production_date: Optional[date] = None,
) -> BizProductionMaterialStockBatch:
if qty <= 0:
raise ValueError("入库数量必须大于0")
@@ -351,6 +363,7 @@ def _create_batch_and_usage(
stock_in_no=sno,
stock_in_date=stock_in_date,
expire_date=expire_date,
production_date=production_date,
catalog_no=catalog_no,
batch_no=batch_no,
manufacturer=manufacturer,
@@ -417,6 +430,7 @@ def stock_in_one(
qty: Decimal,
remark: Optional[str] = None,
storage_location: Optional[str] = None,
production_date: Any = None,
tenant_no: str,
user_id: Optional[str] = None,
operate_time: Optional[datetime] = None,
@@ -440,6 +454,7 @@ def stock_in_one(
tenant_no=tenant_no,
user_id=user_id,
operate_time=op_time,
production_date=_parse_optional_date(production_date, label="生产日期"),
commit=True,
)
return batch
@@ -493,6 +508,10 @@ def batch_stock_in(
tenant_no=tenant_no,
user_id=user_id,
operate_time=op_time,
production_date=_parse_optional_date(
it.get("production_date") or it.get("productionDate"),
label="生产日期",
),
commit=False,
)
db.commit()
@@ -796,6 +815,7 @@ def update_batch(
stock_in_no: Optional[str] = None,
stock_in_date: Optional[Any] = None,
expire_date: Optional[Any] = None,
production_date: Optional[Any] = None,
catalog_no: Optional[str] = None,
batch_no: Optional[str] = None,
manufacturer: Optional[str] = None,
@@ -848,6 +868,8 @@ def update_batch(
batch.stock_in_date = _parse_date(stock_in_date)
if expire_date is not None:
batch.expire_date = _parse_expire_date(expire_date)
if production_date is not None:
batch.production_date = _parse_optional_date(production_date, label="生产日期")
if catalog_no is not None:
batch.catalog_no = _require_str(catalog_no, "货号")
if batch_no is not None:
+186 -2
View File
@@ -8,7 +8,7 @@ from typing import Any, Dict, List, Optional, Tuple
from sqlalchemy import and_, func, or_
from sqlalchemy.orm import Session
from app.core.utils import generate_uuid, parse_datetime
from app.core.utils import format_date, generate_uuid, parse_datetime
from app.crud.equipment_snapshot import parse_equipment_ids, resolve_equipment_list_snapshot
from app.models.models import (
BizCellProduct,
@@ -306,7 +306,7 @@ 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)),
"plannedStartTime": format_date(getattr(s, "planned_start_time", None)) or "",
"startedTime": _fmt_dt(s.started_time),
"completedTime": _fmt_dt(s.completed_time),
"completedBy": getattr(s, "completed_by", None) or "",
@@ -2621,3 +2621,187 @@ def total_pages(total: int, page_size: int) -> int:
if total <= 0:
return 0
return int(math.ceil(total / float(page_size)))
_PRODUCTION_TAB_STEP_COUNT_KEYS = (
"inbound_cell_storage",
"inbound_research_donation",
"outbound_pilot_service",
"outbound_basic_experiment",
"temporary",
)
def _empty_production_tab_step_counts() -> Dict[str, Dict[str, int]]:
return {
k: {"overdueIncomplete": 0, "unassigned": 0}
for k in _PRODUCTION_TAB_STEP_COUNT_KEYS
}
def _resolve_production_tab_key(
wo_type: Optional[str],
reception_source: Optional[str],
biz_type: Optional[str],
) -> Optional[str]:
wt = (wo_type or "inbound").strip() or "inbound"
if wt == "temporary":
return "temporary"
if wt == "outbound":
bt = (biz_type or "pilot_service").strip() or "pilot_service"
if bt == "basic_experiment":
return "outbound_basic_experiment"
if bt == "clinical_application":
return None
return "outbound_pilot_service"
rs = (reception_source or "cell_storage").strip() or "cell_storage"
return "inbound_research_donation" if rs == "research_donation" else "inbound_cell_storage"
def _aggregate_production_tab_step_field(
counts: Dict[str, Dict[str, int]],
rows,
field: str,
) -> None:
for wo_type, rs, bt, cnt in rows:
tab_key = _resolve_production_tab_key(wo_type, rs, bt)
if not tab_key or tab_key not in counts:
continue
counts[tab_key][field] += int(cnt or 0)
def _base_active_step_count_query(
db: Session,
*,
tenant_no: str,
user_info=None,
mine_user_id: Optional[str] = None,
):
q = (
db.query(
BizProductionWorkOrder.work_order_type,
BizProductionWorkOrder.reception_source,
BizProductionWorkOrder.biz_type,
func.count(BizProductionTaskStep.id),
)
.select_from(BizProductionTaskStep)
.join(
BizProductionTask,
and_(
BizProductionTask.id == BizProductionTaskStep.task_id,
BizProductionTask.del_flag == 0,
BizProductionTask.tenant_no == tenant_no,
),
)
.join(
BizProductionWorkOrder,
and_(
BizProductionWorkOrder.id == BizProductionTask.work_order_id,
BizProductionWorkOrder.del_flag == 0,
BizProductionWorkOrder.tenant_no == tenant_no,
),
)
.filter(
BizProductionTaskStep.del_flag == 0,
BizProductionTaskStep.tenant_no == tenant_no,
BizProductionWorkOrder.work_order_status.in_(("dpz", "jxz")),
BizProductionTask.task_status.in_(("dpz", "jxz")),
BizProductionTaskStep.step_status != "ywc",
)
)
if user_info:
from app.core.data_scope import apply_data_scope
q = apply_data_scope(q, BizProductionWorkOrder, user_info, db, tenant_no)
uid = (mine_user_id or "").strip()
if uid:
step_task_ids = (
db.query(BizProductionTaskStep.task_id)
.join(
BizProductionTaskStepAssignee,
BizProductionTaskStepAssignee.step_id == BizProductionTaskStep.id,
)
.filter(
BizProductionTaskStepAssignee.user_id == 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 == uid,
)
)
return q
def count_step_tab_badges_by_tab(
db: Session,
*,
tenant_no: str,
user_info=None,
mine_user_id: Optional[str] = None,
) -> Dict[str, Dict[str, int]]:
"""按 Tab 统计工艺步骤角标:计划开始已到期未完成数 + 未安排负责人数。"""
counts = _empty_production_tab_step_counts()
today_end = datetime.now().replace(hour=23, minute=59, second=59, microsecond=0)
overdue_rows = (
_base_active_step_count_query(
db, tenant_no=tenant_no, user_info=user_info, mine_user_id=mine_user_id
)
.filter(
BizProductionTaskStep.planned_start_time.isnot(None),
BizProductionTaskStep.planned_start_time <= today_end,
)
.group_by(
BizProductionWorkOrder.work_order_type,
BizProductionWorkOrder.reception_source,
BizProductionWorkOrder.biz_type,
)
.all()
)
_aggregate_production_tab_step_field(counts, overdue_rows, "overdueIncomplete")
assignee_exists = (
db.query(BizProductionTaskStepAssignee.id)
.filter(
BizProductionTaskStepAssignee.step_id == BizProductionTaskStep.id,
BizProductionTaskStepAssignee.tenant_no == tenant_no,
BizProductionTaskStepAssignee.del_flag == 0,
)
.exists()
)
unassigned_rows = (
_base_active_step_count_query(
db, tenant_no=tenant_no, user_info=user_info, mine_user_id=mine_user_id
)
.filter(~assignee_exists)
.group_by(
BizProductionWorkOrder.work_order_type,
BizProductionWorkOrder.reception_source,
BizProductionWorkOrder.biz_type,
)
.all()
)
_aggregate_production_tab_step_field(counts, unassigned_rows, "unassigned")
return counts
def count_production_work_order_tab_step_badges(
db: Session, *, tenant_no: str, user_info=None
) -> Dict[str, Dict[str, int]]:
return count_step_tab_badges_by_tab(db, tenant_no=tenant_no, user_info=user_info)
def count_mine_production_work_order_tab_step_badges(
db: Session, *, tenant_no: str, assignee_user_id: str
) -> Dict[str, Dict[str, int]]:
return count_step_tab_badges_by_tab(
db, tenant_no=tenant_no, mine_user_id=assignee_user_id
)
+848
View File
@@ -0,0 +1,848 @@
"""质检工单检测批次 CRUD"""
from __future__ import annotations
from datetime import datetime
from decimal import Decimal
from typing import Any, Dict, List, Optional, Set, Tuple
from sqlalchemy.orm import Session
from app.core.utils import generate_uuid
from app.crud import qc_lab_test_item as lab_item_svc
from app.crud import qc_task as task_svc
from app.crud import qc_task_assignee as assignee_svc
from app.crud import qc_third_party_item as third_item_svc
from app.crud.qc_third_party_company import get_company
from app.models.models import (
BizQcBatchAssignee,
BizQcTask,
BizQcWorkOrder,
BizQcWorkOrderInspectionBatch,
BizQcWorkOrderLabItem,
BizQcWorkOrderThirdItem,
SysUser,
)
def _dec(v) -> float:
if v is None:
return 0.0
return float(v)
def _unique_ids(ids: List[str]) -> List[str]:
seen: Set[str] = set()
out: List[str] = []
for x in ids:
v = (x or "").strip()
if not v or v in seen:
continue
seen.add(v)
out.append(v)
return out
def _validate_batch_payload(batch: Dict[str, Any], index: int) -> None:
label = f"批次 {index + 1}"
batch_name = (batch.get("batchName") or batch.get("batch_name") or "").strip()
lab_ids = _unique_ids(batch.get("labItemIds") or batch.get("lab_item_ids") or [])
third_ids = _unique_ids(batch.get("thirdPartyItemIds") or batch.get("third_party_item_ids") or [])
assignees = batch.get("assigneeUserIds") or batch.get("assignee_user_ids") or []
has_lab = bool(batch.get("hasLab") or batch.get("has_lab")) or bool(lab_ids)
has_third = bool(batch.get("hasThirdParty") or batch.get("has_third_party")) or bool(third_ids)
if not batch_name:
raise ValueError(f"{label}:请填写批次名称")
if not has_lab and not has_third:
raise ValueError(f"{label}:请至少选择一项检测项目")
if has_lab and not lab_ids:
raise ValueError(f"{label}:请选择实验室自检项目")
if has_third and not third_ids:
raise ValueError(f"{label}:请选择第三方检测项目")
if len(lab_ids) != len(set(lab_ids)):
raise ValueError(f"{label}:实验室项目不能重复")
if len(third_ids) != len(set(third_ids)):
raise ValueError(f"{label}:第三方项目不能重复")
if not assignees:
raise ValueError(f"{label}:请指定负责人")
def _order_source_for_wo(wo: BizQcWorkOrder) -> str:
wo_type = getattr(wo, "work_order_type", None) or "inbound"
if wo_type == "temporary":
return "temporary"
if wo_type == "outbound":
return "outbound"
return "inbound"
def _next_batch_no(db: Session, wo: BizQcWorkOrder, tenant_no: str) -> str:
prefix = f"{wo.work_order_no}-B"
rows = (
db.query(BizQcWorkOrderInspectionBatch.batch_no)
.filter(
BizQcWorkOrderInspectionBatch.work_order_id == wo.id,
BizQcWorkOrderInspectionBatch.tenant_no == tenant_no,
BizQcWorkOrderInspectionBatch.del_flag == 0,
)
.all()
)
max_seq = 0
for (bn,) in rows:
s = (bn or "")[len(prefix) :]
if s.isdigit():
max_seq = max(max_seq, int(s))
return f"{prefix}{max_seq + 1:02d}"
def _allocate_batch_name(db: Session, wo_id: str, tenant_no: str, day: datetime) -> str:
base = day.strftime("%Y%m%d") + "检测"
rows = (
db.query(BizQcWorkOrderInspectionBatch.batch_name)
.filter(
BizQcWorkOrderInspectionBatch.work_order_id == wo_id,
BizQcWorkOrderInspectionBatch.tenant_no == tenant_no,
BizQcWorkOrderInspectionBatch.del_flag == 0,
)
.all()
)
same_day = [n for (n,) in rows if (n or "").startswith(base)]
if not same_day:
return base
if base not in same_day:
return base
suffix = 2
while f"{base}-{suffix}" in same_day:
suffix += 1
return f"{base}-{suffix}"
def sync_batch_assignees(
db: Session,
batch_id: str,
user_ids: List[str],
*,
tenant_no: str,
operator_id: Optional[str],
create_dept_id: str = "",
) -> List[str]:
normalized = assignee_svc.validate_assignee_user_ids(db, user_ids, tenant_no)
now = datetime.now()
existing = (
db.query(BizQcBatchAssignee)
.filter(
BizQcBatchAssignee.batch_id == batch_id,
BizQcBatchAssignee.tenant_no == tenant_no,
BizQcBatchAssignee.del_flag == 0,
)
.all()
)
keep = set(normalized)
for row in existing:
if row.user_id not in keep:
row.del_flag = 1
row.update_by = operator_id
row.update_time = now
for idx, uid in enumerate(normalized):
hit = next((r for r in existing if r.user_id == uid and r.del_flag == 0), None)
if hit:
continue
db.add(
BizQcBatchAssignee(
id=generate_uuid(),
batch_id=batch_id,
user_id=uid,
tenant_no=tenant_no,
order_num=idx,
create_by=operator_id,
create_dept_id=create_dept_id or "",
create_time=now,
update_by=operator_id,
update_time=now,
del_flag=0,
)
)
db.flush()
return normalized
def _batch_assignee_user_ids(db: Session, batch_id: str, tenant_no: str) -> List[str]:
rows = (
db.query(BizQcBatchAssignee.user_id)
.filter(
BizQcBatchAssignee.batch_id == batch_id,
BizQcBatchAssignee.tenant_no == tenant_no,
BizQcBatchAssignee.del_flag == 0,
)
.order_by(BizQcBatchAssignee.order_num.asc())
.all()
)
return [r[0] for r in rows]
def _assignee_names(db: Session, user_ids: List[str]) -> str:
if not user_ids:
return ""
rows = (
db.query(SysUser.id, SysUser.nick_name, SysUser.user_no)
.filter(SysUser.id.in_(user_ids), SysUser.del_flag == 0)
.all()
)
name_map = {r.id: (r.nick_name or r.user_no or r.id) for r in rows}
return "、".join(name_map.get(uid, uid) for uid in user_ids)
def _apply_assignees_to_batch_tasks(
db: Session,
batch_id: str,
user_ids: List[str],
*,
tenant_no: str,
operator_id: Optional[str],
create_dept_id: str,
) -> None:
tasks = (
db.query(BizQcTask)
.filter(
BizQcTask.batch_id == batch_id,
BizQcTask.tenant_no == tenant_no,
BizQcTask.del_flag == 0,
)
.all()
)
for task in tasks:
assignee_svc.sync_assignees_for_task(
db,
task.id,
user_ids,
tenant_no=tenant_no,
operator_id=operator_id,
create_dept_id=create_dept_id,
)
def _batch_has_blocking_tasks(db: Session, batch_id: str, tenant_no: str) -> bool:
return (
db.query(BizQcTask)
.filter(
BizQcTask.batch_id == batch_id,
BizQcTask.tenant_no == tenant_no,
BizQcTask.del_flag == 0,
BizQcTask.task_status.in_(["jxz", "ywc"]),
)
.first()
is not None
)
def _soft_delete_batch(
db: Session,
batch: BizQcWorkOrderInspectionBatch,
*,
tenant_no: str,
user_id: Optional[str],
) -> None:
from app.crud.qc_work_order import _soft_delete_tasks_by_source_line_ids
if _batch_has_blocking_tasks(db, batch.id, tenant_no):
raise ValueError(f"批次「{batch.batch_name}」存在进行中或已完成任务,无法删除")
now = datetime.now()
lab_lines = (
db.query(BizQcWorkOrderLabItem)
.filter(
BizQcWorkOrderLabItem.batch_id == batch.id,
BizQcWorkOrderLabItem.del_flag == 0,
)
.all()
)
third_lines = (
db.query(BizQcWorkOrderThirdItem)
.filter(
BizQcWorkOrderThirdItem.batch_id == batch.id,
BizQcWorkOrderThirdItem.del_flag == 0,
)
.all()
)
line_ids = [r.id for r in lab_lines + third_lines]
for line in lab_lines + third_lines:
line.del_flag = 1
line.update_by = user_id
line.update_time = now
_soft_delete_tasks_by_source_line_ids(db, line_ids, tenant_no)
assignees = (
db.query(BizQcBatchAssignee)
.filter(BizQcBatchAssignee.batch_id == batch.id, BizQcBatchAssignee.del_flag == 0)
.all()
)
for a in assignees:
a.del_flag = 1
a.update_by = user_id
a.update_time = now
batch.del_flag = 1
batch.update_by = user_id
batch.update_time = now
def _sync_batch_lab_items(
db: Session,
wo: BizQcWorkOrder,
batch: BizQcWorkOrderInspectionBatch,
*,
order_source: str,
lab_item_ids: List[str],
assignee_user_ids: List[str],
tenant_no: str,
user_id: Optional[str],
create_dept_id: str,
seq_start: int,
) -> int:
from app.crud.qc_work_order import _get_task_by_source_line_id, _soft_delete_tasks_by_source_line_ids
now = datetime.now()
existing_rows = (
db.query(BizQcWorkOrderLabItem)
.filter(
BizQcWorkOrderLabItem.batch_id == batch.id,
BizQcWorkOrderLabItem.del_flag == 0,
)
.all()
)
existing_by_item_id = {r.lab_test_item_id: r for r in existing_rows}
new_id_set = set(lab_item_ids)
old_id_set = set(existing_by_item_id.keys())
for item_id in old_id_set - new_id_set:
line = existing_by_item_id[item_id]
task = _get_task_by_source_line_id(db, line.id, tenant_no)
if task and (task.task_status or "") in ("jxz", "ywc"):
raise ValueError(f"项目「{line.item_name}」任务进行中或已完成,无法移除")
line.del_flag = 1
line.update_by = user_id
line.update_time = now
db.add(line)
remove_line_ids = [existing_by_item_id[i].id for i in old_id_set - new_id_set]
_soft_delete_tasks_by_source_line_ids(db, remove_line_ids, tenant_no)
seq = seq_start
for lid in lab_item_ids:
seq += 1
if lid in existing_by_item_id and lid in new_id_set:
line = existing_by_item_id[lid]
line.order_num = seq
line.update_by = user_id
line.update_time = now
db.add(line)
task = _get_task_by_source_line_id(db, line.id, tenant_no)
if task:
task.order_num = seq
task.batch_id = batch.id
task.update_by = user_id
task.update_time = now
db.add(task)
else:
task = task_svc.create_task_for_line(
db,
work_order=wo,
task_type="sysz",
source_line_id=line.id,
item_code=line.item_code,
item_name=line.item_name,
tenant_no=tenant_no,
user_id=user_id,
create_dept_id=create_dept_id,
seq=seq,
order_source=order_source,
batch_id=batch.id,
)
assignee_svc.sync_assignees_for_task(
db,
task.id,
assignee_user_ids,
tenant_no=tenant_no,
operator_id=user_id,
create_dept_id=create_dept_id,
)
continue
item = lab_item_svc.get_lab_item(db, lid, tenant_no=tenant_no)
if not item or item.status != "1":
raise ValueError("实验室项目无效或已停用")
line = BizQcWorkOrderLabItem(
id=generate_uuid(),
work_order_id=wo.id,
order_source=order_source,
batch_id=batch.id,
lab_test_item_id=item.id,
item_code=item.item_code,
item_name=item.item_name,
tenant_no=tenant_no,
order_num=seq,
create_by=user_id,
create_dept_id=create_dept_id or "",
create_time=now,
update_by=user_id,
update_time=now,
del_flag=0,
)
db.add(line)
db.flush()
task = task_svc.create_task_for_line(
db,
work_order=wo,
task_type="sysz",
source_line_id=line.id,
item_code=line.item_code,
item_name=line.item_name,
tenant_no=tenant_no,
user_id=user_id,
create_dept_id=create_dept_id,
seq=seq,
order_source=order_source,
batch_id=batch.id,
)
assignee_svc.sync_assignees_for_task(
db,
task.id,
assignee_user_ids,
tenant_no=tenant_no,
operator_id=user_id,
create_dept_id=create_dept_id,
)
return seq
def _sync_batch_third_items(
db: Session,
wo: BizQcWorkOrder,
batch: BizQcWorkOrderInspectionBatch,
*,
order_source: str,
third_party_item_ids: List[str],
assignee_user_ids: List[str],
tenant_no: str,
user_id: Optional[str],
create_dept_id: str,
seq_start: int,
) -> Tuple[int, Decimal]:
from app.crud.qc_work_order import _get_task_by_source_line_id, _soft_delete_tasks_by_source_line_ids
now = datetime.now()
existing_rows = (
db.query(BizQcWorkOrderThirdItem)
.filter(
BizQcWorkOrderThirdItem.batch_id == batch.id,
BizQcWorkOrderThirdItem.del_flag == 0,
)
.all()
)
existing_by_item_id = {r.third_party_item_id: r for r in existing_rows}
new_id_set = set(third_party_item_ids)
old_id_set = set(existing_by_item_id.keys())
for item_id in old_id_set - new_id_set:
line = existing_by_item_id[item_id]
task = _get_task_by_source_line_id(db, line.id, tenant_no)
if task and (task.task_status or "") in ("jxz", "ywc"):
raise ValueError(f"项目「{line.item_name}」任务进行中或已完成,无法移除")
line.del_flag = 1
line.update_by = user_id
line.update_time = now
db.add(line)
remove_line_ids = [existing_by_item_id[i].id for i in old_id_set - new_id_set]
_soft_delete_tasks_by_source_line_ids(db, remove_line_ids, tenant_no)
fee_total = Decimal("0")
seq = seq_start
for tid in third_party_item_ids:
seq += 1
if tid in existing_by_item_id and tid in new_id_set:
line = existing_by_item_id[tid]
line.order_num = seq
line.update_by = user_id
line.update_time = now
db.add(line)
fee_total += line.fee_amount or Decimal("0")
task = _get_task_by_source_line_id(db, line.id, tenant_no)
if task:
task.order_num = seq
task.batch_id = batch.id
task.update_by = user_id
task.update_time = now
db.add(task)
else:
task = task_svc.create_task_for_line(
db,
work_order=wo,
task_type="dsfjc",
source_line_id=line.id,
item_code=line.item_code,
item_name=line.item_name,
tenant_no=tenant_no,
user_id=user_id,
create_dept_id=create_dept_id,
seq=seq,
company_id=line.company_id,
company_name=line.company_name,
fee_amount=line.fee_amount,
order_source=order_source,
batch_id=batch.id,
)
assignee_svc.sync_assignees_for_task(
db,
task.id,
assignee_user_ids,
tenant_no=tenant_no,
operator_id=user_id,
create_dept_id=create_dept_id,
)
continue
item = third_item_svc.get_item(db, tid, tenant_no=tenant_no)
if not item or item.status != "1":
raise ValueError("第三方项目无效或已停用")
comp = get_company(db, item.company_id, tenant_no=tenant_no)
if not comp:
raise ValueError("第三方公司不存在")
fee = item.fee_amount or Decimal("0")
fee_total += fee
line = BizQcWorkOrderThirdItem(
id=generate_uuid(),
work_order_id=wo.id,
order_source=order_source,
batch_id=batch.id,
third_party_item_id=item.id,
company_id=item.company_id,
company_name=comp.company_name,
item_code=item.item_code,
item_name=item.item_name,
fee_amount=fee,
tenant_no=tenant_no,
order_num=seq,
create_by=user_id,
create_dept_id=create_dept_id or "",
create_time=now,
update_by=user_id,
update_time=now,
del_flag=0,
)
db.add(line)
db.flush()
task = task_svc.create_task_for_line(
db,
work_order=wo,
task_type="dsfjc",
source_line_id=line.id,
item_code=line.item_code,
item_name=line.item_name,
tenant_no=tenant_no,
user_id=user_id,
create_dept_id=create_dept_id,
seq=seq,
company_id=line.company_id,
company_name=line.company_name,
fee_amount=line.fee_amount,
order_source=order_source,
batch_id=batch.id,
)
assignee_svc.sync_assignees_for_task(
db,
task.id,
assignee_user_ids,
tenant_no=tenant_no,
operator_id=user_id,
create_dept_id=create_dept_id,
)
return seq, fee_total
def _batch_item_ids(db: Session, batch_id: str) -> Tuple[List[str], List[str]]:
lab_ids = [
r.lab_test_item_id
for r in db.query(BizQcWorkOrderLabItem)
.filter(BizQcWorkOrderLabItem.batch_id == batch_id, BizQcWorkOrderLabItem.del_flag == 0)
.order_by(BizQcWorkOrderLabItem.order_num.asc())
.all()
]
third_ids = [
r.third_party_item_id
for r in db.query(BizQcWorkOrderThirdItem)
.filter(
BizQcWorkOrderThirdItem.batch_id == batch_id,
BizQcWorkOrderThirdItem.del_flag == 0,
)
.order_by(BizQcWorkOrderThirdItem.order_num.asc())
.all()
]
return lab_ids, third_ids
def _batch_to_dict(
db: Session,
batch: BizQcWorkOrderInspectionBatch,
*,
tenant_no: str,
tasks: Optional[List[Dict[str, Any]]] = None,
) -> Dict[str, Any]:
assignee_ids = _batch_assignee_user_ids(db, batch.id, tenant_no)
lab_ids, third_ids = _batch_item_ids(db, batch.id)
task_list = tasks or []
completed = sum(1 for t in task_list if (t.get("taskStatus") or "") == "ywc")
return {
"id": batch.id,
"workOrderId": batch.work_order_id,
"batchNo": batch.batch_no,
"batchName": batch.batch_name,
"orderNum": batch.order_num or 0,
"assigneeUserIds": assignee_ids,
"assigneeUserNames": _assignee_names(db, assignee_ids),
"hasLab": bool(lab_ids),
"hasThirdParty": bool(third_ids),
"labItemIds": lab_ids,
"thirdPartyItemIds": third_ids,
"taskCount": len(task_list),
"completedTaskCount": completed,
"tasks": task_list,
}
def list_inspection_batches(
db: Session, work_order_id: str, tenant_no: str, *, user_info=None
) -> List[Dict[str, Any]]:
batches = (
db.query(BizQcWorkOrderInspectionBatch)
.filter(
BizQcWorkOrderInspectionBatch.work_order_id == work_order_id,
BizQcWorkOrderInspectionBatch.tenant_no == tenant_no,
BizQcWorkOrderInspectionBatch.del_flag == 0,
)
.order_by(BizQcWorkOrderInspectionBatch.order_num.asc())
.all()
)
all_tasks = task_svc.list_tasks_by_work_order(db, work_order_id, tenant_no)
tasks_by_batch: Dict[str, List[Dict[str, Any]]] = {}
orphan: List[Dict[str, Any]] = []
batch_ids = {b.id for b in batches}
for t in all_tasks:
bid = t.get("batchId") or ""
if bid and bid in batch_ids:
tasks_by_batch.setdefault(bid, []).append(t)
else:
orphan.append(t)
out = [_batch_to_dict(db, b, tenant_no=tenant_no, tasks=tasks_by_batch.get(b.id, [])) for b in batches]
if orphan and out:
out[0]["tasks"] = orphan + out[0]["tasks"]
out[0]["taskCount"] = len(out[0]["tasks"])
out[0]["completedTaskCount"] = sum(
1 for t in out[0]["tasks"] if (t.get("taskStatus") or "") == "ywc"
)
elif orphan:
out.append(
{
"id": "",
"workOrderId": work_order_id,
"batchNo": "—",
"batchName": "历史任务",
"orderNum": 0,
"assigneeUserIds": [],
"assigneeUserNames": "",
"taskCount": len(orphan),
"completedTaskCount": sum(1 for t in orphan if (t.get("taskStatus") or "") == "ywc"),
"tasks": orphan,
}
)
return out
def configure_work_order_batches(
db: Session,
wo: BizQcWorkOrder,
batches: List[Dict[str, Any]],
*,
tenant_no: str,
user_id: Optional[str],
create_dept_id: str = "",
) -> BizQcWorkOrder:
status = (wo.work_order_status or "").strip()
if status not in ("dpz", "ypz"):
raise ValueError("当前工单状态不可配置检测")
if not batches:
raise ValueError("请至少添加一个检测批次")
order_source = _order_source_for_wo(wo)
now = datetime.now()
is_first = status == "dpz"
for idx, b in enumerate(batches):
_validate_batch_payload(b, idx)
existing_batches = {
r.id: r
for r in db.query(BizQcWorkOrderInspectionBatch)
.filter(
BizQcWorkOrderInspectionBatch.work_order_id == wo.id,
BizQcWorkOrderInspectionBatch.tenant_no == tenant_no,
BizQcWorkOrderInspectionBatch.del_flag == 0,
)
.all()
}
keep_batch_ids = {((b.get("id") or "").strip()) for b in batches if (b.get("id") or "").strip()}
for bid, batch_row in existing_batches.items():
if bid not in keep_batch_ids:
_soft_delete_batch(db, batch_row, tenant_no=tenant_no, user_id=user_id)
fee_total = Decimal("0")
has_lab = False
has_third = False
global_seq = 0
for batch_idx, payload in enumerate(batches):
batch_id = (payload.get("id") or "").strip()
lab_ids = _unique_ids(payload.get("labItemIds") or payload.get("lab_item_ids") or [])
third_ids = _unique_ids(payload.get("thirdPartyItemIds") or payload.get("third_party_item_ids") or [])
has_lab_batch = bool(lab_ids) or bool(payload.get("hasLab") or payload.get("has_lab"))
has_third_batch = bool(third_ids) or bool(payload.get("hasThirdParty") or payload.get("has_third_party"))
assignee_ids = payload.get("assigneeUserIds") or payload.get("assignee_user_ids") or []
normalized_assignees = assignee_svc.validate_assignee_user_ids(db, assignee_ids, tenant_no)
batch_name = (payload.get("batchName") or payload.get("batch_name") or "").strip()
if batch_id and batch_id in existing_batches:
batch = existing_batches[batch_id]
batch.order_num = batch_idx + 1
if batch_name:
batch.batch_name = batch_name
batch.update_by = user_id
batch.update_time = now
db.add(batch)
else:
batch = BizQcWorkOrderInspectionBatch(
id=generate_uuid(),
work_order_id=wo.id,
order_source=order_source,
batch_no=_next_batch_no(db, wo, tenant_no),
batch_name=batch_name or _allocate_batch_name(db, wo.id, tenant_no, now),
tenant_no=tenant_no,
order_num=batch_idx + 1,
create_by=user_id,
create_dept_id=create_dept_id or "",
create_time=now,
update_by=user_id,
update_time=now,
del_flag=0,
)
db.add(batch)
db.flush()
sync_batch_assignees(
db,
batch.id,
normalized_assignees,
tenant_no=tenant_no,
operator_id=user_id,
create_dept_id=create_dept_id,
)
if has_lab_batch:
has_lab = True
global_seq = _sync_batch_lab_items(
db,
wo,
batch,
order_source=order_source,
lab_item_ids=lab_ids,
assignee_user_ids=normalized_assignees,
tenant_no=tenant_no,
user_id=user_id,
create_dept_id=create_dept_id,
seq_start=global_seq,
)
else:
global_seq = _sync_batch_lab_items(
db,
wo,
batch,
order_source=order_source,
lab_item_ids=[],
assignee_user_ids=normalized_assignees,
tenant_no=tenant_no,
user_id=user_id,
create_dept_id=create_dept_id,
seq_start=global_seq,
)
if has_third_batch:
has_third = True
global_seq, batch_fee = _sync_batch_third_items(
db,
wo,
batch,
order_source=order_source,
third_party_item_ids=third_ids,
assignee_user_ids=normalized_assignees,
tenant_no=tenant_no,
user_id=user_id,
create_dept_id=create_dept_id,
seq_start=global_seq,
)
fee_total += batch_fee
else:
global_seq, _ = _sync_batch_third_items(
db,
wo,
batch,
order_source=order_source,
third_party_item_ids=[],
assignee_user_ids=normalized_assignees,
tenant_no=tenant_no,
user_id=user_id,
create_dept_id=create_dept_id,
seq_start=global_seq,
)
wo.has_lab = "1" if has_lab else "0"
wo.has_third_party = "1" if has_third else "0"
wo.work_order_status = "ypz"
if is_first:
wo.configured_time = now
wo.configured_by = user_id
wo.third_party_fee_total = fee_total if has_third else None
wo.update_by = user_id
wo.update_time = now
db.add(wo)
from app.crud.pilot_outbound_status_sync import sync_schedule_from_qc_work_order
sync_schedule_from_qc_work_order(db, wo, user_id=user_id)
db.commit()
db.refresh(wo)
return wo
def normalize_legacy_configure_to_batches(body: Dict[str, Any]) -> List[Dict[str, Any]]:
"""将扁平 labItemIds 配置转为单批次结构。"""
batches = body.get("batches")
if batches:
return batches
has_lab = bool(body.get("hasLab") or body.get("has_lab"))
has_third = bool(body.get("hasThirdParty") or body.get("has_third_party"))
lab_ids = body.get("labItemIds") or body.get("lab_item_ids") or []
third_ids = body.get("thirdPartyItemIds") or body.get("third_party_item_ids") or []
lab_assignees = body.get("labItemAssignees") or body.get("lab_item_assignees") or {}
third_assignees = body.get("thirdPartyItemAssignees") or body.get("third_party_item_assignees") or {}
assignee_set: Set[str] = set()
for lid in lab_ids:
for uid in lab_assignees.get(lid) or []:
if uid:
assignee_set.add(uid)
for tid in third_ids:
for uid in third_assignees.get(tid) or []:
if uid:
assignee_set.add(uid)
return [
{
"assigneeUserIds": list(assignee_set),
"hasLab": has_lab,
"hasThirdParty": has_third,
"labItemIds": lab_ids,
"thirdPartyItemIds": third_ids,
}
]
+45 -1
View File
@@ -1,5 +1,6 @@
from __future__ import annotations
import json
from datetime import datetime
from typing import Any, Dict, List, Optional
@@ -16,11 +17,41 @@ def _row_to_dict(r: BizQcItemParam) -> Dict[str, Any]:
"paramCode": r.param_code,
"paramUnit": r.param_unit or "",
"paramType": r.param_type or "text",
"options": r.options or "",
"defaultValue": r.default_value or "",
"requiredFlag": r.required_flag or "0",
"orderNum": r.order_num if r.order_num is not None else 0,
}
def _parse_options_list(options_raw: Any) -> List[str]:
if not options_raw:
return []
if isinstance(options_raw, list):
arr = options_raw
else:
try:
arr = json.loads(str(options_raw))
except json.JSONDecodeError:
raise ValueError("单选选项格式无效")
if not isinstance(arr, list):
raise ValueError("单选选项格式无效")
cleaned = [str(x).strip() for x in arr if str(x).strip()]
return cleaned
def _validate_param(p: Dict[str, Any], index: int) -> None:
label = (p.get("paramName") or "").strip() or f"第{index + 1}个参数"
param_type = (p.get("paramType") or "text").strip()
if param_type != "select":
return
opts = _parse_options_list(p.get("options"))
if not opts:
raise ValueError(f"「{label}」为单选类型,请至少配置一个选项")
if len(opts) != len(set(opts)):
raise ValueError(f"「{label}」的选项不能重复")
def list_params_by_item(
db: Session, item_source: str, item_id: str, tenant_no: str, *, user_info=None
) -> List[Dict[str, Any]]:
@@ -45,6 +76,9 @@ def save_params_for_item(
tenant_no: str,
user_id: Optional[str] = None,
) -> List[Dict[str, Any]]:
for idx, p in enumerate(params):
_validate_param(p, idx)
now = datetime.now()
db.query(BizQcItemParam).filter(
BizQcItemParam.item_source == item_source,
@@ -58,6 +92,14 @@ def save_params_for_item(
db.flush()
for idx, p in enumerate(params):
param_type = (p.get("paramType") or "text").strip()
options_val = None
default_val = None
if param_type == "select":
opts = _parse_options_list(p.get("options"))
options_val = json.dumps(opts, ensure_ascii=False)
default_val = opts[0] if opts else None
obj = BizQcItemParam(
id=generate_uuid(),
item_source=item_source,
@@ -65,7 +107,9 @@ def save_params_for_item(
param_name=p.get("paramName", ""),
param_code=p.get("paramCode", ""),
param_unit=p.get("paramUnit") or None,
param_type=p.get("paramType", "text"),
param_type=param_type,
options=options_val,
default_value=default_val,
required_flag=p.get("requiredFlag", "0"),
tenant_no=tenant_no,
order_num=p.get("orderNum", idx),
+3
View File
@@ -71,6 +71,7 @@ def _task_to_dict(
return {
"id": t.id,
"workOrderId": t.work_order_id,
"batchId": getattr(t, "batch_id", None) or "",
"orderSource": getattr(t, "order_source", "inbound") or "inbound",
"workOrderNo": work_order_no,
"taskNo": t.task_no,
@@ -266,10 +267,12 @@ def create_task_for_line(
company_name: Optional[str] = None,
fee_amount: Optional[Decimal] = None,
order_source: str = "inbound",
batch_id: Optional[str] = None,
) -> BizQcTask:
obj = BizQcTask(
id=generate_uuid(),
work_order_id=work_order.id,
batch_id=batch_id,
order_source=order_source,
task_no=_gen_task_no(db, work_order, seq),
task_type=task_type,
+9 -2
View File
@@ -76,14 +76,21 @@ def _merge_saved_into_template(
pid = p.get("id") or ""
code = p.get("paramCode") or ""
hit = by_id.get(pid) or (by_code.get(code) if code else None)
saved_value = (hit.get("paramValue") if hit else "") or ""
default_value = (p.get("defaultValue") or "").strip()
param_type = p.get("paramType", "text")
if not saved_value and param_type == "select" and default_value:
saved_value = default_value
out.append(
{
"paramId": pid,
"paramName": p.get("paramName", ""),
"paramCode": code,
"paramUnit": p.get("paramUnit", ""),
"paramType": p.get("paramType", "text"),
"paramValue": (hit.get("paramValue") if hit else "") or "",
"paramType": param_type,
"options": p.get("options", ""),
"defaultValue": default_value,
"paramValue": saved_value,
"requiredFlag": p.get("requiredFlag", "0"),
}
)
+202 -96
View File
@@ -9,7 +9,7 @@ from typing import Any, Dict, List, Optional, Tuple
from sqlalchemy import and_, func, or_
from sqlalchemy.orm import Session
from app.core.db_collation import eq_unicode, like_unicode
from app.core.db_collation import eq_unicode, like_unicode, unicode_ci
from app.core.utils import generate_uuid
from app.crud import qc_lab_test_item as lab_item_svc
from app.crud import qc_task as task_svc
@@ -39,6 +39,7 @@ from app.models.models import (
BizOutputType,
BizQcLabTestItem,
BizQcTask,
BizQcTaskAssignee,
BizQcThirdPartyItem,
BizQcWorkOrder,
BizQcWorkOrderLabItem,
@@ -1231,6 +1232,13 @@ def list_work_orders(
wt = (work_order_type or "").strip()
if wt:
q0 = q0.filter(BizQcWorkOrder.work_order_type == wt)
else:
q0 = q0.filter(
~and_(
BizQcWorkOrder.work_order_type == "outbound",
BizQcWorkOrder.biz_type == "clinical_application",
)
)
bt = (biz_type or "").strip()
if bt:
q0 = q0.filter(BizQcWorkOrder.biz_type == bt)
@@ -1239,8 +1247,23 @@ def list_work_orders(
q0 = q0.filter(BizQcWorkOrder.reception_source == rs)
ts = (trigger_source or "").strip()
if ts:
if ts == "manual" and wt == "temporary":
if ts == "temp_manual":
q0 = q0.filter(
BizQcWorkOrder.work_order_type == "temporary",
BizQcWorkOrder.trigger_source != "production",
)
elif ts == "temp_production":
q0 = q0.filter(
BizQcWorkOrder.work_order_type == "temporary",
BizQcWorkOrder.trigger_source == "production",
)
elif ts == "manual" and wt == "temporary":
q0 = q0.filter(BizQcWorkOrder.trigger_source != "production")
elif ts == "production" and not wt:
q0 = q0.filter(
BizQcWorkOrder.trigger_source == "production",
BizQcWorkOrder.work_order_type != "temporary",
)
else:
q0 = q0.filter(BizQcWorkOrder.trigger_source == ts)
kw = (keyword or "").strip()
@@ -1249,6 +1272,7 @@ def list_work_orders(
keyword_conditions = [
BizQcWorkOrder.work_order_no.like(like),
BizQcWorkOrder.sample_code.like(like),
BizQcWorkOrder.protocol_no.like(like),
]
if tenant_no:
from app.crud import sample_reception as reception_svc
@@ -1263,7 +1287,7 @@ def list_work_orders(
)
if lab_codes:
keyword_conditions.append(BizQcWorkOrder.sample_code.in_(lab_codes))
if tenant_no and wt == "outbound":
if tenant_no and (wt == "outbound" or not wt):
outbound_ids = _outbound_qc_work_order_ids_by_keyword(db, tenant_no, kw)
if outbound_ids:
keyword_conditions.append(BizQcWorkOrder.id.in_(outbound_ids))
@@ -1327,6 +1351,140 @@ def list_work_orders(
return items, int(total)
_QC_TAB_COUNT_KEYS = (
"all",
"inbound_cell_storage",
"inbound_research_donation",
"outbound_pilot_service",
"outbound_basic_experiment",
"temporary",
)
def _empty_qc_tab_counts() -> Dict[str, int]:
return {k: 0 for k in _QC_TAB_COUNT_KEYS}
def _resolve_wo_tab_key(
wo_type: Optional[str],
reception_source: Optional[str],
biz_type: Optional[str],
) -> str:
wt = (wo_type or "inbound").strip() or "inbound"
if wt == "temporary":
return "temporary"
if wt == "outbound":
bt = (biz_type or "pilot_service").strip() or "pilot_service"
if bt == "basic_experiment":
return "outbound_basic_experiment"
if bt == "clinical_application":
return "outbound_clinical_application"
return "outbound_pilot_service"
rs = (reception_source or "cell_storage").strip() or "cell_storage"
return "inbound_research_donation" if rs == "research_donation" else "inbound_cell_storage"
def _unfinished_work_order_filter(q):
return q.filter(
BizQcWorkOrder.work_order_status.in_(("dpz", "ypz")),
~and_(
BizQcWorkOrder.work_order_type == "outbound",
BizQcWorkOrder.biz_type == "clinical_application",
),
)
def _aggregate_qc_tab_counts(rows) -> Dict[str, int]:
counts = _empty_qc_tab_counts()
for wo_type, rs, bt, cnt in rows:
tab_key = _resolve_wo_tab_key(wo_type, rs, bt)
if tab_key == "outbound_clinical_application":
continue
n = int(cnt or 0)
counts[tab_key] = counts.get(tab_key, 0) + n
counts["all"] += n
return counts
def count_unfinished_work_orders_by_tab(
db: Session,
*,
tenant_no: str,
user_info=None,
) -> Dict[str, int]:
"""按 Tab 统计未完成质检工单数(待配置 + 已配置)。"""
q = db.query(BizQcWorkOrder).filter(
BizQcWorkOrder.del_flag == 0,
BizQcWorkOrder.tenant_no == tenant_no,
)
q = _unfinished_work_order_filter(q)
if user_info:
from app.core.data_scope import apply_data_scope
q = apply_data_scope(q, BizQcWorkOrder, user_info, db, tenant_no)
rows = (
q.with_entities(
BizQcWorkOrder.work_order_type,
BizQcWorkOrder.reception_source,
BizQcWorkOrder.biz_type,
func.count(BizQcWorkOrder.id),
)
.group_by(
BizQcWorkOrder.work_order_type,
BizQcWorkOrder.reception_source,
BizQcWorkOrder.biz_type,
)
.all()
)
return _aggregate_qc_tab_counts(rows)
def count_mine_unfinished_work_orders_by_tab(
db: Session,
*,
tenant_no: str,
assignee_user_id: str,
) -> Dict[str, int]:
"""我的质检任务:按 Tab 统计当前用户负责且未完成的质检工单数。"""
q = (
db.query(
BizQcWorkOrder.work_order_type,
BizQcWorkOrder.reception_source,
BizQcWorkOrder.biz_type,
func.count(func.distinct(BizQcWorkOrder.id)),
)
.join(
BizQcTask,
and_(
BizQcTask.work_order_id == BizQcWorkOrder.id,
BizQcTask.del_flag == 0,
),
)
.join(
BizQcTaskAssignee,
and_(
eq_unicode(BizQcTaskAssignee.task_id, BizQcTask.id),
unicode_ci(BizQcTaskAssignee.user_id) == assignee_user_id,
BizQcTaskAssignee.del_flag == 0,
),
)
.filter(
BizQcWorkOrder.del_flag == 0,
BizQcWorkOrder.tenant_no == tenant_no,
)
)
q = _unfinished_work_order_filter(q)
rows = (
q.group_by(
BizQcWorkOrder.work_order_type,
BizQcWorkOrder.reception_source,
BizQcWorkOrder.biz_type,
)
.all()
)
return _aggregate_qc_tab_counts(rows)
def get_work_order(
db: Session, row_id: str, tenant_no: Optional[str] = None
) -> Optional[BizQcWorkOrder]:
@@ -1382,6 +1540,8 @@ def _third_lines(db: Session, work_order_id: str) -> List[Dict[str, Any]]:
def _qc_info_item(db: Session, wo: BizQcWorkOrder, tenant_no: str) -> Dict[str, Any]:
from app.crud import qc_inspection_batch as batch_svc
tasks = task_svc.list_tasks_by_work_order(db, wo.id, tenant_no)
for t in tasks:
tt = (t.get("taskType") or "").strip()
@@ -1398,6 +1558,7 @@ def _qc_info_item(db: Session, wo: BizQcWorkOrder, tenant_no: str) -> Dict[str,
"qcBizTypeLabel": _qc_biz_type_label(biz),
"labItems": _lab_lines(db, wo.id),
"thirdPartyItems": _third_lines(db, wo.id),
"inspectionBatches": batch_svc.list_inspection_batches(db, wo.id, tenant_no),
"tasks": tasks,
}
@@ -1712,6 +1873,9 @@ def get_work_order_detail(
out["labItems"] = _lab_lines(db, wo.id)
out["thirdPartyItems"] = _third_lines(db, wo.id)
out["tasks"] = task_svc.list_tasks_by_work_order(db, wo.id, tenant_no)
from app.crud import qc_inspection_batch as batch_svc
out["inspectionBatches"] = batch_svc.list_inspection_batches(db, wo.id, tenant_no)
enriched_fee = enrich_work_order_fee_summaries(db, [out], tenant_no)
if enriched_fee:
out["paymentSummary"] = enriched_fee[0].get("paymentSummary")
@@ -2069,69 +2233,38 @@ def configure_work_order(
third_party_item_ids: List[str],
lab_item_assignees: Optional[Dict[str, List[str]]] = None,
third_party_item_assignees: Optional[Dict[str, List[str]]] = None,
batches: Optional[List[Dict[str, Any]]] = None,
tenant_no: str,
user_id: Optional[str] = None,
create_dept_id: str = "",
) -> BizQcWorkOrder:
from app.crud import qc_inspection_batch as batch_svc
wo = get_work_order(db, work_order_id, tenant_no=tenant_no)
if not wo:
raise ValueError("工单不存在")
wo_type = getattr(wo, "work_order_type", None) or "inbound"
if wo_type == "temporary":
order_source = "temporary"
elif wo_type == "outbound":
order_source = "outbound"
if batches is not None:
payload_batches = batches
else:
order_source = "inbound"
if not has_lab and not has_third_party:
raise ValueError("请至少选择一种检测方式")
if has_lab and not lab_item_ids:
raise ValueError("请选择实验室自检项目")
if has_third_party and not third_party_item_ids:
raise ValueError("请选择第三方检测项目")
if not has_lab:
lab_item_ids = []
if not has_third_party:
third_party_item_ids = []
seq = _sync_work_order_lab_items(
payload_batches = batch_svc.normalize_legacy_configure_to_batches(
{
"hasLab": has_lab,
"hasThirdParty": has_third_party,
"labItemIds": lab_item_ids,
"thirdPartyItemIds": third_party_item_ids,
"labItemAssignees": lab_item_assignees,
"thirdPartyItemAssignees": third_party_item_assignees,
}
)
return batch_svc.configure_work_order_batches(
db,
wo,
order_source=order_source,
lab_item_ids=lab_item_ids,
lab_item_assignees=lab_item_assignees,
payload_batches,
tenant_no=tenant_no,
user_id=user_id,
create_dept_id=create_dept_id,
)
seq, fee_total = _sync_work_order_third_party_items(
db,
wo,
order_source=order_source,
third_party_item_ids=third_party_item_ids,
third_party_item_assignees=third_party_item_assignees,
tenant_no=tenant_no,
user_id=user_id,
create_dept_id=create_dept_id,
seq_start=seq,
)
wo.has_lab = "1" if has_lab else "0"
wo.has_third_party = "1" if has_third_party else "0"
wo.work_order_status = "ypz"
wo.configured_time = datetime.now()
wo.configured_by = user_id
wo.third_party_fee_total = fee_total if has_third_party else None
wo.update_by = user_id
wo.update_time = datetime.now()
db.add(wo)
from app.crud.pilot_outbound_status_sync import sync_schedule_from_qc_work_order
sync_schedule_from_qc_work_order(db, wo, user_id=user_id)
db.commit()
db.refresh(wo)
return wo
def try_complete_work_order(db: Session, work_order_id: str, tenant_no: str) -> None:
@@ -2412,6 +2545,7 @@ def configure_temp_qc_work_order(
has_third_party: bool,
lab_item_ids: List[str],
third_party_item_ids: List[str],
batches: Optional[List[Dict[str, Any]]] = None,
tenant_no: str,
user_id: Optional[str] = None,
create_dept_id: str = "",
@@ -2419,50 +2553,27 @@ def configure_temp_qc_work_order(
wo = get_temp_qc_work_order(db, work_order_id, tenant_no)
if not wo:
raise ValueError("工单不存在")
from app.crud import qc_inspection_batch as batch_svc
if not has_lab and not has_third_party:
raise ValueError("请至少选择一种检测方式")
if has_lab and not lab_item_ids:
raise ValueError("请选择实验室自检项目")
if has_third_party and not third_party_item_ids:
raise ValueError("请选择第三方检测项目")
if not has_lab:
lab_item_ids = []
if not has_third_party:
third_party_item_ids = []
seq = _sync_work_order_lab_items(
if batches is not None:
payload_batches = batches
else:
payload_batches = batch_svc.normalize_legacy_configure_to_batches(
{
"hasLab": has_lab,
"hasThirdParty": has_third_party,
"labItemIds": lab_item_ids,
"thirdPartyItemIds": third_party_item_ids,
}
)
return batch_svc.configure_work_order_batches(
db,
wo,
order_source="temporary",
lab_item_ids=lab_item_ids,
payload_batches,
tenant_no=tenant_no,
user_id=user_id,
create_dept_id=create_dept_id,
)
seq, fee_total = _sync_work_order_third_party_items(
db,
wo,
order_source="temporary",
third_party_item_ids=third_party_item_ids,
tenant_no=tenant_no,
user_id=user_id,
create_dept_id=create_dept_id,
seq_start=seq,
)
wo.has_lab = "1" if has_lab else "0"
wo.has_third_party = "1" if has_third_party else "0"
wo.work_order_status = "ypz"
wo.configured_time = datetime.now()
wo.configured_by = user_id
wo.third_party_fee_total = fee_total if has_third_party else None
wo.update_by = user_id
wo.update_time = datetime.now()
db.add(wo)
db.commit()
db.refresh(wo)
return wo
def try_complete_temp_qc_work_order(
@@ -2713,15 +2824,10 @@ def get_outbound_qc_detail(
"tasks": [],
}
if not schedule_id:
out["tasks"] = task_svc.list_tasks_by_work_order(db, wo.id, tenant_no)
enriched_fee = enrich_work_order_fee_summaries(db, [out["workOrder"]], tenant_no)
if enriched_fee:
out["workOrder"]["feeSummary"] = enriched_fee[0].get("feeSummary")
out["feeSummary"] = enriched_fee[0].get("feeSummary")
return out
from app.crud import qc_inspection_batch as batch_svc
out["tasks"] = task_svc.list_tasks_by_work_order(db, wo.id, tenant_no)
out["inspectionBatches"] = batch_svc.list_inspection_batches(db, wo.id, tenant_no)
enriched_fee = enrich_work_order_fee_summaries(db, [out["workOrder"]], tenant_no)
if enriched_fee:
out["workOrder"]["feeSummary"] = enriched_fee[0].get("feeSummary")
+16 -14
View File
@@ -955,13 +955,14 @@ def create_sample_reception(
_, biz_date, seq = lab_code_svc.validate_and_parse_lab_storage_code(
resolved_lab_code, reception_time
)
lab_code_svc.sync_lab_storage_seq_after_use(
db,
tenant_no=tenant_no,
biz_date=biz_date,
seq=seq,
user_id=user_id,
)
if biz_date is not None and seq is not None:
lab_code_svc.sync_lab_storage_seq_after_use(
db,
tenant_no=tenant_no,
biz_date=biz_date,
seq=seq,
user_id=user_id,
)
db.commit()
db.refresh(obj)
return obj
@@ -1024,13 +1025,14 @@ def update_sample_reception(
exclude_reception_id=reception_id,
)
rec.lab_storage_code = parsed_code
lab_code_svc.sync_lab_storage_seq_after_use(
db,
tenant_no=tenant_no,
biz_date=biz_date,
seq=seq,
user_id=user_id,
)
if biz_date is not None and seq is not None:
lab_code_svc.sync_lab_storage_seq_after_use(
db,
tenant_no=tenant_no,
biz_date=biz_date,
seq=seq,
user_id=user_id,
)
handler_display = (handler_name or "").strip()
if handler_user_id and not handler_display:
+10 -6
View File
@@ -167,18 +167,22 @@ def allocate_lab_storage_codes(
def validate_and_parse_lab_storage_code(
code: str,
reception_time: datetime,
) -> Tuple[str, str, int]:
reception_time: Optional[datetime] = None,
) -> Tuple[str, Optional[str], Optional[int]]:
"""校验入库编码非空;若为 RK+日期+序号 格式则解析 biz_date/seq 供序号同步,否则仅返回编码本身。"""
c = (code or "").strip()
if not c:
raise ValueError("实验室入库编码不能为空")
if len(c) > 32:
raise ValueError("实验室入库编码长度不能超过 32 个字符")
m = LAB_STORAGE_CODE_RE.match(c)
if not m:
raise ValueError("实验室入库编码格式无效")
return c, None, None
biz_date, seq_str = m.group(1), m.group(2)
expected_date = reception_time_date_str(reception_time)
if biz_date != expected_date:
raise ValueError("实验室入库编码日期须与样本接收时间日期一致")
if reception_time is not None:
expected_date = reception_time_date_str(reception_time)
if biz_date != expected_date:
raise ValueError("实验室入库编码日期须与样本接收时间日期一致")
return c, biz_date, int(seq_str)
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_api_production_material_py", "label": "production_material.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production_material.py", "source_location": "L1"}, {"id": "api_production_material_productionmaterialbody", "label": "ProductionMaterialBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production_material.py", "source_location": "L28"}, {"id": "basemodel", "label": "BaseModel", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "api_production_material_productionmaterialupdatebody", "label": "ProductionMaterialUpdateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production_material.py", "source_location": "L40"}, {"id": "api_production_material_stockinbody", "label": "StockInBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production_material.py", "source_location": "L52"}, {"id": "api_production_material_batchstockinitem", "label": "BatchStockInItem", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production_material.py", "source_location": "L69"}, {"id": "api_production_material_batchstockinbody", "label": "BatchStockInBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production_material.py", "source_location": "L86"}, {"id": "api_production_material_batchstockoutitem", "label": "BatchStockOutItem", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production_material.py", "source_location": "L93"}, {"id": "api_production_material_batchstockoutbody", "label": "BatchStockOutBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production_material.py", "source_location": "L100"}, {"id": "api_production_material_batchdiscarditem", "label": "BatchDiscardItem", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production_material.py", "source_location": "L109"}, {"id": "api_production_material_batchdiscardbody", "label": "BatchDiscardBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production_material.py", "source_location": "L117"}, {"id": "api_production_material_productionmaterialbatchupdatebody", "label": "ProductionMaterialBatchUpdateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production_material.py", "source_location": "L125"}, {"id": "api_production_material_list_production_material_batches", "label": "list_production_material_batches()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production_material.py", "source_location": "L147"}, {"id": "api_production_material_production_material_batch_options", "label": "production_material_batch_options()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production_material.py", "source_location": "L178"}, {"id": "api_production_material_update_production_material_batch", "label": "update_production_material_batch()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production_material.py", "source_location": "L203"}, {"id": "api_production_material_delete_production_material_batch", "label": "delete_production_material_batch()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production_material.py", "source_location": "L232"}, {"id": "api_production_material_list_production_materials", "label": "list_production_materials()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production_material.py", "source_location": "L249"}, {"id": "api_production_material_production_material_options", "label": "production_material_options()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production_material.py", "source_location": "L278"}, {"id": "api_production_material_create_production_material", "label": "create_production_material()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production_material.py", "source_location": "L293"}, {"id": "api_production_material_update_production_material", "label": "update_production_material()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production_material.py", "source_location": "L324"}, {"id": "api_production_material_delete_production_material", "label": "delete_production_material()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/production_material.py", "source_location": "L353"}, {"id": "api_production_material_batch_stock_in_production_material", "label": "batch_stock_in_production_material()", "file_type": "code", "source_file": "/Users/Line 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_crud_sample_reception_lab_code_py", "label": "sample_reception_lab_code.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception_lab_code.py", "source_location": "L1"}, {"id": "crud_sample_reception_lab_code_parse_reception_date", "label": "parse_reception_date()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception_lab_code.py", "source_location": "L18"}, {"id": "crud_sample_reception_lab_code_reception_time_date_str", "label": "reception_time_date_str()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception_lab_code.py", "source_location": "L28"}, {"id": "crud_sample_reception_lab_code_format_lab_storage_code", "label": "format_lab_storage_code()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception_lab_code.py", "source_location": "L32"}, {"id": "crud_sample_reception_lab_code_max_existing_seq", "label": "_max_existing_seq()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception_lab_code.py", "source_location": "L36"}, {"id": "crud_sample_reception_lab_code_sync_sequence_from_existing", "label": "_sync_sequence_from_existing()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception_lab_code.py", "source_location": "L60"}, {"id": "crud_sample_reception_lab_code_allocate_one_seq", "label": "_allocate_one_seq()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception_lab_code.py", "source_location": "L101"}, {"id": "crud_sample_reception_lab_code_allocate_lab_storage_codes", "label": "allocate_lab_storage_codes()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception_lab_code.py", "source_location": "L145"}, {"id": "crud_sample_reception_lab_code_validate_and_parse_lab_storage_code", "label": "validate_and_parse_lab_storage_code()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception_lab_code.py", "source_location": "L168"}, {"id": "crud_sample_reception_lab_code_assert_lab_storage_code_available", "label": "assert_lab_storage_code_available()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception_lab_code.py", "source_location": "L189"}, {"id": "crud_sample_reception_lab_code_sync_lab_storage_seq_after_use", "label": "sync_lab_storage_seq_after_use()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception_lab_code.py", "source_location": "L207"}, {"id": "crud_sample_reception_lab_code_rationale_1", "label": "\u5b9e\u9a8c\u5ba4\u5165\u5e93\u7f16\u7801\uff1aRK + \u63a5\u6536\u65e5\u671f YYYYMMDD + 3 \u4f4d\u5e8f\u53f7", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception_lab_code.py", "source_location": "L1"}, {"id": "crud_sample_reception_lab_code_rationale_172", "label": "\u6821\u9a8c\u5165\u5e93\u7f16\u7801\u975e\u7a7a\uff1b\u82e5\u4e3a RK+\u65e5\u671f+\u5e8f\u53f7 \u683c\u5f0f\u5219\u89e3\u6790 biz_date/seq \u4f9b\u5e8f\u53f7\u540c\u6b65\uff0c\u5426\u5219\u4ec5\u8fd4\u56de\u7f16\u7801\u672c\u8eab\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception_lab_code.py", "source_location": "L172"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_sample_reception_lab_code_py", "target": "re", "relation": "imports", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception_lab_code.py", "source_location": "L2", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_sample_reception_lab_code_py", "target": "datetime", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception_lab_code.py", "source_location": "L3", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_sample_reception_lab_code_py", "target": "typing", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception_lab_code.py", "source_location": "L4", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_sample_reception_lab_code_py", "target": "sqlalchemy", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception_lab_code.py", "source_location": "L6", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_sample_reception_lab_code_py", "target": "sqlalchemy_orm", "relation": "imports_from", "confidence": "EXTRACTED", "sourceLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_crud_production_material_stock_batch_py", "label": "production_material_stock_batch.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_material_stock_batch.py", "source_location": "L1"}, {"id": "crud_production_material_stock_batch_fmt_date", "label": "_fmt_date()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_material_stock_batch.py", "source_location": "L16"}, {"id": "crud_production_material_stock_batch_parse_date", "label": "_parse_date()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_material_stock_batch.py", "source_location": "L20"}, {"id": "crud_production_material_stock_batch_parse_expire_date", "label": "_parse_expire_date()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_material_stock_batch.py", "source_location": "L34"}, {"id": "crud_production_material_stock_batch_parse_optional_date", "label": "_parse_optional_date()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_material_stock_batch.py", "source_location": "L38"}, {"id": "crud_production_material_stock_batch_require_str", "label": "_require_str()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_material_stock_batch.py", "source_location": "L47"}, {"id": "crud_production_material_stock_batch_optional_str", "label": "_optional_str()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_material_stock_batch.py", "source_location": "L54"}, {"id": "crud_production_material_stock_batch_require_decimal", "label": "_require_decimal()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_material_stock_batch.py", "source_location": "L61"}, {"id": "crud_production_material_stock_batch_stock_in_no_exists", "label": "stock_in_no_exists()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_material_stock_batch.py", "source_location": "L73"}, {"id": "crud_production_material_stock_batch_batch_row_to_dict", "label": "_batch_row_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_material_stock_batch.py", "source_location": "L89"}, {"id": "crud_production_material_stock_batch_usage_batch_snapshots", "label": "_usage_batch_snapshots()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_material_stock_batch.py", "source_location": "L126"}, {"id": "crud_production_material_stock_batch_get_material_for_update", "label": "_get_material_for_update()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_material_stock_batch.py", "source_location": "L148"}, {"id": "crud_production_material_stock_batch_list_batches", "label": "list_batches()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_material_stock_batch.py", "source_location": "L166"}, {"id": "crud_production_material_stock_batch_batch_options", "label": "batch_options()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_material_stock_batch.py", "source_location": "L234"}, {"id": "crud_production_material_stock_batch_create_batch_and_usage", "label": "_create_batch_and_usage()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_material_stock_batch.py", "source_location": "L314"}, {"id": "crud_production_material_stock_batch_stock_in_one", "label": "stock_in_one()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_material_stock_batch.py", "source_location": "L418"}, {"id": "crud_production_material_stock_batch_batch_stock_in", "label": "batch_stock_in()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_material_stock_batch.py", "source_location": "L463"}, {"id": "crud_production_material_stock_batch_stock_out_batch", "label": "_stock_out_batch()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_material_stock_batch.py", "source_location": "L524"}, {"id": "crud_production_material_stock_batch_batch_stock_out", "label": "batch_stock_out()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_material_stock_batch.py", "source_location": "L606"}, {"id": "crud_production_material_stock_batch_validate_discard_reason", "label": "_validate_discard_reason()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_material_stock_batch.py", "source_location": "L659"}, {"id": "crud_pLine 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": "L98"}, {"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": "L135"}, {"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": "L243"}, {"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": "L254"}, {"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": "L301"}, {"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": "L382"}, {"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": "L473"}, {"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": "L480"}, {"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": "L498"}, {"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": "L507"}, {"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": "L540"}, {"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": "L557"}, {"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": "L561"}, {"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": "L605"}, {"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": "L657"}, {"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": "L687"}, {"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": "L736"}, {"id": "crud_qc_task_rationale_616", "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": "L616"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_taLine 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_qc_py", "label": "qc.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L1"}, {"id": "api_qc_thirdpartycompanybody", "label": "ThirdPartyCompanyBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L48"}, {"id": "basemodel", "label": "BaseModel", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "api_qc_thirdpartycompanyupdatebody", "label": "ThirdPartyCompanyUpdateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L66"}, {"id": "api_qc_list_qc_third_party_companies", "label": "list_qc_third_party_companies()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L85"}, {"id": "api_qc_list_qc_third_party_company_options", "label": "list_qc_third_party_company_options()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L107"}, {"id": "api_qc_create_qc_third_party_company", "label": "create_qc_third_party_company()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L121"}, {"id": "api_qc_update_qc_third_party_company", "label": "update_qc_third_party_company()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L156"}, {"id": "api_qc_delete_qc_third_party_company", "label": "delete_qc_third_party_company()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L193"}, {"id": "api_qc_thirdpartyitembody", "label": "ThirdPartyItemBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L213"}, {"id": "api_qc_thirdpartyitemupdatebody", "label": "ThirdPartyItemUpdateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L225"}, {"id": "api_qc_list_qc_third_party_items", "label": "list_qc_third_party_items()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L238"}, {"id": "api_qc_list_qc_third_party_items_active", "label": "list_qc_third_party_items_active()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L267"}, {"id": "api_qc_create_qc_third_party_item", "label": "create_qc_third_party_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L282"}, {"id": "api_qc_update_qc_third_party_item", "label": "update_qc_third_party_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L311"}, {"id": "api_qc_delete_qc_third_party_item", "label": "delete_qc_third_party_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L342"}, {"id": "api_qc_labtestitembody", "label": "LabTestItemBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L362"}, {"id": "api_qc_labtestitemupdatebody", "label": "LabTestItemUpdateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L373"}, {"id": "api_qc_itemparambody", "label": "ItemParamBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L387"}, {"id": "api_qc_taskresultbody", "label": "TaskResultBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L392"}, {"id": "api_qc_taskcompletewithresultbody", "label": "TaskCompleteWithResultBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L397"}, {"id": "api_qc_list_qc_lab_test_items", "label": "list_qc_lab_test_items()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L407"}, {"id": "api_qc_list_qc_lab_test_items_active", "label": "list_qc_lab_test_items_active()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L429"}, {"id": "api_qc_create_qc_lab_test_item", "label": "create_qc_lab_test_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L443"}, {"id": "api_qc_update_qc_lab_test_item", "label": "update_qc_lab_test_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L471"}, {"id": "api_Line 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_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": "L90"}, {"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": "L131"}, {"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": "L137"}, {"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": "L141"}, {"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": "L148"}, {"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": "L153"}, {"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": "L158"}, {"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": "L165"}, {"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": "L177"}, {"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": "L206"}, {"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": "L245"}, {"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": "L252"}, {"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": "L373"}, {"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": "L420"}, {"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": "L425"}, {"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": "L437"}, {"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": "L446"}, {"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": "L455"}, {"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": "L463"}, {"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": "L481"}, {"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_crud_qc_inspection_batch_py", "label": "qc_inspection_batch.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L1"}, {"id": "crud_qc_inspection_batch_dec", "label": "_dec()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L27"}, {"id": "crud_qc_inspection_batch_unique_ids", "label": "_unique_ids()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L33"}, {"id": "crud_qc_inspection_batch_validate_batch_payload", "label": "_validate_batch_payload()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L45"}, {"id": "crud_qc_inspection_batch_order_source_for_wo", "label": "_order_source_for_wo()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L69"}, {"id": "crud_qc_inspection_batch_next_batch_no", "label": "_next_batch_no()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L78"}, {"id": "crud_qc_inspection_batch_allocate_batch_name", "label": "_allocate_batch_name()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L97"}, {"id": "crud_qc_inspection_batch_sync_batch_assignees", "label": "sync_batch_assignees()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L119"}, {"id": "crud_qc_inspection_batch_batch_assignee_user_ids", "label": "_batch_assignee_user_ids()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L168"}, {"id": "crud_qc_inspection_batch_assignee_names", "label": "_assignee_names()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L182"}, {"id": "crud_qc_inspection_batch_apply_assignees_to_batch_tasks", "label": "_apply_assignees_to_batch_tasks()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L194"}, {"id": "crud_qc_inspection_batch_batch_has_blocking_tasks", "label": "_batch_has_blocking_tasks()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L223"}, {"id": "crud_qc_inspection_batch_soft_delete_batch", "label": "_soft_delete_batch()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L237"}, {"id": "crud_qc_inspection_batch_sync_batch_lab_items", "label": "_sync_batch_lab_items()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L285"}, {"id": "crud_qc_inspection_batch_sync_batch_third_items", "label": "_sync_batch_third_items()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L413"}, {"id": "crud_qc_inspection_batch_batch_item_ids", "label": "_batch_item_ids()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L557"}, {"id": "crud_qc_inspection_batch_batch_to_dict", "label": "_batch_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L578"}, {"id": "crud_qc_inspection_batch_list_inspection_batches", "label": "list_inspection_batches()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L607"}, {"id": "crud_qc_inspection_batch_configure_work_order_batches", "label": "configure_work_order_batches()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L655"}, {"id": "crud_qc_inspection_batch_normalize_legacy_configure_to_batches", "label": "normalize_legacy_configure_to_batches()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L820"}, {"id": "crud_qc_inspection_batch_rationale_821", "label": "\u5c06\u6241\u5e73 labItemIds \u914d\u7f6e\u8f6c\u4e3a\u5355\u6279\u6b21\u7ed3\u6784\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L821"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_bLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_api_qc_py", "label": "qc.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L1"}, {"id": "api_qc_thirdpartycompanybody", "label": "ThirdPartyCompanyBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L50"}, {"id": "basemodel", "label": "BaseModel", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "api_qc_thirdpartycompanyupdatebody", "label": "ThirdPartyCompanyUpdateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L68"}, {"id": "api_qc_list_qc_third_party_companies", "label": "list_qc_third_party_companies()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L87"}, {"id": "api_qc_list_qc_third_party_company_options", "label": "list_qc_third_party_company_options()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L109"}, {"id": "api_qc_create_qc_third_party_company", "label": "create_qc_third_party_company()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L123"}, {"id": "api_qc_update_qc_third_party_company", "label": "update_qc_third_party_company()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L158"}, {"id": "api_qc_delete_qc_third_party_company", "label": "delete_qc_third_party_company()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L195"}, {"id": "api_qc_thirdpartyitembody", "label": "ThirdPartyItemBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L215"}, {"id": "api_qc_thirdpartyitemupdatebody", "label": "ThirdPartyItemUpdateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L227"}, {"id": "api_qc_list_qc_third_party_items", "label": "list_qc_third_party_items()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L240"}, {"id": "api_qc_list_qc_third_party_items_active", "label": "list_qc_third_party_items_active()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L269"}, {"id": "api_qc_create_qc_third_party_item", "label": "create_qc_third_party_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L284"}, {"id": "api_qc_update_qc_third_party_item", "label": "update_qc_third_party_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L313"}, {"id": "api_qc_delete_qc_third_party_item", "label": "delete_qc_third_party_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L344"}, {"id": "api_qc_labtestitembody", "label": "LabTestItemBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L364"}, {"id": "api_qc_labtestitemupdatebody", "label": "LabTestItemUpdateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L375"}, {"id": "api_qc_itemparambody", "label": "ItemParamBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L389"}, {"id": "api_qc_taskresultbody", "label": "TaskResultBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L394"}, {"id": "api_qc_taskcompletewithresultbody", "label": "TaskCompleteWithResultBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L399"}, {"id": "api_qc_list_qc_lab_test_items", "label": "list_qc_lab_test_items()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L409"}, {"id": "api_qc_list_qc_lab_test_items_active", "label": "list_qc_lab_test_items_active()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L431"}, {"id": "api_qc_create_qc_lab_test_item", "label": "create_qc_lab_test_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L445"}, {"id": "api_qc_update_qc_lab_test_item", "label": "update_qc_lab_test_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L473"}, {"id": "api_Line 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": "L90"}, {"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": "L131"}, {"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": "L137"}, {"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": "L141"}, {"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": "L148"}, {"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": "L153"}, {"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": "L158"}, {"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": "L165"}, {"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": "L177"}, {"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": "L206"}, {"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": "L245"}, {"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": "L252"}, {"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": "L373"}, {"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": "L420"}, {"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": "L425"}, {"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": "L437"}, {"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": "L446"}, {"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": "L455"}, {"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": "L463"}, {"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": "L481"}, {"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_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": "L90"}, {"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": "L131"}, {"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": "L137"}, {"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": "L141"}, {"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": "L148"}, {"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": "L153"}, {"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": "L158"}, {"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": "L165"}, {"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": "L177"}, {"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": "L206"}, {"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": "L245"}, {"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": "L252"}, {"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": "L373"}, {"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": "L420"}, {"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": "L425"}, {"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": "L437"}, {"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": "L446"}, {"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": "L455"}, {"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": "L463"}, {"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": "L481"}, {"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_crud_qc_task_result_py", "label": "qc_task_result.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task_result.py", "source_location": "L1"}, {"id": "crud_qc_task_result_row_to_dict", "label": "_row_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task_result.py", "source_location": "L15"}, {"id": "crud_qc_task_result_list_saved_results_by_task", "label": "_list_saved_results_by_task()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task_result.py", "source_location": "L27"}, {"id": "crud_qc_task_result_resolve_task_item_ref", "label": "_resolve_task_item_ref()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task_result.py", "source_location": "L42"}, {"id": "crud_qc_task_result_merge_saved_into_template", "label": "_merge_saved_into_template()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task_result.py", "source_location": "L69"}, {"id": "crud_qc_task_result_list_results_by_task", "label": "list_results_by_task()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task_result.py", "source_location": "L100"}, {"id": "crud_qc_task_result_save_results_for_task", "label": "save_results_for_task()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task_result.py", "source_location": "L117"}, {"id": "crud_qc_task_result_rationale_43", "label": "\u8fd4\u56de (item_source, item_id) \u4f9b biz_qc_item_param \u67e5\u8be2\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task_result.py", "source_location": "L43"}, {"id": "crud_qc_task_result_rationale_103", "label": "\u8fd4\u56de\u4efb\u52a1\u7ed3\u679c\u586b\u5199\u8868\u5355\uff1a\u9879\u76ee\u53c2\u6570\u6a21\u677f + \u5df2\u4fdd\u5b58\u503c\uff08\u65e0\u6a21\u677f\u65f6\u4ec5\u8fd4\u56de\u5df2\u4fdd\u5b58\u8bb0\u5f55\uff09\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task_result.py", "source_location": "L103"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_task_result_py", "target": "datetime", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task_result.py", "source_location": "L3", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_task_result_py", "target": "typing", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task_result.py", "source_location": "L4", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_task_result_py", "target": "sqlalchemy_orm", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task_result.py", "source_location": "L6", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_task_result_py", "target": "app_core_utils", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task_result.py", "source_location": "L8", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_task_result_py", "target": "app_crud", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task_result.py", "source_location": "L9", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_task_result_py", "target": "app_crud_qc_task", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task_result.py", "source_location": "L10", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_task_result_py", "target": "app_models_models", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task_result.py", "source_location": "L11", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_task_result_py", "target": "app_models_qc_param", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task_result.py", "source_location": "L12", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_task_result_py", "target": "crud_qc_task_result_row_to_dict", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task_result.py", "source_location": "L15", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_task_result_py", "targeLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_inspection_batch_py", "label": "qc_inspection_batch.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L1"}, {"id": "crud_qc_inspection_batch_dec", "label": "_dec()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L27"}, {"id": "crud_qc_inspection_batch_unique_ids", "label": "_unique_ids()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L33"}, {"id": "crud_qc_inspection_batch_validate_batch_payload", "label": "_validate_batch_payload()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L45"}, {"id": "crud_qc_inspection_batch_order_source_for_wo", "label": "_order_source_for_wo()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L66"}, {"id": "crud_qc_inspection_batch_next_batch_no", "label": "_next_batch_no()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L75"}, {"id": "crud_qc_inspection_batch_allocate_batch_name", "label": "_allocate_batch_name()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L94"}, {"id": "crud_qc_inspection_batch_sync_batch_assignees", "label": "sync_batch_assignees()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L116"}, {"id": "crud_qc_inspection_batch_batch_assignee_user_ids", "label": "_batch_assignee_user_ids()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L165"}, {"id": "crud_qc_inspection_batch_assignee_names", "label": "_assignee_names()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L179"}, {"id": "crud_qc_inspection_batch_apply_assignees_to_batch_tasks", "label": "_apply_assignees_to_batch_tasks()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L191"}, {"id": "crud_qc_inspection_batch_batch_has_blocking_tasks", "label": "_batch_has_blocking_tasks()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L220"}, {"id": "crud_qc_inspection_batch_soft_delete_batch", "label": "_soft_delete_batch()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L234"}, {"id": "crud_qc_inspection_batch_sync_batch_lab_items", "label": "_sync_batch_lab_items()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L282"}, {"id": "crud_qc_inspection_batch_sync_batch_third_items", "label": "_sync_batch_third_items()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L410"}, {"id": "crud_qc_inspection_batch_batch_item_ids", "label": "_batch_item_ids()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L554"}, {"id": "crud_qc_inspection_batch_batch_to_dict", "label": "_batch_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L575"}, {"id": "crud_qc_inspection_batch_list_inspection_batches", "label": "list_inspection_batches()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L604"}, {"id": "crud_qc_inspection_batch_configure_work_order_batches", "label": "configure_work_order_batches()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L652"}, {"id": "crud_qc_inspection_batch_normalize_legacy_configure_to_batches", "label": "normalize_legacy_configure_to_batches()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L818"}, {"id": "crud_qc_inspection_batch_rationale_819", "label": "\u5c06\u6241\u5e73 labItemIds \u914d\u7f6e\u8f6c\u4e3a\u5355\u6279\u6b21\u7ed3\u6784\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py", "source_location": "L819"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_bLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_api_qc_py", "label": "qc.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L1"}, {"id": "api_qc_thirdpartycompanybody", "label": "ThirdPartyCompanyBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L48"}, {"id": "basemodel", "label": "BaseModel", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "api_qc_thirdpartycompanyupdatebody", "label": "ThirdPartyCompanyUpdateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L66"}, {"id": "api_qc_list_qc_third_party_companies", "label": "list_qc_third_party_companies()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L85"}, {"id": "api_qc_list_qc_third_party_company_options", "label": "list_qc_third_party_company_options()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L107"}, {"id": "api_qc_create_qc_third_party_company", "label": "create_qc_third_party_company()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L121"}, {"id": "api_qc_update_qc_third_party_company", "label": "update_qc_third_party_company()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L156"}, {"id": "api_qc_delete_qc_third_party_company", "label": "delete_qc_third_party_company()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L193"}, {"id": "api_qc_thirdpartyitembody", "label": "ThirdPartyItemBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L213"}, {"id": "api_qc_thirdpartyitemupdatebody", "label": "ThirdPartyItemUpdateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L225"}, {"id": "api_qc_list_qc_third_party_items", "label": "list_qc_third_party_items()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L238"}, {"id": "api_qc_list_qc_third_party_items_active", "label": "list_qc_third_party_items_active()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L267"}, {"id": "api_qc_create_qc_third_party_item", "label": "create_qc_third_party_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L282"}, {"id": "api_qc_update_qc_third_party_item", "label": "update_qc_third_party_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L311"}, {"id": "api_qc_delete_qc_third_party_item", "label": "delete_qc_third_party_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L342"}, {"id": "api_qc_labtestitembody", "label": "LabTestItemBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L362"}, {"id": "api_qc_labtestitemupdatebody", "label": "LabTestItemUpdateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L373"}, {"id": "api_qc_itemparambody", "label": "ItemParamBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L387"}, {"id": "api_qc_taskresultbody", "label": "TaskResultBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L392"}, {"id": "api_qc_taskcompletewithresultbody", "label": "TaskCompleteWithResultBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L397"}, {"id": "api_qc_list_qc_lab_test_items", "label": "list_qc_lab_test_items()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L407"}, {"id": "api_qc_list_qc_lab_test_items_active", "label": "list_qc_lab_test_items_active()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L429"}, {"id": "api_qc_create_qc_lab_test_item", "label": "create_qc_lab_test_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L443"}, {"id": "api_qc_update_qc_lab_test_item", "label": "update_qc_lab_test_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L471"}, {"id": "api_Line 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_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_api_env_detect_py", "label": "env_detect.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L1"}, {"id": "api_env_detect_get_tenant_no", "label": "_get_tenant_no()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L22"}, {"id": "api_env_detect_get_current_user_id", "label": "_get_current_user_id()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L32"}, {"id": "api_env_detect_page_payload", "label": "_page_payload()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L39"}, {"id": "api_env_detect_envdetectitembody", "label": "EnvDetectItemBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L52"}, {"id": "basemodel", "label": "BaseModel", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "api_env_detect_envdetectitemupdatebody", "label": "EnvDetectItemUpdateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L62"}, {"id": "api_env_detect_list_env_detect_items", "label": "list_env_detect_items()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L73"}, {"id": "api_env_detect_list_active_env_detect_items", "label": "list_active_env_detect_items()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L117"}, {"id": "api_env_detect_get_env_detect_item", "label": "get_env_detect_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L132"}, {"id": "api_env_detect_create_env_detect_item", "label": "create_env_detect_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L150"}, {"id": "api_env_detect_update_env_detect_item", "label": "update_env_detect_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L181"}, {"id": "api_env_detect_delete_env_detect_item", "label": "delete_env_detect_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L211"}, {"id": "api_env_detect_parse_detect_date", "label": "_parse_detect_date()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L231"}, {"id": "api_env_detect_parse_detect_date_bound", "label": "_parse_detect_date_bound()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L241"}, {"id": "api_env_detect_envdetectrecordbody", "label": "EnvDetectRecordBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L251"}, {"id": "api_env_detect_envdetectrecordupdatebody", "label": "EnvDetectRecordUpdateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L263"}, {"id": "api_env_detect_list_env_detect_records", "label": "list_env_detect_records()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L276"}, {"id": "api_env_detect_get_env_detect_record", "label": "get_env_detect_record()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L336"}, {"id": "api_env_detect_create_env_detect_record", "label": "create_env_detect_record()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L354"}, {"id": "api_env_detect_update_env_detect_record", "label": "update_env_detect_record()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L395"}, {"id": "api_env_detect_delete_env_detect_record", "label": "delete_env_detect_record()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L437"}, {"id": "api_env_detect_rationale_1", "label": "\u73af\u5883\u68c0\u6d4b \u2014 \u68c0\u6d4b\u9879\u7ef4\u62a4 + \u767b\u8bb0\u7ba1\u7406 API", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L1"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_backend_app_api_env_detect_py", "target": "datetime", "relation": "imports_froLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_item_param_py", "label": "qc_item_param.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L1"}, {"id": "crud_qc_item_param_row_to_dict", "label": "_row_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L13"}, {"id": "crud_qc_item_param_parse_options_list", "label": "_parse_options_list()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L27"}, {"id": "crud_qc_item_param_validate_param", "label": "_validate_param()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L43"}, {"id": "crud_qc_item_param_list_params_by_item", "label": "list_params_by_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L55"}, {"id": "crud_qc_item_param_save_params_for_item", "label": "save_params_for_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L71"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_item_param_py", "target": "json", "relation": "imports", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L3", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_item_param_py", "target": "datetime", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L4", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_item_param_py", "target": "typing", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L5", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_item_param_py", "target": "sqlalchemy_orm", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L7", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_item_param_py", "target": "app_core_utils", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L9", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_item_param_py", "target": "app_models_qc_param", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L10", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_item_param_py", "target": "crud_qc_item_param_row_to_dict", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L13", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_item_param_py", "target": "crud_qc_item_param_parse_options_list", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L27", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_item_param_py", "target": "crud_qc_item_param_validate_param", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L43", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_item_param_py", "target": "crud_qc_item_param_list_params_by_item", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L55", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_item_param_py", "target": "crud_qc_item_param_save_params_for_item", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L71", "weight": 1.0}, {"source": "crud_qc_item_param_validate_param", "target": "crud_qc_item_param_parse_options_list", "relation": "calls", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L48", "weight": 1.0}, {"source": "crud_qc_item_param_list_params_by_item", "target": "crud_qc_item_param_row_to_dict", "relation": "calls", "confidence": "EXTRACTED", "source_file": "Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_api_env_detect_py", "label": "env_detect.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L1"}, {"id": "api_env_detect_get_tenant_no", "label": "_get_tenant_no()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L21"}, {"id": "api_env_detect_get_current_user_id", "label": "_get_current_user_id()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L31"}, {"id": "api_env_detect_page_payload", "label": "_page_payload()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L38"}, {"id": "api_env_detect_envdetectitembody", "label": "EnvDetectItemBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L51"}, {"id": "basemodel", "label": "BaseModel", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "api_env_detect_envdetectitemupdatebody", "label": "EnvDetectItemUpdateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L61"}, {"id": "api_env_detect_list_env_detect_items", "label": "list_env_detect_items()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L72"}, {"id": "api_env_detect_list_active_env_detect_items", "label": "list_active_env_detect_items()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L116"}, {"id": "api_env_detect_get_env_detect_item", "label": "get_env_detect_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L131"}, {"id": "api_env_detect_create_env_detect_item", "label": "create_env_detect_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L149"}, {"id": "api_env_detect_update_env_detect_item", "label": "update_env_detect_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L180"}, {"id": "api_env_detect_delete_env_detect_item", "label": "delete_env_detect_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L210"}, {"id": "api_env_detect_envdetectrecordbody", "label": "EnvDetectRecordBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L230"}, {"id": "api_env_detect_envdetectrecordupdatebody", "label": "EnvDetectRecordUpdateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L242"}, {"id": "api_env_detect_list_env_detect_records", "label": "list_env_detect_records()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L255"}, {"id": "api_env_detect_get_env_detect_record", "label": "get_env_detect_record()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L315"}, {"id": "api_env_detect_create_env_detect_record", "label": "create_env_detect_record()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L333"}, {"id": "api_env_detect_update_env_detect_record", "label": "update_env_detect_record()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L374"}, {"id": "api_env_detect_delete_env_detect_record", "label": "delete_env_detect_record()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L416"}, {"id": "api_env_detect_rationale_1", "label": "\u73af\u5883\u68c0\u6d4b \u2014 \u68c0\u6d4b\u9879\u7ef4\u62a4 + \u767b\u8bb0\u7ba1\u7406 API", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L1"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_backend_app_api_env_detect_py", "target": "typing", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L2", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_api_env_detect_py", "target": "fastapi", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L4", "weight": 1.0},Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_api_env_detect_py", "label": "env_detect.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L1"}, {"id": "api_env_detect_get_tenant_no", "label": "_get_tenant_no()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L21"}, {"id": "api_env_detect_get_current_user_id", "label": "_get_current_user_id()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L31"}, {"id": "api_env_detect_page_payload", "label": "_page_payload()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L38"}, {"id": "api_env_detect_envdetectitembody", "label": "EnvDetectItemBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L51"}, {"id": "basemodel", "label": "BaseModel", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "api_env_detect_envdetectitemupdatebody", "label": "EnvDetectItemUpdateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L61"}, {"id": "api_env_detect_list_env_detect_items", "label": "list_env_detect_items()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L72"}, {"id": "api_env_detect_list_active_env_detect_items", "label": "list_active_env_detect_items()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L116"}, {"id": "api_env_detect_get_env_detect_item", "label": "get_env_detect_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L131"}, {"id": "api_env_detect_create_env_detect_item", "label": "create_env_detect_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L149"}, {"id": "api_env_detect_update_env_detect_item", "label": "update_env_detect_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L180"}, {"id": "api_env_detect_delete_env_detect_item", "label": "delete_env_detect_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L210"}, {"id": "api_env_detect_envdetectrecordbody", "label": "EnvDetectRecordBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L230"}, {"id": "api_env_detect_envdetectrecordupdatebody", "label": "EnvDetectRecordUpdateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L242"}, {"id": "api_env_detect_list_env_detect_records", "label": "list_env_detect_records()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L255"}, {"id": "api_env_detect_get_env_detect_record", "label": "get_env_detect_record()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L315"}, {"id": "api_env_detect_create_env_detect_record", "label": "create_env_detect_record()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L333"}, {"id": "api_env_detect_update_env_detect_record", "label": "update_env_detect_record()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L374"}, {"id": "api_env_detect_delete_env_detect_record", "label": "delete_env_detect_record()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L416"}, {"id": "api_env_detect_rationale_1", "label": "\u73af\u5883\u68c0\u6d4b \u2014 \u68c0\u6d4b\u9879\u7ef4\u62a4 + \u767b\u8bb0\u7ba1\u7406 API", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L1"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_backend_app_api_env_detect_py", "target": "typing", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L2", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_api_env_detect_py", "target": "fastapi", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py", "source_location": "L4", "weight": 1.0},Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_api_qc_py", "label": "qc.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L1"}, {"id": "api_qc_thirdpartycompanybody", "label": "ThirdPartyCompanyBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L48"}, {"id": "basemodel", "label": "BaseModel", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "api_qc_thirdpartycompanyupdatebody", "label": "ThirdPartyCompanyUpdateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L66"}, {"id": "api_qc_list_qc_third_party_companies", "label": "list_qc_third_party_companies()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L85"}, {"id": "api_qc_list_qc_third_party_company_options", "label": "list_qc_third_party_company_options()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L107"}, {"id": "api_qc_create_qc_third_party_company", "label": "create_qc_third_party_company()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L121"}, {"id": "api_qc_update_qc_third_party_company", "label": "update_qc_third_party_company()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L156"}, {"id": "api_qc_delete_qc_third_party_company", "label": "delete_qc_third_party_company()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L193"}, {"id": "api_qc_thirdpartyitembody", "label": "ThirdPartyItemBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L213"}, {"id": "api_qc_thirdpartyitemupdatebody", "label": "ThirdPartyItemUpdateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L225"}, {"id": "api_qc_list_qc_third_party_items", "label": "list_qc_third_party_items()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L238"}, {"id": "api_qc_list_qc_third_party_items_active", "label": "list_qc_third_party_items_active()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L267"}, {"id": "api_qc_create_qc_third_party_item", "label": "create_qc_third_party_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L282"}, {"id": "api_qc_update_qc_third_party_item", "label": "update_qc_third_party_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L311"}, {"id": "api_qc_delete_qc_third_party_item", "label": "delete_qc_third_party_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L342"}, {"id": "api_qc_labtestitembody", "label": "LabTestItemBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L362"}, {"id": "api_qc_labtestitemupdatebody", "label": "LabTestItemUpdateBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L373"}, {"id": "api_qc_itemparambody", "label": "ItemParamBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L387"}, {"id": "api_qc_taskresultbody", "label": "TaskResultBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L392"}, {"id": "api_qc_taskcompletewithresultbody", "label": "TaskCompleteWithResultBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L397"}, {"id": "api_qc_list_qc_lab_test_items", "label": "list_qc_lab_test_items()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L407"}, {"id": "api_qc_list_qc_lab_test_items_active", "label": "list_qc_lab_test_items_active()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L429"}, {"id": "api_qc_create_qc_lab_test_item", "label": "create_qc_lab_test_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L443"}, {"id": "api_qc_update_qc_lab_test_item", "label": "update_qc_lab_test_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py", "source_location": "L471"}, {"id": "api_Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_item_param_py", "label": "qc_item_param.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L1"}, {"id": "crud_qc_item_param_row_to_dict", "label": "_row_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L13"}, {"id": "crud_qc_item_param_parse_options_list", "label": "_parse_options_list()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L27"}, {"id": "crud_qc_item_param_validate_param", "label": "_validate_param()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L43"}, {"id": "crud_qc_item_param_list_params_by_item", "label": "list_params_by_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L58"}, {"id": "crud_qc_item_param_save_params_for_item", "label": "save_params_for_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L74"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_item_param_py", "target": "json", "relation": "imports", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L3", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_item_param_py", "target": "datetime", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L4", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_item_param_py", "target": "typing", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L5", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_item_param_py", "target": "sqlalchemy_orm", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L7", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_item_param_py", "target": "app_core_utils", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L9", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_item_param_py", "target": "app_models_qc_param", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L10", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_item_param_py", "target": "crud_qc_item_param_row_to_dict", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L13", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_item_param_py", "target": "crud_qc_item_param_parse_options_list", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L27", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_item_param_py", "target": "crud_qc_item_param_validate_param", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L43", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_item_param_py", "target": "crud_qc_item_param_list_params_by_item", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L58", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_qc_item_param_py", "target": "crud_qc_item_param_save_params_for_item", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L74", "weight": 1.0}, {"source": "crud_qc_item_param_validate_param", "target": "crud_qc_item_param_parse_options_list", "relation": "calls", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py", "source_location": "L48", "weight": 1.0}, {"source": "crud_qc_item_param_list_params_by_item", "target": "crud_qc_item_param_row_to_dict", "relation": "calls", "confidence": "EXTRACTED", "source_file": "Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_models_production_material_py", "label": "production_material.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/production_material.py", "source_location": "L1"}, {"id": "models_production_material_bizproductionmaterial", "label": "BizProductionMaterial", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/production_material.py", "source_location": "L8"}, {"id": "base", "label": "Base", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "models_production_material_bizproductionmaterialstockbatch", "label": "BizProductionMaterialStockBatch", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/production_material.py", "source_location": "L31"}, {"id": "models_production_material_bizproductionmaterialusage", "label": "BizProductionMaterialUsage", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/production_material.py", "source_location": "L64"}, {"id": "models_production_material_bizproductionmaterialformula", "label": "BizProductionMaterialFormula", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/production_material.py", "source_location": "L114"}, {"id": "models_production_material_bizproductionmaterialprep", "label": "BizProductionMaterialPrep", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/production_material.py", "source_location": "L136"}, {"id": "models_production_material_bizproductionmaterialprepline", "label": "BizProductionMaterialPrepLine", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/production_material.py", "source_location": "L164"}, {"id": "models_production_material_bizproductionmaterialstockcheck", "label": "BizProductionMaterialStockCheck", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/production_material.py", "source_location": "L187"}, {"id": "models_production_material_bizproductionmaterialstockcheckbatchline", "label": "BizProductionMaterialStockCheckBatchLine", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/production_material.py", "source_location": "L206"}, {"id": "models_production_material_bizproductionmaterialstockcheckunusedline", "label": "BizProductionMaterialStockCheckUnusedLine", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/production_material.py", "source_location": "L239"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_backend_app_models_production_material_py", "target": "datetime", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/production_material.py", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_models_production_material_py", "target": "sqlalchemy", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/production_material.py", "source_location": "L3", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_models_production_material_py", "target": "app_core_database", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/production_material.py", "source_location": "L5", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_models_production_material_py", "target": "models_production_material_bizproductionmaterial", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/production_material.py", "source_location": "L8", "weight": 1.0}, {"source": "models_production_material_bizproductionmaterial", "target": "base", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/production_material.py", "source_location": "L8", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_models_production_material_py", "target": "models_production_material_bizproductionmaterialstockbatch", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/production_material.py", "source_location": "L31", "weight": 1.0}, {"source": "models_production_material_bizproductionmaterialstockbatch", "target": "base", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/production_material.py", "source_location": "L31", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_models_production_material_py", "target": "models_production_material_bizproductionmaterialusage", "relation": "contains", "confidence": "EXTRACTED", "sourceLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_models_qc_param_py", "label": "qc_param.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/qc_param.py", "source_location": "L1"}, {"id": "models_qc_param_bizqcitemparam", "label": "BizQcItemParam", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/qc_param.py", "source_location": "L8"}, {"id": "base", "label": "Base", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "models_qc_param_bizqctaskresult", "label": "BizQcTaskResult", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/qc_param.py", "source_location": "L32"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_backend_app_models_qc_param_py", "target": "datetime", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/qc_param.py", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_models_qc_param_py", "target": "sqlalchemy", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/qc_param.py", "source_location": "L3", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_models_qc_param_py", "target": "app_core_database", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/qc_param.py", "source_location": "L5", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_models_qc_param_py", "target": "models_qc_param_bizqcitemparam", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/qc_param.py", "source_location": "L8", "weight": 1.0}, {"source": "models_qc_param_bizqcitemparam", "target": "base", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/qc_param.py", "source_location": "L8", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_models_qc_param_py", "target": "models_qc_param_bizqctaskresult", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/qc_param.py", "source_location": "L32", "weight": 1.0}, {"source": "models_qc_param_bizqctaskresult", "target": "base", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/qc_param.py", "source_location": "L32", "weight": 1.0}], "raw_calls": []}
@@ -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_env_detect_record_py", "label": "env_detect_record.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L1"}, {"id": "crud_env_detect_record_split_csv", "label": "_split_csv()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L15"}, {"id": "crud_env_detect_record_join_csv", "label": "_join_csv()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L19"}, {"id": "crud_env_detect_record_row_to_dict", "label": "_row_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L23"}, {"id": "crud_env_detect_record_generate_record_no", "label": "_generate_record_no()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L47"}, {"id": "crud_env_detect_record_resolve_inspector_snapshots", "label": "_resolve_inspector_snapshots()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L69"}, {"id": "crud_env_detect_record_resolve_item_snapshot", "label": "_resolve_item_snapshot()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L102"}, {"id": "crud_env_detect_record_get_env_detect_record", "label": "get_env_detect_record()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L113"}, {"id": "crud_env_detect_record_list_env_detect_records", "label": "list_env_detect_records()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L124"}, {"id": "crud_env_detect_record_create_env_detect_record", "label": "create_env_detect_record()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L181"}, {"id": "crud_env_detect_record_update_env_detect_record", "label": "update_env_detect_record()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L230"}, {"id": "crud_env_detect_record_soft_delete_env_detect_record", "label": "soft_delete_env_detect_record()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L282"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_env_detect_record_py", "target": "datetime", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L4", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_env_detect_record_py", "target": "typing", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L5", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_env_detect_record_py", "target": "sqlalchemy", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L7", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_env_detect_record_py", "target": "sqlalchemy_orm", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L8", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_env_detect_record_py", "target": "app_core_utils", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L10", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_env_detect_record_py", "target": "app_crud", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L11", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_env_detect_record_py", "target": "app_models_models", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L12", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_env_detect_record_py", "target": "crud_env_detect_record_split_csv", "relation": "contains", "confidence": "EXTRACTED"Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_crud_sample_reception_py", "label": "sample_reception.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L1"}, {"id": "crud_sample_reception_donation_sample_id_col", "label": "_donation_sample_id_col()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L36"}, {"id": "crud_sample_reception_fmt_dt", "label": "_fmt_dt()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L41"}, {"id": "crud_sample_reception_normalize_date_bound", "label": "_normalize_date_bound()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L45"}, {"id": "crud_sample_reception_dec", "label": "_dec()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L52"}, {"id": "crud_sample_reception_reception_to_dict", "label": "_reception_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L58"}, {"id": "crud_sample_reception_sample_detail_rows_query", "label": "_sample_detail_rows_query()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L83"}, {"id": "crud_sample_reception_donation_sample_detail_rows_query", "label": "_donation_sample_detail_rows_query()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L117"}, {"id": "crud_sample_reception_donation_sample_pending_query", "label": "_donation_sample_pending_query()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L151"}, {"id": "crud_sample_reception_donation_rows_to_sample_list_items", "label": "_donation_rows_to_sample_list_items()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L188"}, {"id": "crud_sample_reception_sample_pending_query", "label": "_sample_pending_query()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L284"}, {"id": "crud_sample_reception_rows_to_sample_list_items", "label": "_rows_to_sample_list_items()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L321"}, {"id": "crud_sample_reception_list_pending_protocol_samples", "label": "list_pending_protocol_samples()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L427"}, {"id": "crud_sample_reception_list_received_protocol_samples", "label": "list_received_protocol_samples()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L500"}, {"id": "crud_sample_reception_get_reception_by_id", "label": "get_reception_by_id()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L639"}, {"id": "crud_sample_reception_create_sample_reception", "label": "create_sample_reception()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L707"}, {"id": "crud_sample_reception_update_sample_reception", "label": "update_sample_reception()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L971"}, {"id": "crud_sample_reception_reception_detail_item", "label": "_reception_detail_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L1086"}, {"id": "crud_sample_reception_reception_sample_code", "label": "_reception_sample_code()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L1103"}, {"id": "crud_sample_reception_get_receptions_map_for_samples", "label": "get_receptions_map_for_samples()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L1118"}, {"id": "crud_sample_reception_get_receptions_map_for_donation_samples", "label": "get_receptions_map_for_donation_samples()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L1163"}, {"id": "crud_sample_reception_get_lab_storage_code_map_by_sample_codes", "label": "get_lab_storLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_crud_env_detect_record_py", "label": "env_detect_record.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L1"}, {"id": "crud_env_detect_record_split_csv", "label": "_split_csv()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L15"}, {"id": "crud_env_detect_record_join_csv", "label": "_join_csv()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L19"}, {"id": "crud_env_detect_record_row_to_dict", "label": "_row_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L23"}, {"id": "crud_env_detect_record_generate_record_no", "label": "_generate_record_no()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L47"}, {"id": "crud_env_detect_record_resolve_inspector_snapshots", "label": "_resolve_inspector_snapshots()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L69"}, {"id": "crud_env_detect_record_resolve_item_snapshot", "label": "_resolve_item_snapshot()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L102"}, {"id": "crud_env_detect_record_get_env_detect_record", "label": "get_env_detect_record()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L113"}, {"id": "crud_env_detect_record_list_env_detect_records", "label": "list_env_detect_records()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L124"}, {"id": "crud_env_detect_record_create_env_detect_record", "label": "create_env_detect_record()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L181"}, {"id": "crud_env_detect_record_update_env_detect_record", "label": "update_env_detect_record()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L230"}, {"id": "crud_env_detect_record_soft_delete_env_detect_record", "label": "soft_delete_env_detect_record()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L282"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_env_detect_record_py", "target": "datetime", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L4", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_env_detect_record_py", "target": "typing", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L5", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_env_detect_record_py", "target": "sqlalchemy", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L7", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_env_detect_record_py", "target": "sqlalchemy_orm", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L8", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_env_detect_record_py", "target": "app_core_utils", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L10", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_env_detect_record_py", "target": "app_crud", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L11", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_env_detect_record_py", "target": "app_models_models", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py", "source_location": "L12", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_env_detect_record_py", "target": "crud_env_detect_record_split_csv", "relation": "contains", "confidence": "EXTRACTED"Line 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
+44
View File
@@ -1373,6 +1373,46 @@ class BizQcWorkOrder(Base):
del_flag = Column(SmallInteger, nullable=False, default=0, comment="删除标记")
class BizQcWorkOrderInspectionBatch(Base):
"""质检工单检测批次 biz_qc_work_order_inspection_batch"""
__tablename__ = "biz_qc_work_order_inspection_batch"
id = Column(String(64), primary_key=True, comment="主键ID")
work_order_id = Column(String(64), nullable=False, comment="工单ID")
order_source = Column(String(20), nullable=False, default="inbound", comment="工单来源")
batch_no = Column(String(32), nullable=False, comment="批次编号")
batch_name = Column(String(200), nullable=False, comment="批次展示名")
tenant_no = Column(String(64), nullable=False, default="zkxw", comment="租户编号")
order_num = Column(Integer, nullable=True, 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 BizQcBatchAssignee(Base):
"""质检检测批次负责人 biz_qc_batch_assignee"""
__tablename__ = "biz_qc_batch_assignee"
id = Column(String(64), primary_key=True, comment="主键")
batch_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=True, comment="创建人部门ID")
create_time = Column(DateTime, nullable=True, comment="创建时间")
update_by = Column(String(64), nullable=True, comment="更新人id")
update_time = Column(DateTime, nullable=True, comment="更新时间")
del_flag = Column(SmallInteger, nullable=False, default=0, comment="删除标记")
class BizQcWorkOrderLabItem(Base):
"""质检工单-实验室项目明细 biz_qc_work_order_lab_item"""
@@ -1381,6 +1421,7 @@ class BizQcWorkOrderLabItem(Base):
id = Column(String(64), primary_key=True, comment="主键ID")
work_order_id = Column(String(64), nullable=False, comment="工单ID")
order_source = Column(String(20), nullable=False, default="inbound", comment="工单来源: inbound=入库, temporary=临时")
batch_id = Column(String(64), nullable=True, comment="检测批次ID")
lab_test_item_id = Column(String(64), nullable=False, comment="实验室项目ID")
item_code = Column(String(64), nullable=False, comment="项目编码快照")
item_name = Column(String(200), nullable=False, comment="项目名称快照")
@@ -1403,6 +1444,7 @@ class BizQcWorkOrderThirdItem(Base):
id = Column(String(64), primary_key=True, comment="主键ID")
work_order_id = Column(String(64), nullable=False, comment="工单ID")
order_source = Column(String(20), nullable=False, default="inbound", comment="工单来源: inbound=入库, temporary=临时")
batch_id = Column(String(64), nullable=True, comment="检测批次ID")
third_party_item_id = Column(String(64), nullable=False, comment="第三方项目ID")
company_id = Column(String(64), nullable=False, comment="公司ID")
company_name = Column(String(200), nullable=False, comment="公司名快照")
@@ -1427,6 +1469,7 @@ class BizQcTask(Base):
id = Column(String(64), primary_key=True, comment="主键ID")
work_order_id = Column(String(64), nullable=False, comment="工单ID")
batch_id = Column(String(64), nullable=True, comment="检测批次ID")
order_source = Column(String(20), nullable=False, default="inbound", comment="工单来源: inbound=入库, temporary=临时")
task_no = Column(String(32), nullable=False, comment="任务号")
task_type = Column(String(16), nullable=False, comment="sysz实验室 dsfjc第三方")
@@ -3548,6 +3591,7 @@ class BizEnvDetectRecord(Base):
id = Column(String(64), primary_key=True, comment="主键ID")
record_no = Column(String(32), nullable=False, comment="登记编号")
detect_time = Column(DateTime, nullable=False, comment="检测时间")
result_time = Column(DateTime, nullable=True, comment="结果时间")
inspector_ids = Column(String(500), nullable=False, comment="检测人用户ID,逗号分隔")
inspector_names = Column(String(500), nullable=False, comment="检测人姓名快照,逗号分隔")
env_detect_item_id = Column(String(64), nullable=False, comment="检测项目ID")
@@ -41,6 +41,7 @@ class BizProductionMaterialStockBatch(Base):
stock_in_no = Column(String(64), nullable=False, comment="入库编号")
stock_in_date = Column(Date, nullable=False, comment="入库日期")
expire_date = Column(Date, nullable=False, comment="过期日期")
production_date = Column(Date, nullable=True, comment="生产日期")
catalog_no = Column(String(128), nullable=False, comment="货号")
batch_no = Column(String(128), nullable=False, comment="批号")
manufacturer = Column(String(200), nullable=False, comment="厂家")
+2
View File
@@ -15,6 +15,8 @@ class BizQcItemParam(Base):
param_code = Column(String(64), nullable=False, comment="参数编码")
param_unit = Column(String(32), nullable=True, comment="单位")
param_type = Column(String(16), nullable=False, default="text", comment="number/text/select")
options = Column(String(2000), nullable=True, comment="单选选项列表,JSON数组格式")
default_value = Column(String(500), nullable=True, comment="单选默认值")
required_flag = Column(CHAR(1), nullable=False, default="0", comment="1必填 0非必填")
tenant_no = Column(String(64), nullable=False, default="zkxw", comment="租户编号")
order_num = Column(Integer, nullable=True, comment="排序号")
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+164 -107
View File
@@ -1,11 +1,11 @@
# Graph Report - app (2026-06-25)
# Graph Report - app (2026-06-26)
## Corpus Check
- 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.
- Large corpus: 212 files · ~254,769 words. Semantic extraction will be expensive (many Claude tokens). Consider running on a subfolder, or use --no-semantic to run AST-only.
## Summary
- 4285 nodes · 22669 edges · 43 communities detected
- Extraction: 32% EXTRACTED · 68% INFERRED · 0% AMBIGUOUS · INFERRED: 15304 edges (avg confidence: 0.59)
- 4340 nodes · 23048 edges · 54 communities detected
- Extraction: 32% EXTRACTED · 68% INFERRED · 0% AMBIGUOUS · INFERRED: 15583 edges (avg confidence: 0.59)
- Token cost: 0 input · 0 output
## Community Hubs (Navigation)
@@ -46,249 +46,306 @@
- [[_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 47|Community 47]]
- [[_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]]
## God Nodes (most connected - your core abstractions)
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
1. `success_response()` - 658 edges
2. `error_response()` - 544 edges
3. `SysUser` - 466 edges
4. `BizProductionMaterial` - 305 edges
5. `BizQcTaskResult` - 305 edges
6. `BizProductionMaterialUsage` - 304 edges
7. `BizQcMaterialUsage` - 301 edges
8. `BizQcMaterial` - 292 edges
9. `BizProductionMaterialStockBatch` - 291 edges
10. `BizMaterialUsage` - 288 edges
## Surprising Connections (you probably didn't know these)
- `统一记录 API 请求/响应出入参的中间件` --uses--> `SysApiRequestLog` [INFERRED]
core/api_request_log_middleware.py → models/models.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
- `中试服务体检管理 API(体检信息、预约项目、实际项目)` --uses--> `SysUser` [INFERRED]
api/pilot_checkup.py → models/models.py
- `get_menu_list()` --calls--> `success_response()` [INFERRED]
api/menu.py → core/response.py
- `保留匹配节点及其全部上级,供关键词检索后仍能正确组树。` --uses--> `SysDept` [INFERRED]
crud/dept.py → models/models.py
- `物品单位 CRUD(biz_item_unit)` --uses--> `BizItemUnit` [INFERRED]
crud/item_unit.py → models/models.py
- `快递公司 CRUD(biz_express_company)` --uses--> `BizExpressCompany` [INFERRED]
crud/express_company.py → models/models.py
## Communities
### Community 0 - "Community 0"
Cohesion: 0.01
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)
Nodes (821): attachment_download_url(), create_dept(), create_dict_data(), create_dict_type(), create_express_company(), create_item_unit(), create_menu(), create_post() (+813 more)
### Community 1 - "Community 1"
Cohesion: 0.01
Nodes (605): AttachmentDownloadUrlBody, DeptCreateBody, DeptUpdateBody, DictDataCreateBody, DictDataUpdateBody, DictTypeCreateBody, DictTypeUpdateBody, ExpressCompanyCreateBody (+597 more)
Nodes (600): AttachmentDownloadUrlBody, DeptCreateBody, DeptUpdateBody, DictDataCreateBody, DictDataUpdateBody, DictTypeCreateBody, DictTypeUpdateBody, ExpressCompanyCreateBody (+592 more)
### Community 2 - "Community 2"
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)
Cohesion: 0.09
Nodes (344): 环境检测 — 检测项维护 + 登记管理 API, 保险公司及险种价格管理 API(保险公司 + 开票信息 + 险种价格), ProcessDefCreateBody, ProcessDefUpdateBody, PublishVersionBody, 审批意见:前端传 commentText,兼容 comment。, StartInstanceBody, TaskActionBody (+336 more)
### Community 3 - "Community 3"
Cohesion: 0.08
Nodes (367): 环境检测 — 检测项维护 + 登记管理 API, 保险公司及险种价格管理 API(保险公司 + 开票信息 + 险种价格), AccessLogCreateBody, AccessLogUpdateBody, CleanRoomCreateBody, CleanRoomUpdateBody, ExhibitionAppointmentCreateBody, ExhibitionAppointmentUpdateBody (+359 more)
Cohesion: 0.01
Nodes (314): generate_uuid(), batch_stock_in(), batch_stock_out(), consumable_code_exists(), create_consumable(), _dec(), get_consumable(), _is_enabled_status() (+306 more)
### Community 4 - "Community 4"
Cohesion: 0.01
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)
Cohesion: 0.02
Nodes (248): _normalize_date_bound(), _persist_protocol_sub_resources(), format_date(), generate_id(), optional_date(), parse_date(), 将字符串 / date / datetime 转为 date 或 None, camel_to_snake() (+240 more)
### Community 5 - "Community 5"
Cohesion: 0.07
Nodes (231): CellSampleTypeCreateBody, CellSampleTypeUpdateBody, CollectionRegionCreateBody, CollectionRegionUpdateBody, CustomerCreateBody, CustomerUpdateBody, HospitalCreateBody, HospitalUpdateBody (+223 more)
Nodes (252): CellSampleTypeCreateBody, CellSampleTypeUpdateBody, CollectionRegionCreateBody, CollectionRegionUpdateBody, CustomerCreateBody, CustomerUpdateBody, HospitalCreateBody, HospitalUpdateBody (+244 more)
### Community 6 - "Community 6"
Cohesion: 0.02
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)
Nodes (225): list_ethnics_for_select(), AccessLogCreateBody, AccessLogUpdateBody, CleanRoomCreateBody, CleanRoomUpdateBody, create_access_log(), create_storage_slot(), create_uv_disinfect_log() (+217 more)
### Community 7 - "Community 7"
Cohesion: 0.02
Nodes (167): _code_exists(), create_material(), _dec(), _fmt_dt(), get_material(), get_material_entity(), get_my_usages(), _is_enabled_status() (+159 more)
Nodes (226): eq_unicode(), like_unicode(), MySQL 字符串比较/模糊匹配时统一排序规则,避免 Illegal mix of collations。, 跨表字符串等值比较(JOIN / WHERE)时统一为 utf8mb4_unicode_ci。, 将列显式设为 utf8mb4_unicode_ci。, unicode_ci(), batch_review(), _dec() (+218 more)
### Community 8 - "Community 8"
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)
Cohesion: 0.03
Nodes (167): batch_discard(), batch_options(), _batch_row_to_dict(), batch_stock_in(), batch_stock_out(), _create_batch_and_usage(), delete_batch(), _discard_batch() (+159 more)
### Community 9 - "Community 9"
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)
Nodes (107): get_designer_data(), _error_json(), 数据库异常:返回 JSON 并保留 CORS 头,避免浏览器只报跨域错误, 未捕获异常:统一 JSON 500,确保跨域场景下仍有 Access-Control-Allow-Origin, sqlalchemy_exception_handler(), unhandled_exception_handler(), BaseHTTPMiddleware, ApiCryptoError (+99 more)
### Community 10 - "Community 10"
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)
Cohesion: 0.03
Nodes (124): map_payments_by_schedule_ids(), 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() (+116 more)
### Community 11 - "Community 11"
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)
Cohesion: 0.06
Nodes (67): 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() (+59 more)
### Community 12 - "Community 12"
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)
Cohesion: 0.08
Nodes (58): 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() (+50 more)
### Community 13 - "Community 13"
Cohesion: 0.08
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)
Cohesion: 0.07
Nodes (49): _jwt_user_payload(), Web 端登录用 RSA 公钥(PKCS#1 v1.5,与 JSEncrypt 一致), Web 端登录:账号为 sys_user.user_no,密码为 sys_user.password(bcrypt)。, refresh_token(), web_login(), web_login_rsa_public_key(), lifespan(), create_access_token() (+41 more)
### Community 14 - "Community 14"
Cohesion: 0.09
Nodes (49): api_save_application(), auto_generate_schedules(), complete_application(), create_application(), create_basic_experiment_sale_schedules(), create_infusion_plan(), create_schedule(), _create_schedule_products() (+41 more)
### Community 15 - "Community 15"
Cohesion: 0.11
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
Nodes (11): get_wechat_pay_client(), get_wechat_pay_v2(), 微信支付工具模块 支持微信支付 V3 API 文档参考: https://pay.weixin.qq.com/docs/v3/apis/index.ht, 统一下单接口 (V3) Args: out_trade_no: 商户订单号 total, 申请退款 Args: out_trade_no: 商户订单号 total_amount, 小程序调起支付的二次签名参数(与公众号 JSAPI 规则一致), 统一下单 (V2 API) Args: out_trade_no: 商户订单号 tot, 生成签名 V3 (SHA256 with RSA) (+3 more)
### Community 16 - "Community 16"
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)
Cohesion: 0.12
Nodes (10): get_wechat_pay_client(), get_wechat_pay_v2(), 微信支付工具模块 支持微信支付 V3 API 文档参考: https://pay.weixin.qq.com/docs/v3/apis/index.ht, 统一下单接口 (V3) Args: out_trade_no: 商户订单号 total, 小程序调起支付的二次签名参数(与公众号 JSAPI 规则一致), 统一下单 (V2 API) Args: out_trade_no: 商户订单号 tot, 生成签名 V3 (SHA256 with RSA), wechat_pay() (+2 more)
### Community 17 - "Community 17"
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)
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)
### Community 18 - "Community 18"
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)
Cohesion: 0.21
Nodes (15): batch_generate_protocol_nos(), batch_issue_protocol_nos(), batch_revoke_protocol_nos(), create_protocol_no(), get_protocol_no(), list_protocol_nos(), _parse_issued_time(), protocol_no_exists() (+7 more)
### Community 19 - "Community 19"
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.22
Nodes (13): create_collection_region(), create_hospital(), get_collection_region(), get_hospital(), _hospital_names_by_region_ids(), _hospital_to_dict(), list_collection_regions(), list_hospitals() (+5 more)
### Community 21 - "Community 21"
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"
### Community 22 - "Community 22"
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.27
Nodes (11): _build_tree(), _cascade_soft_delete(), _collect_with_ancestors(), create_dept(), get_dept(), list_depts(), list_depts_tree(), 保留匹配节点及其全部上级,供关键词检索后仍能正确组树。 (+3 more)
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.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 26 - "Community 26"
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 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 28 - "Community 28"
Cohesion: 0.31
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()
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 29 - "Community 29"
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()
### Community 30 - "Community 30"
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 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 31 - "Community 31"
Cohesion: 0.29
Nodes (2): BaseModel, SoftDeleteModel
Cohesion: 0.27
Nodes (8): create_dict_data(), get_dict_data(), list_dict_data(), list_dict_data_options(), 字典数据 CRUD(sys_dict_data), _row_to_dict(), soft_delete_dict_data(), update_dict_data()
### 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()
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 33 - "Community 33"
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 34 - "Community 34"
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 35 - "Community 35"
Cohesion: 0.31
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 36 - "Community 36"
Cohesion: 0.31
Nodes (8): 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(), update_cell_sample_type()
### Community 37 - "Community 37"
Cohesion: 0.31
Nodes (8): create_output_type(), get_output_type(), list_output_types(), 工艺产出物类型 CRUD(biz_output_type), _row_to_dict(), soft_delete_output_type(), type_code_exists(), update_output_type()
### Community 38 - "Community 38"
Cohesion: 0.36
Nodes (7): create_post(), get_post(), list_posts(), post_code_exists(), _row_to_dict(), soft_delete_post(), update_post()
### Community 39 - "Community 39"
Cohesion: 0.36
Nodes (7): create_tenant(), get_tenant(), list_tenants(), _row_to_dict(), soft_delete_tenant(), tenant_no_exists(), update_tenant()
### Community 40 - "Community 40"
Cohesion: 0.36
Nodes (7): create_site(), get_site(), list_sites(), _row_to_dict(), site_code_exists(), soft_delete_site(), update_site()
### Community 41 - "Community 41"
Cohesion: 0.29
Nodes (2): BaseModel, SoftDeleteModel
### Community 42 - "Community 42"
Cohesion: 0.38
Nodes (6): derive_legacy_status_from_three(), derive_three_status_from_legacy(), 阶段A兼容:从旧 status 推导三字段。 约束:旧 status 本身混合了支付/配送/订单生命周期,因此只能尽力推断; 若业务侧已有更精确, 阶段A兼容:从三字段反推旧 status(用于旧前端/旧筛选参数兼容)。, ThreeStatus, _to_decimal()
### Community 43 - "Community 43"
Cohesion: 0.5
Nodes (3): BaseSettings, Config, Settings
### Community 34 - "Community 34"
### Community 44 - "Community 44"
Cohesion: 0.5
Nodes (2): _auto_fill_create_dept_id(), ORM before_insert 钩子:当 create_dept_id 为空时,自动从请求上下文填充当前用户部门 ID
### Community 35 - "Community 35"
### Community 45 - "Community 45"
Cohesion: 0.67
Nodes (2): get_menu_list(), 运营端菜单接口(与 web-admin 前端静态路由 sqxp.ts 对齐,5 个一级菜单分组)
### Community 46 - "Community 46"
Cohesion: 1.0
Nodes (1): 请求级别的用户上下文变量 通过 contextvars 在请求处理链路中传递当前用户信息, 供 SQLAlchemy 事件监听器等底层机制使用,避免逐层手动传
### Community 36 - "Community 36"
### Community 47 - "Community 47"
Cohesion: 1.0
Nodes (1): API 路由包(骨架:公共鉴权 + 静态菜单 + 运营端占位)
### Community 39 - "Community 39"
### Community 50 - "Community 50"
Cohesion: 1.0
Nodes (1): 发送订单状态通知 Args: openid: 用户openid ord
### Community 40 - "Community 40"
### Community 51 - "Community 51"
Cohesion: 1.0
Nodes (1): 发送支付成功通知 Args: openid: 用户openid ord
### Community 41 - "Community 41"
### Community 52 - "Community 52"
Cohesion: 1.0
Nodes (1): 发送配送通知 Args: openid: 配送员openid task
### Community 42 - "Community 42"
### Community 53 - "Community 53"
Cohesion: 1.0
Nodes (1): 发送配送时效预警 Args: openid: 配送员openid ta
### Community 43 - "Community 43"
### Community 54 - "Community 54"
Cohesion: 1.0
Nodes (1): 发送退款结果通知 Args: openid: 用户openid ord
### Community 44 - "Community 44"
### Community 55 - "Community 55"
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 31`** (8 nodes): `BaseModel`, `.to_dict()`, `get_active()`, `base.py`, `SoftDeleteModel`, `.restore()`, `.soft_delete()`, `__tablename__()`
- **Thin community `Community 41`** (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 34`** (4 nodes): `_auto_fill_create_dept_id()`, `get_db()`, `database.py`, `ORM before_insert 钩子:当 create_dept_id 为空时,自动从请求上下文填充当前用户部门 ID`
- **Thin community `Community 44`** (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 35`** (2 nodes): `context.py`, `请求级别的用户上下文变量 通过 contextvars 在请求处理链路中传递当前用户信息, 供 SQLAlchemy 事件监听器等底层机制使用,避免逐层手动传`
- **Thin community `Community 45`** (3 nodes): `get_menu_list()`, `menu.py`, `运营端菜单接口(与 web-admin 前端静态路由 sqxp.ts 对齐,5 个一级菜单分组)`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 36`** (2 nodes): `__init__.py`, `API 路由包(骨架:公共鉴权 + 静态菜单 + 运营端占位)`
- **Thin community `Community 46`** (2 nodes): `context.py`, `请求级别的用户上下文变量 通过 contextvars 在请求处理链路中传递当前用户信息, 供 SQLAlchemy 事件监听器等底层机制使用,避免逐层手动传`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 39`** (1 nodes): `发送订单状态通知 Args: openid: 用户openid ord`
- **Thin community `Community 47`** (2 nodes): `__init__.py`, `API 路由包(骨架:公共鉴权 + 静态菜单 + 运营端占位)`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 40`** (1 nodes): `发送支付成功通知 Args: openid: 用户openid ord`
- **Thin community `Community 50`** (1 nodes): `发送订单状态通知 Args: openid: 用户openid ord`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 41`** (1 nodes): `发送配送通知 Args: openid: 配送员openid task`
- **Thin community `Community 51`** (1 nodes): `发送支付成功通知 Args: openid: 用户openid ord`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 42`** (1 nodes): `发送配送时效预警 Args: openid: 配送员openid ta`
- **Thin community `Community 52`** (1 nodes): `发送配送通知 Args: openid: 配送员openid task`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 43`** (1 nodes): `发送退款结果通知 Args: openid: 用户openid ord`
- **Thin community `Community 53`** (1 nodes): `发送配送时效预警 Args: openid: 配送员openid ta`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 44`** (1 nodes): `发送催单通知给配送员 Args: deliver_openid: 配送员openid`
- **Thin community `Community 54`** (1 nodes): `发送退款结果通知 Args: openid: 用户openid ord`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 55`** (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 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._
- **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 12`, `Community 13`, `Community 18`, `Community 19`, `Community 24`, `Community 25`?**
_High betweenness centrality (0.109) - this node is a cross-community bridge._
- **Why does `generate_uuid()` connect `Community 3` to `Community 0`, `Community 4`, `Community 5`, `Community 6`, `Community 7`, `Community 8`, `Community 9`, `Community 10`, `Community 11`, `Community 12`, `Community 13`, `Community 15`, `Community 17`, `Community 18`, `Community 19`, `Community 20`, `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 36`, `Community 37`, `Community 38`, `Community 39`, `Community 40`?**
_High betweenness centrality (0.098) - this node is a cross-community bridge._
- **Why does `success_response()` connect `Community 0` to `Community 1`, `Community 6`, `Community 9`, `Community 11`, `Community 12`, `Community 45`, `Community 13`, `Community 14`?**
_High betweenness centrality (0.051) - this node is a cross-community bridge._
- **Are the 714 inferred relationships involving `str` (e.g. with `unhandled_exception_handler()` and `get_current_user_by_token()`) actually correct?**
_`str` has 714 INFERRED edges - model-reasoned connections that need verification._
- **Are the 657 inferred relationships involving `success_response()` (e.g. with `list_storage_release_reviews()` and `get_storage_release_review_detail()`) actually correct?**
_`success_response()` has 657 INFERRED edges - model-reasoned connections that need verification._
- **Are the 543 inferred relationships involving `error_response()` (e.g. with `_json_error_response()` and `list_storage_release_reviews()`) actually correct?**
_`error_response()` has 543 INFERRED edges - model-reasoned connections that need verification._
- **Are the 464 inferred relationships involving `SysUser` (e.g. with `数据权限过滤(V0.3:基于角色 data_scope 的查询级行级过滤) data_scope 值: 1 = 全部数据 2 = 本部门数据 3` and `从 SysUser 读取用户的 dept_id。`) actually correct?**
_`SysUser` has 464 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">4285 nodes &middot; 22669 edges &middot; 49 communities</div>
<div id="stats">4340 nodes &middot; 23048 edges &middot; 60 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": 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}];
const RAW_NODES = [{"id": "init_py", "label": "__init__.py", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "__init__.py", "community": 48, "community_name": "Community 48", "source_file": "__init__.py", "file_type": "code", "degree": 0}, {"id": "main_py", "label": "main.py", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "main.py", "community": 9, "community_name": "Community 9", "source_file": "main.py", "file_type": "code", "degree": 7}, {"id": "app_main_lifespan", "label": "lifespan()", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "lifespan()", "community": 13, "community_name": "Community 13", "source_file": "main.py", "file_type": "code", "degree": 4}, {"id": "app_main_error_json", "label": "_error_json()", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "_error_json()", "community": 9, "community_name": "Community 9", "source_file": "main.py", "file_type": "code", "degree": 3}, {"id": "app_main_sqlalchemy_exception_handler", "label": "sqlalchemy_exception_handler()", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "sqlalchemy_exception_handler()", "community": 9, "community_name": "Community 9", "source_file": "main.py", "file_type": "code", "degree": 4}, {"id": "app_main_unhandled_exception_handler", "label": "unhandled_exception_handler()", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "unhandled_exception_handler()", "community": 9, "community_name": "Community 9", "source_file": "main.py", "file_type": "code", "degree": 5}, {"id": "app_main_mask_secret", "label": "_mask_secret()", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "_mask_secret()", "community": 9, "community_name": "Community 9", "source_file": "main.py", "file_type": "code", "degree": 1}, {"id": "app_main_root", "label": "root()", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "root()", "community": 9, "community_name": "Community 9", "source_file": "main.py", "file_type": "code", "degree": 1}, {"id": "app_main_health_check", "label": "health_check()", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "health_check()", "community": 9, "community_name": "Community 9", "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": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "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": 9, "community_name": "Community 9", "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": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "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": 9, "community_name": "Community 9", "source_file": "main.py", "file_type": "rationale", "degree": 3}, {"id": "tasks_init_py", "label": "__init__.py", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "__init__.py", "community": 13, "community_name": "Community 13", "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": 855}, {"cid": 1, "color": "#F28E2B", "label": "Community 1", "count": 644}, {"cid": 2, "color": "#E15759", "label": "Community 2", "count": 349}, {"cid": 3, "color": "#76B7B2", "label": "Community 3", "count": 340}, {"cid": 4, "color": "#59A14F", "label": "Community 4", "count": 286}, {"cid": 5, "color": "#EDC948", "label": "Community 5", "count": 255}, {"cid": 6, "color": "#B07AA1", "label": "Community 6", "count": 244}, {"cid": 7, "color": "#FF9DA7", "label": "Community 7", "count": 241}, {"cid": 8, "color": "#9C755F", "label": "Community 8", "count": 179}, {"cid": 9, "color": "#BAB0AC", "label": "Community 9", "count": 174}, {"cid": 10, "color": "#4E79A7", "label": "Community 10", "count": 141}, {"cid": 11, "color": "#F28E2B", "label": "Community 11", "count": 71}, {"cid": 12, "color": "#E15759", "label": "Community 12", "count": 62}, {"cid": 13, "color": "#76B7B2", "label": "Community 13", "count": 58}, {"cid": 14, "color": "#59A14F", "label": "Community 14", "count": 50}, {"cid": 15, "color": "#EDC948", "label": "Community 15", "count": 38}, {"cid": 16, "color": "#B07AA1", "label": "Community 16", "count": 29}, {"cid": 17, "color": "#FF9DA7", "label": "Community 17", "count": 20}, {"cid": 18, "color": "#9C755F", "label": "Community 18", "count": 17}, {"cid": 19, "color": "#BAB0AC", "label": "Community 19", "count": 16}, {"cid": 20, "color": "#4E79A7", "label": "Community 20", "count": 15}, {"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": 13}, {"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": 11}, {"cid": 29, "color": "#BAB0AC", "label": "Community 29", "count": 10}, {"cid": 30, "color": "#4E79A7", "label": "Community 30", "count": 10}, {"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": 10}, {"cid": 37, "color": "#FF9DA7", "label": "Community 37", "count": 10}, {"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": 9}, {"cid": 41, "color": "#F28E2B", "label": "Community 41", "count": 8}, {"cid": 42, "color": "#E15759", "label": "Community 42", "count": 7}, {"cid": 43, "color": "#76B7B2", "label": "Community 43", "count": 4}, {"cid": 44, "color": "#59A14F", "label": "Community 44", "count": 4}, {"cid": 45, "color": "#EDC948", "label": "Community 45", "count": 3}, {"cid": 46, "color": "#B07AA1", "label": "Community 46", "count": 2}, {"cid": 47, "color": "#FF9DA7", "label": "Community 47", "count": 2}, {"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}];
// HTML-escape helper — prevents XSS when injecting graph data into innerHTML
function esc(s) {
+20275 -15232
View File
File diff suppressed because it is too large. Load diff
+17 -16
View File
@@ -26,9 +26,9 @@
"/Users/wuzilong/work/00code/zkxw/backend/app/core/api_crypto_middleware.py": 1778581508.2309763,
"/Users/wuzilong/work/00code/zkxw/backend/app/core/wechat_message.py": 1778578258.0458734,
"/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": 1782351467.7127345,
"/Users/wuzilong/work/00code/zkxw/backend/app/models/production_material.py": 1782404882.9262497,
"/Users/wuzilong/work/00code/zkxw/backend/app/models/qc_param.py": 1782379593.174925,
"/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py": 1782381881.8049347,
"/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,
@@ -74,16 +74,16 @@
"/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": 1782350322.6331227,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task.py": 1782381972.0363982,
"/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/qc_task_result.py": 1782379630.9640372,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/outbound_release_review.py": 1781603141.940902,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_material_stock_check.py": 1781423988.9720724,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/material_stock_batch.py": 1781521998.393167,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/post.py": 1779265648.38155,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception_check_result.py": 1779255909.8554008,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_material_stock_batch.py": 1781513705.4688208,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_material_stock_batch.py": 1782404955.550011,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/user.py": 1780539127.1805215,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_work_order.py": 1781696877.7453213,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/material.py": 1781522283.6438622,
@@ -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": 1782360197.8427687,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py": 1782404282.3374326,
"/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,
@@ -138,23 +138,24 @@
"/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": 1782350337.3184292,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_work_order.py": 1782399959.6748888,
"/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/env_detect_record.py": 1782376492.2365673,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/outbound_release_material.py": 1781612877.0137436,
"/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,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_sms_notification.py": 1781063495.0317714,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py": 1781532303.452117,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py": 1782377432.963105,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/consumable_pack.py": 1779265715.416005,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_inspection_batch.py": 1782384683.8310702,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_material_prep.py": 1781514953.3551314,
"/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": 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/sample_reception_lab_code.py": 1782377429.191194,
"/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,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/consumable_stock_log.py": 1779515477.284446,
@@ -162,7 +163,7 @@
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/base.py": 1778578258.062207,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/protocol_donation_convert.py": 1779161468.6582925,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/protocol_sample_collection.py": 1781063441.1529672,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py": 1779265680.3885922,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_item_param.py": 1782380067.962216,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/leave.py": 1779265839.500242,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/output_type.py": 1779265703.7363207,
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/exhibition_appointment.py": 1778801075.5211918,
@@ -171,8 +172,8 @@
"/Users/wuzilong/work/00code/zkxw/backend/app/api/outbound_release_review.py": 1779968232.5759368,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py": 1782122639.96012,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/material.py": 1781522000.6097813,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/production_material.py": 1781513734.218474,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py": 1782120920.0829217,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/production_material.py": 1782404982.2474263,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py": 1782405787.3654518,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/market.py": 1781522283.6823432,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/collection_protocol.py": 1782195334.2335806,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/cleanroom_garment.py": 1781347256.2326367,
@@ -189,7 +190,7 @@
"/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,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py": 1781145712.2433698,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/env_detect.py": 1782376501.8919773,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/research_donation.py": 1782195482.219445,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/ethnic.py": 1779266243.9511197,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/pilot_checkup.py": 1779935751.2658868,
@@ -198,7 +199,7 @@
"/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": 1782362266.599568,
"/Users/wuzilong/work/00code/zkxw/backend/app/api/production.py": 1782403704.9644573,
"/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,
@@ -0,0 +1,3 @@
-- 环境检测登记:新增结果时间
ALTER TABLE `biz_env_detect_record`
ADD COLUMN `result_time` datetime DEFAULT NULL COMMENT '结果时间' AFTER `detect_time`;
@@ -0,0 +1,3 @@
-- 生产物料入库批次:增加生产日期
ALTER TABLE `biz_production_material_stock_batch`
ADD COLUMN `production_date` date DEFAULT NULL COMMENT '生产日期' AFTER `expire_date`;
@@ -0,0 +1,4 @@
-- 检测项目参数模板:单选类型支持选项列表与默认值
ALTER TABLE `biz_qc_item_param`
ADD COLUMN `options` varchar(2000) DEFAULT NULL COMMENT '单选选项列表,JSON数组格式' AFTER `param_type`,
ADD COLUMN `default_value` varchar(500) DEFAULT NULL COMMENT '单选默认值' AFTER `options`;
@@ -0,0 +1,46 @@
-- 质检管理 · 质检物料库:新增「查看单价金额」按钮权限(F 类型,仅角色权限树勾选)
INSERT INTO `sys_menu` (
`id`,
`menu_name`,
`parent_id`,
`menu_type`,
`path`,
`component`,
`perms`,
`is_frame`,
`visible`,
`status`,
`tenant_no`,
`order_num`,
`remark`,
`create_by`,
`create_dept_id`,
`create_time`,
`update_by`,
`update_time`,
`del_flag`
)
SELECT
'70000000000000000000000000000090',
'查看单价金额',
'70000000000000000000000000000009',
'F',
'',
'',
'biz:qcMaterial:viewAmount',
1,
'0',
'1',
'zkxw',
1,
'控制质检物料库各 Tab 列表、查看弹窗与导出中的单价、金额字段展示',
NULL,
'',
NOW(),
NULL,
NOW(),
0
FROM DUAL
WHERE NOT EXISTS (
SELECT 1 FROM `sys_menu` WHERE `id` = '70000000000000000000000000000090'
);
@@ -0,0 +1,83 @@
-- 历史质检工单:为每工单创建默认检测批次 B01,并回填 batch_id
-- 注意:按租户/环境执行前请备份;若已存在批次可跳过对应工单
INSERT INTO `biz_qc_work_order_inspection_batch` (
`id`, `work_order_id`, `order_source`, `batch_no`, `batch_name`,
`tenant_no`, `order_num`, `remark`, `create_by`, `create_dept_id`,
`create_time`, `update_by`, `update_time`, `del_flag`
)
SELECT
UUID() AS id,
wo.id AS work_order_id,
CASE wo.work_order_type
WHEN 'temporary' THEN 'temporary'
WHEN 'outbound' THEN 'outbound'
ELSE 'inbound'
END AS order_source,
CONCAT(wo.work_order_no, '-B01') AS batch_no,
CONCAT(DATE_FORMAT(COALESCE(wo.create_time, NOW()), '%Y%m%d'), '检测') AS batch_name,
wo.tenant_no,
1 AS order_num,
'历史数据迁移默认批次' AS remark,
wo.create_by,
COALESCE(wo.create_dept_id, '') AS create_dept_id,
NOW() AS create_time,
wo.update_by,
NOW() AS update_time,
0 AS del_flag
FROM `biz_qc_work_order` wo
WHERE wo.del_flag = 0
AND wo.work_order_status IN ('ypz', 'ywc')
AND NOT EXISTS (
SELECT 1 FROM `biz_qc_work_order_inspection_batch` b
WHERE b.work_order_id = wo.id AND b.del_flag = 0
);
-- 回填实验室明细 batch_id
UPDATE `biz_qc_work_order_lab_item` li
INNER JOIN `biz_qc_work_order_inspection_batch` b
ON b.work_order_id = li.work_order_id AND b.del_flag = 0 AND b.batch_no LIKE '%-B01'
SET li.batch_id = b.id
WHERE li.del_flag = 0 AND (li.batch_id IS NULL OR li.batch_id = '');
-- 回填第三方明细 batch_id
UPDATE `biz_qc_work_order_third_item` ti
INNER JOIN `biz_qc_work_order_inspection_batch` b
ON b.work_order_id = ti.work_order_id AND b.del_flag = 0 AND b.batch_no LIKE '%-B01'
SET ti.batch_id = b.id
WHERE ti.del_flag = 0 AND (ti.batch_id IS NULL OR ti.batch_id = '');
-- 回填任务 batch_id
UPDATE `biz_qc_task` t
INNER JOIN `biz_qc_work_order_inspection_batch` b
ON b.work_order_id = t.work_order_id AND b.del_flag = 0 AND b.batch_no LIKE '%-B01'
SET t.batch_id = b.id
WHERE t.del_flag = 0 AND (t.batch_id IS NULL OR t.batch_id = '');
-- 从任务负责人迁移批次负责人(去重)
INSERT INTO `biz_qc_batch_assignee` (
`id`, `batch_id`, `user_id`, `tenant_no`, `order_num`,
`create_by`, `create_dept_id`, `create_time`, `update_by`, `update_time`, `del_flag`
)
SELECT
UUID(),
b.id,
ta.user_id,
b.tenant_no,
0,
b.create_by,
b.create_dept_id,
NOW(),
b.update_by,
NOW(),
0
FROM `biz_qc_work_order_inspection_batch` b
INNER JOIN `biz_qc_task` t ON t.batch_id = b.id AND t.del_flag = 0
INNER JOIN `biz_qc_task_assignee` ta ON ta.task_id = t.id AND ta.del_flag = 0
WHERE b.del_flag = 0
AND NOT EXISTS (
SELECT 1 FROM `biz_qc_batch_assignee` ba
WHERE ba.batch_id = b.id AND ba.user_id = ta.user_id AND ba.del_flag = 0
)
GROUP BY b.id, ta.user_id, b.tenant_no, b.create_by, b.create_dept_id, b.update_by;
+54
View File
@@ -0,0 +1,54 @@
-- 质检检测批次:新表 + 明细 batch_id + 历史数据迁移
-- 1. 检测批次主表
CREATE TABLE IF NOT EXISTS `biz_qc_work_order_inspection_batch` (
`id` varchar(64) NOT NULL COMMENT '主键ID',
`work_order_id` varchar(64) NOT NULL COMMENT '工单ID',
`order_source` varchar(20) NOT NULL DEFAULT 'inbound' COMMENT '工单来源',
`batch_no` varchar(32) NOT NULL COMMENT '批次编号',
`batch_name` varchar(200) NOT NULL COMMENT '批次展示名',
`tenant_no` varchar(64) NOT NULL DEFAULT 'zkxw' COMMENT '租户编号',
`order_num` int(11) DEFAULT NULL 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 '删除标记',
PRIMARY KEY (`id`),
KEY `idx_qc_wo_batch_wo` (`work_order_id`,`del_flag`),
KEY `idx_qc_wo_batch_no` (`tenant_no`,`batch_no`,`del_flag`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='质检工单检测批次';
-- 2. 批次负责人
CREATE TABLE IF NOT EXISTS `biz_qc_batch_assignee` (
`id` varchar(64) NOT NULL COMMENT '主键',
`batch_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) DEFAULT NULL COMMENT '创建人部门ID',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人id',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`del_flag` tinyint(4) NOT NULL DEFAULT '0' COMMENT '删除标记',
PRIMARY KEY (`id`),
KEY `idx_qc_batch_assignee_batch` (`batch_id`,`del_flag`),
KEY `idx_qc_batch_assignee_user` (`user_id`,`tenant_no`,`del_flag`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='质检检测批次负责人';
-- 3. 明细表增加 batch_id
ALTER TABLE `biz_qc_work_order_lab_item`
ADD COLUMN `batch_id` varchar(64) DEFAULT NULL COMMENT '检测批次ID' AFTER `order_source`;
ALTER TABLE `biz_qc_work_order_third_item`
ADD COLUMN `batch_id` varchar(64) DEFAULT NULL COMMENT '检测批次ID' AFTER `order_source`;
ALTER TABLE `biz_qc_task`
ADD COLUMN `batch_id` varchar(64) DEFAULT NULL COMMENT '检测批次ID' AFTER `work_order_id`;
-- 4. 历史数据迁移:每工单默认批次 B01(需在应用层或下列 SQL 按环境执行)
-- 见 migrate_qc_inspection_batch_data.sql
+4
View File
@@ -1413,6 +1413,7 @@ CREATE TABLE `biz_env_detect_record` (
`id` varchar(64) NOT NULL COMMENT '主键ID',
`record_no` varchar(32) NOT NULL COMMENT '登记编号',
`detect_time` datetime NOT NULL COMMENT '检测时间',
`result_time` datetime DEFAULT NULL COMMENT '结果时间',
`inspector_ids` varchar(500) NOT NULL COMMENT '检测人用户ID,逗号分隔',
`inspector_names` varchar(500) NOT NULL COMMENT '检测人姓名快照,逗号分隔',
`env_detect_item_id` varchar(64) NOT NULL COMMENT '检测项目ID',
@@ -3177,6 +3178,7 @@ CREATE TABLE `biz_production_material_stock_batch` (
`stock_in_no` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '入库编号(租户内唯一)',
`stock_in_date` date NOT NULL COMMENT '入库日期',
`expire_date` date NOT NULL COMMENT '过期日期',
`production_date` date DEFAULT NULL COMMENT '生产日期',
`catalog_no` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '货号',
`batch_no` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '批号',
`manufacturer` varchar(200) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '厂家',
@@ -4168,6 +4170,8 @@ CREATE TABLE `biz_qc_item_param` (
`param_code` varchar(64) NOT NULL COMMENT '参数编码',
`param_unit` varchar(32) DEFAULT NULL COMMENT '单位',
`param_type` varchar(16) NOT NULL DEFAULT 'text' COMMENT 'number/text/select',
`options` varchar(2000) DEFAULT NULL COMMENT '单选选项列表,JSON数组格式',
`default_value` varchar(500) DEFAULT NULL COMMENT '单选默认值',
`required_flag` char(1) NOT NULL DEFAULT '0' COMMENT '1必填 0非必填',
`tenant_no` varchar(64) NOT NULL DEFAULT 'zkxw' COMMENT '租户编号',
`order_num` int(11) DEFAULT NULL COMMENT '排序号',
@@ -0,0 +1,66 @@
# 质检工单「所有」Tab 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:** Add an「所有」tab before category tabs on QC work order and My QC Task pages, with shared filters, per-row type tags, and cross-type list support.
**Architecture:** Extend `QcWorkOrderTabKey` with `all`; backend `list_work_orders` supports cross-type keyword and excludes hidden clinical type; frontend uses standalone flat cards with `resolveCardMetaForRow()` and dynamic view dialogs.
**Tech Stack:** Python/FastAPI backend, Vue 3 + Element Plus frontend
**Spec:** `docs/superpowers/specs/2026-06-25-qc-work-order-all-tab-design.md`
---
### Task 1: Tab utils + category helpers
**Files:**
- Modify: `web-admin/src/utils/qc-work-order-tabs.ts`
- Create: `web-admin/src/utils/qc-work-order-category.ts`
- [ ] Add `all` tab config, default tab, category options, `toAllTabListQuery()`
- [ ] Add `qcWorkOrderCategoryLabel/TagType`, trigger filter value helpers
### Task 2: Card meta for all tab
**Files:**
- Modify: `web-admin/src/utils/qc-work-order-card-meta.ts`
- [ ] Add `all` config, `resolveCardMetaForRow()`, meta field subsets per type
### Task 3: Backend list API
**Files:**
- Modify: `backend/app/crud/qc_work_order.py`
- [ ] Cross-type keyword (outbound join when `work_order_type` empty)
- [ ] Exclude `clinical_application` when listing all types
- [ ] All-tab trigger source values (`temp_production`, `temp_manual`, `production` non-temp)
### Task 4: Page composable + mine filters
**Files:**
- Modify: `web-admin/src/views/quality-inspection/qc-work-order/composables/useQcWorkOrderTabPage.ts`
- Modify: `web-admin/src/utils/qc-mine-task-groups.ts`
- [ ] `qcCategory` in searchForm, list query mapping, mine task fetch without type limit
### Task 5: Tab panel filters + view dialog
**Files:**
- Modify: `web-admin/src/views/quality-inspection/qc-work-order/tabs/QcWorkOrderTabPanel.vue`
- Create: `web-admin/src/views/quality-inspection/qc-work-order/tabs/AllTab.vue`
- Modify: `web-admin/src/views/quality-inspection/qc-work-order/tabs/tab-registry.ts`
### Task 6: Card components
**Files:**
- Modify: `web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderSingleCard.vue`
- Modify: `web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderCardList.vue`
- [ ] Type tag, per-row meta, work order no title on all tab
### Task 7: Verify
- [ ] Update spec status to 已确认
- [ ] `./scripts/update-graph.sh frontend` (and backend if needed)
@@ -0,0 +1,777 @@
# 入库质检工单 · 协议单多样本 Tab 分组 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:** 在细胞存储质检、科研捐赠质检两个 Tab 中,将同一协议单下多个样本质检工单合并为一张协议组卡片,组内用 Tab 切换样本,样本内保留触发来源 Tab。
**Architecture:** 前端新增 `groupInboundQcWorkOrdersByProtocol()` 与 `QcWorkOrderProtocolGroup.vue` 作为协议外层;复用 `QcWorkOrderSampleGroup` 作为样本内层(`hideContextBar`);`QcWorkOrderCardList` 在 `inbound-grouped` 布局时渲染协议组。后端 `list_work_orders` 关键词增加 `protocol_no` 匹配。两页(manage/mine)共用 CardList,mine 关键词补 `protocolNo`。
**Tech Stack:** Python 3.9 / FastAPI / SQLAlchemy(backend);Vue 3 / TypeScript / Element Plus(web-admin)
**Spec:** `docs/superpowers/specs/2026-06-25-qc-work-order-protocol-group-design.md`
---
## File Map
| 文件 | 职责 |
|------|------|
| `web-admin/src/utils/qc-work-order-groups.ts` | 协议分组模型、`groupInboundQcWorkOrdersByProtocol`、gate 工单/进度统计 helper |
| `web-admin/src/utils/qc-work-order-card-meta.ts` | 协议组头副标题、协议状态 Tag、组级 `paymentSummary` 选取 |
| `web-admin/src/utils/qc-work-order-tabs.ts` | 入库 Tab 关键词 placeholder 含协议号/捐赠单号 |
| `web-admin/src/utils/qc-mine-task-groups.ts` | mine 前端关键词增加 `protocolNo` |
| `web-admin/src/views/.../QcWorkOrderProtocolGroup.vue` | **新建** 协议组卡片(组头 + 样本 Tab + 内嵌 SampleGroup) |
| `web-admin/src/views/.../QcWorkOrderSampleGroup.vue` | `hideContextBar`、`hideProtocolFeeSummary` prop |
| `web-admin/src/views/.../QcWorkOrderSingleCard.vue` | 尊重 `hideProtocolFeeSummary` |
| `web-admin/src/views/.../QcWorkOrderCardList.vue` | inbound 布局改用 `QcWorkOrderProtocolGroup` |
| `backend/app/crud/qc_work_order.py` | 列表关键词 OR 增加 `protocol_no.like` |
**不修改:** `buildAllTabListItems`(「所有」Tab)、出库/临时 Tab、`buildMineCardListModel`(mine 走 flat rows + CardList 分组)。
---
### Task 1: 协议分组工具函数
**Files:**
- Modify: `web-admin/src/utils/qc-work-order-groups.ts`
- [ ] **Step 1: 在文件末尾、`buildAllTabListItems` 之前追加类型与 helper**
在 `isGateTriggerSource` 之后、`buildAllTabListItems` 之前插入:
```typescript
export function resolveInboundProtocolGroupKey(row: QcWorkOrder): string {
const id = (row.collectionProtocolId || '').trim()
if (id) return id
const no = (row.protocolNo || '').trim()
if (no) return `protocol-no:${no}`
return row.id
}
export function resolveInboundSampleGroupKey(row: QcWorkOrder): string {
return (row.sampleCode || '').trim() || row.id
}
/** 样本组内用于组头/Tab 状态的 gate 工单(接收优先,否则首条) */
export function resolveGateWorkOrderForSampleGroup(
workOrders: QcWorkOrder[]
): QcWorkOrder | undefined {
if (!workOrders.length) return undefined
return (
workOrders.find((w) => isGateTriggerSource(w.triggerSource)) ||
workOrders[0]
)
}
export type QcWorkOrderProtocolGroupModel = {
groupKey: string
protocolId: string
protocolNo: string
receptionSource: 'cell_storage' | 'research_donation'
sampleGroups: QcWorkOrderSampleGroupModel[]
workOrders: QcWorkOrder[]
}
function protocolGroupSortTime(group: QcWorkOrderProtocolGroupModel): string {
let max = ''
for (const wo of group.workOrders) {
const t = String(wo.createTime || '')
if (t > max) max = t
}
return max
}
/** 入库 Tab:按协议单分组,组内再按样本分组 */
export function groupInboundQcWorkOrdersByProtocol(
rows: QcWorkOrder[]
): QcWorkOrderProtocolGroupModel[] {
const map = new Map<string, QcWorkOrder[]>()
for (const row of rows) {
const key = resolveInboundProtocolGroupKey(row)
const list = map.get(key)
if (list) list.push(row)
else map.set(key, [row])
}
const groups = Array.from(map.entries()).map(([groupKey, workOrders]) => {
const sampleGroups = groupInboundQcWorkOrders(workOrders)
const head = workOrders[0]
const source =
(head?.receptionSource || 'cell_storage') === 'research_donation'
? 'research_donation'
: 'cell_storage'
return {
groupKey,
protocolId: (head?.collectionProtocolId || '').trim(),
protocolNo: (head?.protocolNo || '').trim(),
receptionSource: source as 'cell_storage' | 'research_donation',
sampleGroups,
workOrders
}
})
return groups.sort(
(a, b) => protocolGroupSortTime(b).localeCompare(protocolGroupSortTime(a))
)
}
/** 协议组进度:已配置样本数 / 样本总数(按 gate 工单 workOrderStatus !== dpz) */
export function countConfiguredSamplesInProtocolGroup(
group: QcWorkOrderProtocolGroupModel
): { configured: number; total: number } {
const total = group.sampleGroups.length
let configured = 0
for (const sg of group.sampleGroups) {
const gate = resolveGateWorkOrderForSampleGroup(sg.workOrders)
if (gate && gate.workOrderStatus !== 'dpz') configured += 1
}
return { configured, total }
}
```
- [ ] **Step 2: 本地 smoke(Node REPL 或临时 script,可选)**
在 `web-admin` 目录用两条 mock 工单(同 `collectionProtocolId`、不同 `sampleCode`)调用 `groupInboundQcWorkOrdersByProtocol`,期望返回 1 个 group、`sampleGroups.length === 2`。
- [ ] **Step 3: Commit**
```bash
git add web-admin/src/utils/qc-work-order-groups.ts
git commit -m "feat(web-admin): add inbound QC work order protocol grouping utils"
```
---
### Task 2: 协议组头 card-meta helper
**Files:**
- Modify: `web-admin/src/utils/qc-work-order-card-meta.ts`
- [ ] **Step 1: 增加 import 与 helper**
文件顶部 import 区增加:
```typescript
import {
countConfiguredSamplesInProtocolGroup,
type QcWorkOrderProtocolGroupModel
} from '@/utils/qc-work-order-groups'
import type { QcProtocolPaymentSummary } from '@/api/qc-work-order'
```
在 `buildDonationGroupContext` 之后追加:
```typescript
export function buildProtocolGroupProgressLabel(
group: QcWorkOrderProtocolGroupModel
): string {
const { configured, total } = countConfiguredSamplesInProtocolGroup(group)
if (total <= 0) return ''
if (configured <= 0) return ''
return `${configured}/${total} 已配置`
}
export function resolveProtocolGroupStatus(row: QcWorkOrder): {
label: string
tagType: TagType
} {
const status = String(row.protocolStatus || '')
const source = row.receptionSource || 'cell_storage'
return {
label: protocolStatusLabelBySource(status, source),
tagType: protocolStatusTagTypeBySource(status, source)
}
}
/** 协议组级费用摘要:取组内首条有 paymentSummary 的 cell_storage 工单 */
export function resolveProtocolGroupPaymentSummary(
group: QcWorkOrderProtocolGroupModel
): QcProtocolPaymentSummary | null {
if (group.receptionSource !== 'cell_storage') return null
for (const wo of group.workOrders) {
if (wo.paymentSummary) return wo.paymentSummary
}
return null
}
```
- [ ] **Step 2: Commit**
```bash
git add web-admin/src/utils/qc-work-order-card-meta.ts
git commit -m "feat(web-admin): add protocol group card meta helpers for inbound QC"
```
---
### Task 3: SampleGroup / SingleCard 支持隐藏组头与卡片级协议费用
**Files:**
- Modify: `web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderSampleGroup.vue`
- Modify: `web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderSingleCard.vue`
- [ ] **Step 1: `QcWorkOrderSampleGroup.vue` — props + 条件渲染**
`defineProps` 增加:
```typescript
hideContextBar?: boolean
hideProtocolFeeSummary?: boolean
```
模板中 `qc-sample-row__ctx` 外层加 `v-if="!hideContextBar"`。
`QcWorkOrderSingleCard` 传参增加:
```vue
:hide-protocol-fee-summary="hideProtocolFeeSummary"
```
- [ ] **Step 2: `QcWorkOrderSingleCard.vue` — prop + computed**
props 增加:
```typescript
hideProtocolFeeSummary?: boolean
```
修改 `showProtocolFeeSummary` computed:
```typescript
const showProtocolFeeSummary = computed(
() =>
!props.hideProtocolFeeSummary &&
effectiveCardMeta.value.showProtocolFeeSummary &&
!!props.row.paymentSummary
)
```
- [ ] **Step 3: 浏览器快速确认**
打开细胞存储 Tab,在改动 CardList 前行为应不变(默认 prop false)。
- [ ] **Step 4: Commit**
```bash
git add web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderSampleGroup.vue \
web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderSingleCard.vue
git commit -m "feat(web-admin): allow hiding sample context bar and card-level protocol fee"
```
---
### Task 4: 新建 `QcWorkOrderProtocolGroup.vue`
**Files:**
- Create: `web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderProtocolGroup.vue`
- [ ] **Step 1: 创建组件(完整骨架)**
```vue
<!-- 质检工单 · 同协议多样本分组(协议组头 + 样本 Tab + SampleGroup) -->
<template>
<article class="qc-protocol-row">
<div class="qc-protocol-row__ctx">
<span class="qc-protocol-row__idx">{{ groupIndex }}</span>
<span class="qc-protocol-row__code">{{ group.protocolNo || '—' }}</span>
<el-tag v-if="protocolStatus.label" :type="protocolStatus.tagType" size="small">
{{ protocolStatus.label }}
</el-tag>
<span class="qc-protocol-row__sub">
{{ sampleCountLabel }}
<template v-if="progressLabel"> · {{ progressLabel }}</template>
</span>
<span v-if="group.workOrders.length > 1" class="qc-protocol-row__badge">
{{ group.workOrders.length }} 条质检
</span>
</div>
<QcWorkOrderProtocolPaymentPanel
v-if="protocolPaymentSummary"
:summary="protocolPaymentSummary"
compact
class="qc-protocol-row__fee"
/>
<div
v-if="group.sampleGroups.length > 1"
class="qc-protocol-sample-tabs"
>
<div class="qc-protocol-sample-tabs__row" role="tablist" aria-label="同协议样本">
<button
v-for="(sampleGroup, tabIndex) in group.sampleGroups"
:key="sampleTabKey(sampleGroup)"
type="button"
role="tab"
class="qc-wo-indep-tabs__btn"
:class="{ 'is-active': sampleTabKey(sampleGroup) === activeSampleKey }"
:aria-selected="sampleTabKey(sampleGroup) === activeSampleKey"
@click="activeSampleKey = sampleTabKey(sampleGroup)"
>
<span class="qc-wo-tab__index">{{ tabIndex + 1 }}</span>
<span class="qc-wo-tab__no">{{ sampleGroup.sampleCode || '—' }}</span>
<span v-if="sampleTypeLabel(sampleGroup)" class="qc-wo-tab__source qc-wo-tab__source--reception">
{{ sampleTypeLabel(sampleGroup) }}
</span>
<el-tag
v-if="gateWorkOrder(sampleGroup)"
:type="workOrderStatusTagType(gateWorkOrder(sampleGroup)!.workOrderStatus)"
size="small"
class="qc-wo-tab__status"
>
{{ workOrderStatusLabel(gateWorkOrder(sampleGroup)!.workOrderStatus) }}
</el-tag>
</button>
</div>
</div>
<QcWorkOrderSampleGroup
v-if="activeSampleGroup"
:group="activeSampleGroup"
:group-index="groupIndex"
:card-meta-config="cardMetaConfig"
:task-refresh-token="taskRefreshToken"
:show-work-order-ops="showWorkOrderOps"
:show-work-order-view-only="showWorkOrderViewOnly"
:task-list-mode="taskListMode"
:preset-tasks-by-work-order-id="presetTasksByWorkOrderId"
hide-context-bar
:hide-protocol-fee-summary="!!protocolPaymentSummary"
@configure="(row) => emit('configure', row)"
@view="(row) => emit('view', row)"
@command="(cmd, row) => emit('command', cmd, row)"
@tasks-changed="emit('tasks-changed')"
/>
</article>
</template>
<script setup lang="ts">
import { ref, computed, watch } from 'vue'
import type { QcWorkOrder } from '@/api/qc-work-order'
import type { QcTask } from '@/api/qc-task'
import { workOrderStatusLabel } from '@/utils/qc-labels'
import type { TagType } from '@/utils/status-tag-registry'
import {
type QcWorkOrderProtocolGroupModel,
type QcWorkOrderSampleGroupModel,
resolveGateWorkOrderForSampleGroup,
resolveInboundSampleGroupKey
} from '@/utils/qc-work-order-groups'
import type { QcWorkOrderCardMetaConfig } from '@/utils/qc-work-order-card-meta'
import {
buildProtocolGroupProgressLabel,
resolveProtocolGroupPaymentSummary,
resolveProtocolGroupStatus
} from '@/utils/qc-work-order-card-meta'
import QcWorkOrderSampleGroup from './QcWorkOrderSampleGroup.vue'
import QcWorkOrderProtocolPaymentPanel from './QcWorkOrderProtocolPaymentPanel.vue'
defineOptions({ name: 'QcWorkOrderProtocolGroup' })
const props = withDefaults(
defineProps<{
group: QcWorkOrderProtocolGroupModel
groupIndex: number
cardMetaConfig: QcWorkOrderCardMetaConfig
taskRefreshToken: number
showWorkOrderOps?: boolean
showWorkOrderViewOnly?: boolean
taskListMode?: 'workOrder' | 'mine'
presetTasksByWorkOrderId?: Record<string, QcTask[]>
}>(),
{
showWorkOrderOps: true,
showWorkOrderViewOnly: false,
taskListMode: 'workOrder'
}
)
const emit = defineEmits<{
configure: [row: QcWorkOrder]
view: [row: QcWorkOrder]
command: [cmd: string, row: QcWorkOrder]
'tasks-changed': []
}>()
function sampleTabKey(sampleGroup: QcWorkOrderSampleGroupModel) {
return sampleGroup.groupKey || resolveInboundSampleGroupKey(sampleGroup.workOrders[0])
}
const activeSampleKey = ref(
props.group.sampleGroups[0] ? sampleTabKey(props.group.sampleGroups[0]) : ''
)
watch(
() => props.group.sampleGroups.map((g) => sampleTabKey(g)).join(','),
() => {
const keys = props.group.sampleGroups.map((g) => sampleTabKey(g))
if (!keys.includes(activeSampleKey.value)) {
activeSampleKey.value = keys[0] || ''
}
}
)
const activeSampleGroup = computed(() =>
props.group.sampleGroups.find((g) => sampleTabKey(g) === activeSampleKey.value)
)
const gateRow = computed(() =>
resolveGateWorkOrderForSampleGroup(
props.group.sampleGroups[0]?.workOrders || props.group.workOrders
)
)
const protocolStatus = computed(() =>
gateRow.value
? resolveProtocolGroupStatus(gateRow.value)
: { label: '', tagType: 'info' as TagType }
)
const sampleCountLabel = computed(() => `${props.group.sampleGroups.length} 个样本`)
const progressLabel = computed(() => buildProtocolGroupProgressLabel(props.group))
const protocolPaymentSummary = computed(() =>
resolveProtocolGroupPaymentSummary(props.group)
)
function sampleTypeLabel(sampleGroup: QcWorkOrderSampleGroupModel) {
const gate = resolveGateWorkOrderForSampleGroup(sampleGroup.workOrders)
return (gate?.sampleTypeName || sampleGroup.workOrders[0]?.sampleTypeName || '').trim()
}
function gateWorkOrder(sampleGroup: QcWorkOrderSampleGroupModel) {
return resolveGateWorkOrderForSampleGroup(sampleGroup.workOrders)
}
function workOrderStatusTagType(status: string): TagType {
if (status === 'ywc') return 'success'
if (status === 'ypz') return 'warning'
if (status === 'yzf') return 'danger'
return 'info'
}
</script>
```
- [ ] **Step 2: 追加 scoped SCSS**
在 `</script>` 后追加样式(复用 `QcWorkOrderSampleGroup` 的 pill Tab 类名,协议组头对齐 `qc-sample-row__ctx`):
```scss
<style scoped lang="scss">
.qc-protocol-row {
display: flex;
flex-direction: column;
min-width: 0;
border: 1px solid #e2e8f0;
border-radius: 10px;
background: #fff;
overflow: hidden;
}
.qc-protocol-row__ctx {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px 12px;
padding: 12px 16px;
background: linear-gradient(180deg, var(--primary-bg, #ecfdf5) 0%, #fff 100%);
border-bottom: 1px solid #d1fae5;
}
.qc-protocol-row__idx {
flex-shrink: 0;
font-size: 12px;
color: var(--mint-text-muted, #64748b);
min-width: 20px;
}
.qc-protocol-row__code {
font-size: 14px;
font-weight: 600;
color: #1e293b;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
}
.qc-protocol-row__sub {
flex: 1;
min-width: 0;
font-size: 12px;
color: var(--mint-text-muted, #64748b);
}
.qc-protocol-row__badge {
flex-shrink: 0;
font-size: 11px;
font-weight: 600;
padding: 2px 10px;
border-radius: 999px;
background: var(--primary-bg, #ecfdf5);
color: #047857;
border: 1px solid #d1fae5;
}
.qc-protocol-row__fee {
border-bottom: 1px solid #e2e8f0;
}
.qc-protocol-sample-tabs {
padding: 12px 16px;
border-bottom: 1px solid #e2e8f0;
background: #f8fafc;
}
.qc-protocol-sample-tabs__row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 24px;
max-width: 100%;
}
/* 将 QcWorkOrderSampleGroup.vue 中 .qc-wo-indep-tabs__btn 及相关 pill Tab 样式复制到此处 */
</style>
```
- [ ] **Step 3: Commit**
```bash
git add web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderProtocolGroup.vue
git commit -m "feat(web-admin): add protocol group card with sample tabs for inbound QC"
```
---
### Task 5: `QcWorkOrderCardList` 接入协议分组
**Files:**
- Modify: `web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderCardList.vue`
- [ ] **Step 1: import 与 groupedRows 分支**
script 区:
```typescript
import {
groupInboundQcWorkOrders,
groupInboundQcWorkOrdersByProtocol,
groupOutboundQcWorkOrders,
buildAllTabListItems,
type QcWorkOrderAllTabListItem
} from '@/utils/qc-work-order-groups'
import QcWorkOrderProtocolGroup from './QcWorkOrderProtocolGroup.vue'
```
新增 computed:
```typescript
const protocolGroupedRows = computed(() => {
if (!cardMetaUsesInboundGroups(props.cardMetaConfig.tabKey)) return []
return groupInboundQcWorkOrdersByProtocol(props.rows)
})
```
- [ ] **Step 2: 模板 — inbound 分支改用 ProtocolGroup**
将 `useGroupedList` 分支中:
```vue
<QcWorkOrderSampleGroup
v-for="(group, idx) in groupedRows"
```
替换为:
```vue
<QcWorkOrderProtocolGroup
v-if="cardMetaUsesInboundGroups(cardMetaConfig.tabKey)"
v-for="(group, idx) in protocolGroupedRows"
:key="group.groupKey"
:group="group"
:group-index="cardIndex(idx)"
...
/>
<QcWorkOrderSampleGroup
v-else
v-for="(group, idx) in groupedRows"
...
/>
```
或更简洁:`groupedRows` computed 内 inbound 返回空数组,单独 `protocolGroupedRows` 仅 inbound 使用;outbound 仍用 `groupedRows`。
推荐改 `groupedRows`:
```typescript
const groupedRows = computed(() => {
if (cardMetaUsesOutboundGroups(props.cardMetaConfig.tabKey)) {
return groupOutboundQcWorkOrders(props.rows)
}
return []
})
```
inbound 专用 `protocolGroupedRows` 见 Step 1。
- [ ] **Step 3: 浏览器验证 manage 页**
URL: `http://localhost:3006/#/quality-group/quality-manage/qc-work-order?tab=inbound_cell_storage`
期望:同协议多样本 → 1 张协议卡 + 样本 Tab;单样本 → 无样本 Tab 行。
- [ ] **Step 4: Commit**
```bash
git add web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderCardList.vue
git commit -m "feat(web-admin): render inbound QC lists as protocol groups with sample tabs"
```
---
### Task 6: 关键词 placeholder + mine 前端过滤
**Files:**
- Modify: `web-admin/src/utils/qc-work-order-tabs.ts`
- Modify: `web-admin/src/utils/qc-mine-task-groups.ts`
- [ ] **Step 1: `keywordPlaceholderForTab`**
将 inbound 默认 return 改为:
```typescript
if (tab.key === 'inbound_research_donation') {
return '工单号/捐赠单号/样本编码/入库编码'
}
if (tab.group === 'inbound') {
return '工单号/协议号/样本编码/入库编码'
}
```
(放在 `tab.group === 'outbound'` 判断之前,且 `inbound_research_donation` 先于 general inbound。)
- [ ] **Step 2: `keywordMatchesWorkOrder` inbound 字段**
将 inbound 组 fields 数组改为:
```typescript
: [row.workOrderNo, row.protocolNo, row.sampleCode, row.labStorageCode]
```
- [ ] **Step 3: Commit**
```bash
git add web-admin/src/utils/qc-work-order-tabs.ts web-admin/src/utils/qc-mine-task-groups.ts
git commit -m "feat(web-admin): support protocol number in inbound QC keyword filters"
```
---
### Task 7: 后端列表关键词 — `protocol_no`
**Files:**
- Modify: `backend/app/crud/qc_work_order.py`
- [ ] **Step 1: 在 `list_work_orders` 关键词 OR 中追加**
定位 `_filtered()` 内(约 L1272):
```python
keyword_conditions = [
BizQcWorkOrder.work_order_no.like(like),
BizQcWorkOrder.sample_code.like(like),
BizQcWorkOrder.protocol_no.like(like),
]
```
- [ ] **Step 2: 手动 API 验证**
```bash
cd backend && source venv/bin/activate
# 假设本地服务已起;替换 TOKEN 与协议号
curl -s "http://127.0.0.1:8000/api/web/admin/qc-work-orders?page=1&pageSize=10&workOrderType=inbound&receptionSource=cell_storage&keyword=XY2026" \
-H "Authorization: Bearer <token>" | python -m json.tool | head -40
```
期望:返回列表中 `protocolNo` 含关键词的工单。
- [ ] **Step 3: Commit**
```bash
git add backend/app/crud/qc_work_order.py
git commit -m "feat(backend): include protocol number in QC work order list keyword search"
```
---
### Task 8: 联调回归与文档收尾
**Files:**
- Modify: `docs/superpowers/specs/2026-06-25-qc-work-order-protocol-group-design.md`(状态 → 已实现)
- [ ] **Step 1: 前端 lint**
```bash
cd web-admin && pnpm lint
```
期望:无新增 error。
- [ ] **Step 2: 手工回归清单(两页)**
| # | 场景 | 页面 | 期望 |
|---|------|------|------|
| 1 | 同协议 2+ 样本 | 质检工单 · 细胞存储 | 1 协议卡,样本 Tab 可切换 |
| 2 | 同协议 1 样本 | 同上 | 无样本 Tab,直接工单区 |
| 3 | 同样本 接收+生产 | 同上 | 内层触发来源 Tab 正常 |
| 4 | 协议费用 | 细胞存储 | 组头一次,卡片内不重复 |
| 5 | 科研捐赠 | 科研捐赠 Tab | 无费用条,捐赠单号组头 |
| 6 | 关键词 | 两页 | 协议号/捐赠单号可搜 |
| 7 | 所有 Tab | 两页 | 仍平铺,无协议分组 |
| 8 | 出库/临时 Tab | 质检工单 | 布局不变 |
| 9 | 我的质检任务 | mine · 细胞存储 | 与 manage 分组一致,仅查看 |
- [ ] **Step 3: 更新知识图谱**
```bash
./scripts/update-graph.sh frontend
./scripts/update-graph.sh backend
```
- [ ] **Step 4: 更新 spec 状态**
`docs/superpowers/specs/2026-06-25-qc-work-order-protocol-group-design.md` 头部 `状态:已实现`。
- [ ] **Step 5: Commit**
```bash
git add docs/superpowers/specs/2026-06-25-qc-work-order-protocol-group-design.md
git commit -m "docs: mark inbound QC protocol group spec as implemented"
```
---
## Spec Coverage Checklist
| Spec § | Task |
|--------|------|
| §2 三层结构 / 分组键 | Task 1 |
| §3.1 协议组头 | Task 2, 4 |
| §3.2 样本 Tab | Task 4 |
| §3.3 hideContextBar + 内层 SampleGroup | Task 3, 4 |
| §3 协议费用组级一次 | Task 2, 3, 4 |
| §4.4 所有 Tab 不变 | 不修改 `buildAllTabListItems` |
| §4.5 mine 共用 + protocolNo 关键词 | Task 5, 6 |
| §5.1 后端 protocol_no | Task 7 |
| §5.2 placeholder | Task 6 |
| §7 验收 1–9 | Task 8 |
---
## Plan complete
Saved to `docs/superpowers/plans/2026-06-25-qc-work-order-protocol-group.md`.
**Two execution options:**
1. **Subagent-Driven (recommended)** — 按 Task 1→8 派发子 agent,每 Task 完成后 review
2. **Inline Execution** — 本会话内用 `executing-plans` 连续实现,Task 5 完成后浏览器 checkpoint
需要哪种方式开始写代码?
@@ -0,0 +1,326 @@
# 质检检测批次 — 设计文档
> 日期: 2026-06-25
> 状态: **待评审 / 待实现**
> 范围: web-admin + backend(质检工单、我的质检任务、配置检测 / 调整检测)
> 前置: `2026-05-17-quality-inspection-modules-design.md`、`2026-06-12-qc-task-assignee-my-tasks-design.md`(负责人粒度将变更)
## 1. 背景与目标
### 1.1 现状问题
- 配置检测时,同一工单内 **每个检测主数据项目只能选一次**(`_sync_work_order_*` 以 `lab_test_item_id` / `third_party_item_id` 为唯一键)。
- 负责人按 **每个检测项目** 独立指派,配置弹窗右侧「负责人配置」与左侧项目一一对应。
- 工单展开区、我的质检任务中,列表 **平铺展示每条质检任务**(≈ 每个检测项目一张卡片)。
### 1.2 目标
引入 **检测批次** 作为工单与检测项目之间的容器层:
- 一个质检工单可配置 **多个检测批次**;
- 每个批次内勾选 lab / 第三方项目(**批次内项目不重复**;**跨批次允许重复同一主数据项目**);
- 每个批次配置 **多名负责人**,负责该批次下全部项目的任务;
- `ypz`(已配置)后仍可 **追加批次**(调整检测);
- 批次 **无独立状态机**,仅作分组容器;任务仍保留 `dzx / jxz / ywc`;
- 列表 UI:**先展示批次,展开后展示批次内检测项目/任务**。
---
## 2. 已确认的产品决策(2026-06-25)
| # | 问题 | 决策 |
|---|------|------|
| 1 | 批次负责人 | **允许多人**;完成语义沿用现网:**任一负责人可独立开始/填写结果/完成** |
| 2 | 同主数据项目跨批次重复 | **允许**(例如批次 1、批次 2 均可含「无菌检测」) |
| 3 | 同批次内同一项目重复 | **不允许** |
| 4 | 批次内 lab + 第三方混合 | **允许** |
| 5 | 配置时机 | **`dpz` 首次可配多批次**;**`ypz` 后可追加批次**(调整检测) |
| 6 | 批次独立状态 | **否** — 批次仅作容器;不增加批次级开始/完成 |
| 7 | 批次命名 | **自动编号** + 展示名规则:**`YYYYMMDD检测`**(创建日);可选附带序号区分同日多批次,见 §4.2 |
### 2.1 与 2026-06-12 负责人设计的差异
| 项 | 2026-06-12(已实现) | 本变更 |
|----|---------------------|--------|
| 指派粒度 | 每个检测项目 | **每个检测批次** |
| 存储 | `biz_qc_task_assignee` 按 task | 批次表 + `biz_qc_batch_assignee`;任务 assignee **从批次继承同步** |
| 配置 UI | 右栏按项目选负责人 | 每批次一块卡片,批次级多选负责人 |
任务级 `biz_qc_task_assignee` **保留**,作为「我的质检任务」查询与写鉴权的数据源;配置/调整批次负责人时 **写入或更新** 该批次下所有未删除任务的 assignee。
---
## 3. 目标架构
```
biz_qc_work_order
│
│ 1 : N
▼
biz_qc_work_order_inspection_batch ← 新增
│
│ 1 : N
├──────────────────────┐
▼ ▼
biz_qc_work_order_lab_item biz_qc_work_order_third_item
(+ batch_id) (+ batch_id)
│ │
│ 1 : 1 │ 1 : 1
▼ ▼
biz_qc_task
│
▼
biz_qc_task_assignee (从批次负责人继承)
```
**工单完成规则不变**:工单下 **全部任务** `ywc` → 工单 `ywc`。
**批次进度(仅展示)**:前端按批次聚合任务状态计算,如 `2/3 已完成`,不落库批次状态字段。
---
## 4. 数据模型
### 4.1 新表 `biz_qc_work_order_inspection_batch`
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | VARCHAR(64) | PK |
| `work_order_id` | VARCHAR(64) | 工单 ID |
| `order_source` | VARCHAR(20) | 与工单一致:`inbound` / `outbound` / `temporary` |
| `batch_no` | VARCHAR(32) | 批次编号,工单内唯一,如 `{work_order_no}-B01` |
| `batch_name` | VARCHAR(200) | 展示名:`YYYYMMDD检测`;同日多批次可加后缀 `-2` |
| `tenant_no` | VARCHAR(64) | 租户 |
| `order_num` | INT | 工单内排序 |
| 公共字段 | | `remark`, `create_by`, `create_dept_id`, `create_time`, `update_by`, `update_time`, `del_flag` |
索引:`(work_order_id, del_flag)`、`(tenant_no, batch_no, del_flag)`。
**不设** `batch_status` 字段。
### 4.2 批次编号与命名规则
- `batch_no`:`{work_order_no}-B` + 两位序号(`B01`, `B02`, …),工单内递增。
- `batch_name`:创建日 `YYYYMMDD` + 字面量 `检测` → 例:`20260625检测`。
- 若同一工单同一天创建多个批次:第二个起为 `20260625检测-2`、`20260625检测-3`(避免展示歧义)。
### 4.3 新表 `biz_qc_batch_assignee`
结构对齐 `biz_qc_task_assignee`:
| 字段 | 说明 |
|------|------|
| `batch_id` | FK → 批次 |
| `user_id` | 负责人 |
| `tenant_no`, `order_num`, 公共字段 | |
唯一性:应用层保证 `batch_id + user_id` 未删除不重复。
### 4.4 改表现有明细表
**`biz_qc_work_order_lab_item`**、**`biz_qc_work_order_third_item`** 各增加:
| 字段 | 说明 |
|------|------|
| `batch_id` | VARCHAR(64) NOT NULL(迁移后) |
**唯一性变更**:
- 旧:工单内 `lab_test_item_id` 唯一(应用层 dict 键)。
- 新:**批次内** `lab_test_item_id` 唯一;**跨批次可重复**。
- 第三方:**批次内** `third_party_item_id` 唯一。
可选 DB 唯一索引(软删场景用应用层校验):
- `UNIQUE (batch_id, lab_test_item_id)` where del_flag=0(MySQL 5.7 无部分索引,**以应用层校验为准**)。
### 4.5 任务表(可选冗余)
`biz_qc_task` 可增加 `batch_id` 冗余,便于按批次列表查询;非必须(可通过 `source_line_id` JOIN 明细得 `batch_id`)。
### 4.6 历史数据迁移
对已配置工单(存在 lab/third 明细且 `del_flag=0`):
1. 每个工单插入 **默认批次 1**(`batch_no` = `{wo}-B01`,`batch_name` = 配置日或迁移日 `YYYYMMDD检测`)。
2. 现有明细行 `batch_id` 指向该批次。
3. 批次负责人:取该批次下各任务现有 `biz_qc_task_assignee` 的 **并集** 写入 `biz_qc_batch_assignee`;任务 assignee 保持不变。
---
## 5. API 设计
### 5.1 配置 / 调整检测(扩展或 v2)
**推荐**:扩展 `POST /web/admin/qc-work-orders/{id}/configure`,body 改为批次数组;**废弃**扁平 `labItemIds` + `labItemAssignees` 作为唯一入口(可保留兼容一版,内部转批次)。
```typescript
type WorkOrderConfigureBody = {
batches: Array<{
id?: string // 调整时:已有批次传 id;新增批次省略
assigneeUserIds: string[] // ≥1
hasLab: boolean
hasThirdParty: boolean
labItemIds: string[] // 批次内不重复
thirdPartyItemIds: string[]
}>
}
```
**校验**:
| 规则 | 说明 |
|------|------|
| 工单状态 | `dpz` 或 `ypz` 均可提交(`ypz` = 追加/调整批次) |
| 每批次 | 至少 1 种检测方式且对应项目非空 |
| 批次负责人 | 每批次 `assigneeUserIds.length ≥ 1` |
| 批次内项目 | lab / third 列表各自无重复 ID |
| 删除批次 | 若批次内存在 `jxz` 或 `ywc` 任务 → **禁止删除整批**(或仅允许删全 `dzx` 的批次,与现网删项规则对齐) |
| 移除批次内项目 | 沿用现网:进行中/已完成对应任务不可删 |
**同步逻辑**(替代 `_sync_work_order_lab_items` 扁平键):
- 按 `batch_id` 维度比对 lab/third 明细行;
- 新增行 → `create_task_for_line` + 从批次 assignee 写入 `biz_qc_task_assignee`;
- 保留行 → 更新 `order_num`;**同步任务 assignee 为批次当前负责人集合**;
- 删除行 → 软删明细 + 软删任务 + 软删任务 assignee。
**首次配置**(`dpz`):提交后工单 → `ypz`,写 `configured_time` / `configured_by`。
**追加批次**(`ypz`):仅新增批次及其明细/任务;不改 `configured_time` 或更新 `update_time` 即可。
### 5.2 批次列表(供展开 UI)
`GET /web/admin/qc-work-orders/{id}/inspection-batches`
响应示例:
```json
{
"list": [
{
"id": "...",
"batchNo": "ZJ202606250001-B01",
"batchName": "20260625检测",
"orderNum": 1,
"assigneeUserIds": ["..."],
"assigneeUserNames": "张三、李四",
"taskCount": 3,
"completedTaskCount": 1,
"tasks": [ /* 与现有 task 列表项结构一致 */ ]
}
]
}
```
或在工单详情中内嵌 `batches` 字段,减少请求数。
### 5.3 我的质检任务
`GET /qc-tasks/mine`:**不变** — 仍按 `biz_qc_task_assignee` 过滤。
批次负责人变更时 **必须同步** 该批次下任务的 assignee,保证「我的任务」可见性正确。
---
## 6. 前端改造
### 6.1 配置检测 / 调整检测弹窗(`qc-work-order/index.vue`)
**由「左项目 + 右负责人」改为「批次卡片列表」**:
```
┌─ 批次 1 · 20260625检测 ──────────────────┐
│ 负责人: [多选用户] *必填 │
│ □ 实验室自检 □ 第三方检测 │
│ 项目多选区(可混合 lab + third) │
│ [删除批次] │
└──────────────────────────────────────────┘
[+ 添加批次]
```
- `dpz`:可一次添加多批次后提交。
- `ypz`:打开「调整检测」→ 回显已有批次 + 允许「添加批次」;已有批次可改负责人、增删项目(受任务状态约束)。
### 6.2 工单展开 / 我的质检任务列表
**层级**:工单 → **批次行(可折叠)** → 任务卡片(检测项目)。
新建或改造组件,建议 `QcWorkOrderBatchSubList.vue`:
- 批次头:批次名、`batchNo`、负责人 Tag、进度 `n/m`、任务状态 Tag 聚合(如全完成显示 success)。
- 展开:复用现有 `QcWorkOrderTaskSubList` 任务卡片(传入 `presetTasks` 为该批次 tasks)。
**我的质检任务**:`buildMineCardListModel` 由 `工单 → tasks[]` 改为 `工单 → batches[] → tasks[]`;筛选/关键词仍可按任务字段匹配。
### 6.3 UI 模式
- 配置弹窗:参考 P21 + 多卡片表单(可参考生产工单多步骤配置)。
- 批次折叠列表:参考 `el-collapse` 或现有 kanban 子列表样式;遵守 Fresh Mint 规范。
---
## 7. 权限与操作(沿用现网)
| 场景 | 规则 |
|------|------|
| 工单页督办 | 持有 `biz:qcTask:list` 可操作全部任务 |
| 我的任务页 | 仅 `task.assignee` 可开始/填结果 |
| 配置/调整检测 | 持有 `biz:qcWorkOrder:list` |
批次无独立操作按钮;开始/完成仍在任务卡片上。
---
## 8. 风险与缓解
| 风险 | 缓解 |
|------|------|
| 与 2026-06-12「按项目指派」文档冲突 | 本 spec supersede 指派粒度;迁移脚本合并 assignee 到默认批次 |
| 同项目跨批次导致任务/参数混淆 | 任务仍绑定 **明细行 ID** + 主数据快照;参数按 `item_source + item_id` 查模板,与现网一致 |
| 调整批次负责人后「我的任务」不一致 | 配置 API 内 `_sync_task_assignees_from_batch` 批量更新 |
| 配置弹窗复杂度上升 | 批次卡片 + 校验提示;单测覆盖重复项校验 |
| 工作量 | 分阶段:① 数据模型 + 配置 API ② 配置 UI ③ 列表批次化 ④ 迁移 |
---
## 9. 实现阶段建议
| 阶段 | 内容 | 预估 |
|------|------|------|
| P1 | DDL、Model、迁移脚本、批次 CRUD、configure API 重写 | 3~4 人天 |
| P2 | 配置/调整检测弹窗 UI | 2~3 人天 |
| P3 | 工单展开 + 我的任务列表批次化 | 2~3 人天 |
| P4 | 联调、回归六 Tab、历史数据验证 | 1~2 人天 |
| **合计** | | **8~12 人天** |
---
## 10. 验收场景(摘要)
1. `dpz` 工单一次配置 2 个批次,每批不同负责人、不同项目组合 → 工单 `ypz`,任务数 = 各批项目数之和。
2. 两批次均含同一 lab 项目 → 生成 2 条明细、2 条任务,参数模板相同、结果独立填写。
3. 同批次内勾选重复 lab 项目 → 前端 + 后端拒绝。
4. 单批次同时含 lab + third 项目 → 正常生成 `sysz` + `dsfjc` 任务。
5. `ypz` 工单追加批次 3 → 仅新增批次 3 的任务,不影响已完成任务。
6. 修改批次负责人 → 该批次未完成任务在新负责人「我的任务」中可见。
7. 工单列表展开:显示批次列表;展开批次显示项目任务卡片。
8. 历史工单迁移后:显示为「批次 1」,行为与迁移前一致。
---
## 11. 开放项(可选,不阻塞开工)
- 批次 `remark` 是否在 UI 暴露(默认隐藏)。
- 删除「空批次」(无项目)是否允许在保存时自动剔除。
- 第三方费用合计:工单级汇总不变;是否在批次头展示该批 third 费用小计。
---
## 12. 下一步
1. **评审本文档** — 确认无遗漏后标记 `状态: 已批准`。
2. **OpenSpec 变更** — 建议 capability:`qc-inspection-batch`,与 `api-conventions` / `database-conventions` 对齐。
3. **实现计划** — 使用 `writing-plans` 拆 `tasks.md` 后进入开发。
如需进入实现,请明确:**是否按本文档直接创建 OpenSpec proposal + 实现计划**。
@@ -0,0 +1,243 @@
# 质检工单「所有」Tab — 设计文档
> 日期:2026-06-25
> 状态:已确认
> 范围:`web-admin` 【质检工单】【我的质检任务】;后端列表 API 跨类型关键词增强
> 关联:`2026-06-16-qc-work-order-tab-split-design.md`、`2026-06-12-qc-task-assignee-my-tasks-design.md`
---
## 1. 背景与目标
### 1.1 需求
客户在现有 5 个分类 Tab(细胞存储 / 科研捐赠 / 中试服务 / 基础实验 / 临时)**之前**新增 **「所有」** Tab,用于跨类型总览质检工单。
### 1.2 目标
- 【质检工单】【我的质检任务】**均**增加「所有」Tab,置于 Tab 栏首位。
- 「所有」Tab 展示 5 类可见工单(不含 hidden 的临床应用)。
- 卡片标题行增加**质检类型 Tag**(细胞存储 / 科研捐赠 / 中试服务 / 基础实验 / 临时)。
- 筛选区采用**精简共用字段**;mine 页在共用基础上增加任务相关筛选。
### 1.3 非目标
- 不在「所有」Tab 提供「新增临时工单」工具栏(仍仅在「临时质检」Tab)。
- 不在「所有」Tab 复用样本分组 / 输注计划分组布局(混排时分组规则冲突)。
- 不在本期实现出库工单的输注/交付时间筛选(保留在各分类 Tab);「所有」Tab 统一使用**创建时间**。
- 不展示 hidden 的「临床应用」类型(与现有 5 Tab 策略一致)。
---
## 2. Tab 配置变更
### 2.1 新增 Tab Key
| Tab Key | 展示标签 | 位置 | 列表布局 |
|---------|----------|------|----------|
| `all` | 所有 | 首位(默认 Tab) | `standalone`(平铺,不分组) |
### 2.2 Tab 栏顺序(两页一致)
```
所有 | 细胞存储质检 | 科研捐赠质检 | 中试服务质检 | 基础实验质检 | 临时质检
```
### 2.3 默认 Tab
- **新默认:** `all`(替换原 `inbound_cell_storage`)。
- 路由:`?tab=all`;旧链接 `?tab=inbound` 等兼容映射保持不变。
### 2.3 类型 Tag 映射(卡片展示)
复用 `resolveTabKeyFromWorkOrder(row)`,映射为短标签:
| `resolveTabKeyFromWorkOrder` 结果 | Tag 文案 | Tag 语义色 |
|-----------------------------------|----------|------------|
| `inbound_cell_storage` | 细胞存储 | `primary` |
| `inbound_research_donation` | 科研捐赠 | `primary` |
| `outbound_pilot_service` | 中试服务 | `info` |
| `outbound_basic_experiment` | 基础实验 | `info` |
| `temporary` | 临时 | `warning` |
`outbound_clinical_application` 不在「所有」Tab 展示(hidden Tab 策略延续)。
---
## 3. 筛选区设计
### 3.1 【质检工单】manage 模式 — 主区域
| 字段 | 控件 | 说明 |
|------|------|------|
| 质检类型 | `el-select` | 全部 / 细胞存储 / 科研捐赠 / 中试服务 / 基础实验 / 临时;映射为 `workOrderType` + `receptionSource` / `bizType` 组合传给 API |
| 关键词 | `el-input` | placeholder:`工单号 / 样本编码 / 申请单号 / 产品名称等` |
| 工单状态 | `el-select` | 待配置 / 已配置 / 已完成 / 已作废 |
| 创建时间 | `el-date-picker` daterange | 统一按 `createTimeStart` / `createTimeEnd` |
**hint 文案:** `出库工单按输注/交付时间筛选请进入对应分类 Tab。`
### 3.2 【质检工单】manage 模式 — 展开更多
| 字段 | 控件 | 选项 |
|------|------|------|
| 工单来源 | `el-select` | 样本接收 / 计划质检 / 生产过程 / 生产派发 / 手动创建 |
工单来源筛选逻辑:
- `样本接收` → `triggerSource=reception`
- `计划质检` → `triggerSource=schedule`
- `生产过程` → `triggerSource=production`(非临时)
- `生产派发` → 临时工单且 `triggerSource=production`
- `手动创建` → 临时工单且 `triggerSource≠production`
### 3.3 【我的质检任务】mine 模式 — 主区域
与 manage **一致**,额外增加:
| 字段 | 控件 | 说明 |
|------|------|------|
| 任务状态 | `el-select` | 待执行 / 进行中 / 已完成 |
| 任务类型 | `el-select` | 实验室自检 / 第三方检测 |
主区域栅格:关键词、工单状态、任务状态、创建时间(第一行);任务类型可放展开区或与工单状态同行(实现时按 4 列栅格自适应,窄屏折行)。
**hint 文案:** `仅展示指派给当前登录人的质检任务。出库工单按输注/交付时间筛选请进入对应分类 Tab。`
### 3.4 【我的质检任务】mine 模式 — 展开更多
与 manage 一致:工单来源。
### 3.5 质检类型 → API 参数映射
| 选项 | API 参数 |
|------|----------|
| 全部 | 不传 `workOrderType` / `receptionSource` / `bizType` |
| 细胞存储 | `workOrderType=inbound` + `receptionSource=cell_storage` |
| 科研捐赠 | `workOrderType=inbound` + `receptionSource=research_donation` |
| 中试服务 | `workOrderType=outbound` + `bizType=pilot_service` |
| 基础实验 | `workOrderType=outbound` + `bizType=basic_experiment` |
| 临时 | `workOrderType=temporary` |
当用户在「所有」Tab 内选了具体质检类型,行为等价于在该子类型下筛选,但列表仍为平铺 + 类型 Tag 布局。
---
## 4. 列表与卡片
### 4.1 列表布局
- `all` Tab:`listLayout = standalone`,**不使用** `groupInboundQcWorkOrders` / `groupOutboundQcWorkOrders`。
- 分页仍按**工单条数**(与临时 Tab 一致)。
### 4.2 卡片标题行
在现有「工单状态 Tag」「触发来源 Tag」之前或之后,增加**质检类型 Tag**(见 §2.3)。
- 卡片主标题:统一使用 **工单号** `workOrderNo`(不再按入库/出库规则切换为入库编码或产品编码)。
- `showTriggerTag`:保持 `true`(与分类 Tab 一致,展示样本接收/计划质检/生产过程等)。
### 4.3 卡片 meta 字段(按行动态)
根据 `resolveTabKeyFromWorkOrder(row)` 选取对应分类 Tab 的 `metaFields` 子集,**不整表复用**(避免出库 Tab 专有费用条等在「所有」Tab 误显)。
| 类型 | meta 字段(按顺序) |
|------|---------------------|
| 细胞存储 / 科研捐赠 | 质检对象、实验室入库编码、检测方式、创建时间 |
| 中试服务 | 申请单号、申请单名称、患者姓名、输注时间、检测方式 |
| 基础实验 | 申请单号、课题名称、客户名称、交付时间、检测方式 |
| 临时 | 产品名称、产品编码、检测方式、创建时间 |
备注:有值时展示(沿用 `visible` 规则);协议费用条、出库费用条在「所有」Tab **不展示**(`showProtocolFeeSummary=false`,`showOutboundFeeSummary=false`)。
### 4.4 细胞产品区块
- 中试服务 / 基础实验行:若 `triggerSource=schedule` 或 `production`,按原 Tab 规则展示 `showCellProducts` 区块。
- 其它类型:不展示细胞产品区块。
### 4.5 操作与查看弹窗
- manage:操作按钮规则按**行实际类型**的 `cardMetaConfig`(`allowEditTemporary`、`allowDelete` 等)动态判断。
- mine:仅查看工单 + 任务操作(与现有一致)。
- 查看弹窗:`QC_WORK_ORDER_VIEW_DIALOG_COMPONENTS[resolveTabKeyFromWorkOrder(row)]`。
---
## 5. 后端变更
### 5.1 跨类型关键词(必须)
`list_work_orders` 当 `work_order_type` 为空(「所有」+ 质检类型=全部)时,关键词应覆盖:
| 范围 | 匹配字段 |
|------|----------|
| 通用 | `work_order_no`、`sample_code` |
| 入库 | `lab_storage_code`(经 reception 关联,现有逻辑) |
| 出库 | 申请单号、申请单名称、患者、课题、客户(现有 `_outbound_qc_work_order_ids_by_keyword`) |
| 临时 | `work_order_no`、关联生产工单号、细胞产品名称/编码(若已 enrich) |
实现:当 `wt` 为空时,关键词 OR 条件合并入库 + 出库 + 临时字段(与分别传 `outbound` 时行为一致)。
### 5.2 质检类型子筛选
无需新 API 字段;前端将 §3.5 映射为现有 `workOrderType` / `receptionSource` / `bizType` 查询参数。
### 5.3 时间筛选
「所有」Tab 仅传 `createTimeStart` / `createTimeEnd`;不传 `infusionTimeStart` / `infusionTimeEnd`。
### 5.4 我的任务 — mine 数据流
`tabKey=all` 时:
1. `fetchAllMineTasks` 不按 `workOrderType` 限制(或传全类型 union)。
2. `filterMineEntriesByWorkOrderSearch`:`tabKey=all` 时跳过 `resolveTabKeyFromWorkOrder(row) !== tabKey` 校验;关键词/时间/工单状态/工单来源按行类型分别匹配(复用 `keywordMatchesWorkOrder` 时传入 `resolveTabKeyFromWorkOrder(row)`)。
3. 质检类型子筛选:前端 filter `resolveTabKeyFromWorkOrder(row)` 与所选子类型一致。
---
## 6. 前端实现要点
### 6.1 工具与类型
- `QcWorkOrderTabKey` 增加 `'all'`。
- `QC_WORK_ORDER_TABS` 首部插入 `all` 配置;`QC_WORK_ORDER_VISIBLE_TABS` 自动包含。
- `DEFAULT_QC_WORK_ORDER_TAB` → `'all'`。
- 新增 `qcWorkOrderCategoryLabel(row)` / `qcWorkOrderCategoryTagType(row)` 工具函数。
- 新增 `getAllTabCardMetaForRow(row)` 或 `CARD_META_BY_TAB.all` + 行级 meta 解析。
### 6.2 组件
- 新增 `AllTab.vue`(或复用 `QcWorkOrderTabPanel` + `tabKey=all`)。
- `QcWorkOrderSingleCard`:支持 `cardMetaConfig.tabKey === 'all'` 时按行解析 meta;标题行渲染类型 Tag。
- `QcWorkOrderCardList`:`all` 时 `useGroupedList=false`;可选传入 `resolveCardMetaPerRow`。
- `index.vue`(两页):Tab 列表改用含 `all` 的 visible tabs;默认 tab 解析更新。
### 6.3 注册表
- `tab-registry.ts`、`view-dialog-registry.ts`:`all` Tab 无独立 view dialog 组件;查看时按行 `resolveTabKeyFromWorkOrder` 动态选择(`QcWorkOrderTabPanel` 已有模式可扩展)。
---
## 7. 验收标准
1. 两页 Tab 栏首位为「所有」,默认进入「所有」Tab。
2. 「所有」Tab 列表含 5 类工单,每条卡片有正确类型 Tag。
3. 质检类型=全部时,关键词可搜到入库样本编码、出库申请单号、临时工单号。
4. 质检类型子筛选后,列表仅含所选类型。
5. 创建时间筛选在「所有」Tab 对所有类型按 `create_time` 生效。
6. mine 页任务状态/任务类型筛选与现有一致,且与「所有」Tab 共用结构。
7. 查看弹窗按工单实际类型打开正确 dialog。
8. 分类 Tab 行为与改动前一致(回归)。
---
## 8. 已确认决策
| 问题 | 决策 |
|------|------|
| 两页是否都加「所有」? | **是** — 筛选结构一致;mine 页多任务状态/任务类型 |
| 列表是否分组? | **否** — 平铺 standalone |
| 「所有」Tab 时间字段? | **创建时间**;出库输注/交付时间留在分类 Tab |
| 类型 Tag 文案? | 细胞存储 / 科研捐赠 / 中试服务 / 基础实验 / 临时 |
| 默认 Tab? | **所有** |
@@ -0,0 +1,261 @@
# 入库质检工单 · 协议单多样本 Tab 分组 — 设计文档
> 日期:2026-06-25
> 状态:已实现
> 范围:`web-admin` 【质检工单】【我的质检任务】入库类 Tab;后端列表关键词补协议号
> 关联:`2026-06-16-qc-work-order-tab-split-design.md`、`2026-06-25-qc-work-order-all-tab-design.md`、`2026-06-25-qc-inspection-batch-design.md`
---
## 1. 背景与目标
### 1.1 问题
细胞存储质检、科研捐赠质检的工单均来自**采集协议单 / 科赠协议单**。一份协议单可含**多个样本**,每个样本接收后各生成至少一条质检工单(生产过程可再追加)。
现网列表按 **`sampleCode`(样本编码)** 分组:同一协议下的多个样本各自占一张卡片,协议号等信息在组头副标题重复出现,用户难以「按协议一眼看全样本质检进度」。
### 1.2 目标
在 **细胞存储质检**、**科研捐赠质检** 两个 Tab 中,将同一协议单下的多个样本**合并为一张协议组卡片**,组内用 **Tab 切换样本**;样本内仍保留现网 **触发来源 Tab**(样本接收 / 生产过程)。
【质检工单】与【我的质检任务】**共用**同一套列表组件与分组逻辑。
### 1.3 非目标
- 不改变出库、临时 Tab 的列表布局。
- 不在 **「所有」** Tab 做协议分组(沿用平铺 + 类型 Tag,见 `2026-06-25-qc-work-order-all-tab-design.md` §1.3)。
- 不合并细胞存储与科研捐赠(仍分 Tab)。
- 不把同协议多样本压成**单层扁平卡片**(去掉样本层级)。
- 本期不做「按协议分页」后端改造(分页仍按工单条数;见 §5.3 边界说明)。
---
## 2. 列表信息架构
### 2.1 三层结构
```
协议组(新增外层)
└── 样本 Tab(新增中层)
└── 触发来源 Tab(现网 QcWorkOrderSampleGroup 内层)
└── 工单卡片 + 检测批次 + 任务
```
### 2.2 分组键
| 层级 | 分组字段 | 回退 |
|------|----------|------|
| 协议 | `collectionProtocolId` | `(protocolNo).trim()`,仍空则用首条工单 `id` |
| 样本 | `sampleCode` | 工单 `id` |
| 工单 | 同样本下按 `triggerSource` Tab 切换 | 现网逻辑 |
说明:科赠工单的 `collectionProtocolId` 存的是 `research_donation_id`(后端建单已如此),与细胞存储共用分组字段名。
### 2.3 Tab 配置
`listLayout` **保持** `inbound-grouped`(语义扩展为「入库分组列表」,实现改为协议优先分组,不再在 `QcWorkOrderCardList` 顶层直接使用 `QcWorkOrderSampleGroup`)。
受影响 Tab:
| Tab Key | 展示标签 | 列表布局(实现) |
|---------|----------|------------------|
| `inbound_cell_storage` | 细胞存储质检 | 协议分组 + 样本 Tab |
| `inbound_research_donation` | 科研捐赠质检 | 协议分组 + 样本 Tab |
---
## 3. UI 设计
### 3.1 协议组头(`qc-protocol-row__ctx`)
视觉对齐现网 `qc-sample-row__ctx`(绿渐变顶栏 + 序号)。
| 元素 | 细胞存储 | 科研捐赠 |
|------|----------|----------|
| 主标题 | 协议号 `protocolNo` | 捐赠单号 `protocolNo` |
| 副标题 | 协议状态 Tag + `N 个样本` + 可选进度摘要 | 捐赠状态 Tag + `N 个样本` + 可选进度摘要 |
| Badge | 组内工单总数(如 `5 条质检`) | 同左 |
**进度摘要(可选展示):** 如 `2/3 已配置`,按样本维度统计:该样本下存在 `workOrderStatus !== 'dpz'` 的 gate 工单(优先 `triggerSource=reception`,否则取组内首条)即计为「已配置样本」。无数据时不展示。
**协议费用条:** 仅 **细胞存储** Tab、且组内存在 `paymentSummary` 的工单时,在协议组头下方展示 **一次** `QcWorkOrderProtocolPaymentPanel`(compact),**不要**在每个样本 Tab 内重复。
### 3.2 样本 Tab 行(`qc-protocol-sample-tabs`)
- 样式复用现网 `qc-wo-indep-tabs__btn` / pill Tab(与 `QcWorkOrderSampleGroup` 触发来源 Tab 一致)。
- **仅当组内样本数 ≥ 2** 时展示 Tab 行;**单样本**时隐藏 Tab 行,直接展示该样本工单区。
每个 Tab pill 文案:
| 部分 | 内容 |
|------|------|
| 序号 | `1` `2` `3`(圆点序号,与现网一致) |
| 主文案 | 样本编码(monospace) |
| 辅助 | 样本类型 `sampleTypeName`(有值时) |
| 状态 | 该样本 gate 工单 `workOrderStatus` 的 `el-tag` |
Tab 排序:按样本编码字典序;同编码按 gate 工单 `createTime` ASC。
### 3.3 样本工单区
选中样本后渲染 **`QcWorkOrderSampleGroup`**,传入该样本下的 `workOrders` 子集,并增加 prop:
| Prop | 值 | 说明 |
|------|-----|------|
| `hideContextBar` | `true` | 隐藏原样本组头(协议组头 + 样本 Tab 已表达上下文) |
| 其余 | 与现网一致 | 内层触发来源 Tab、卡片、批次、任务 |
单样本且仅 1 条工单时:无样本 Tab、无触发来源 Tab,直接展示 `QcWorkOrderSingleCard`。
### 3.4 线框(ASCII)
```
┌─ 1 XY2026001 [协议进行中] 3 个样本 · 2/3 已配置 [5 条质检] ─┐
│ [协议费用 compact — 仅细胞存储] │
├─ 样本 Tab ───────────────────────────────────────────────────────┤
│ [1 A001·脐带血·待配置] [2 A002·胎盘·已配置] [3 B003·进行中] │
├─ 触发来源 Tab(样本 A001 有 2 条工单时)───────────────────────────┤
│ [样本接收 ZJ…] [生产过程 ZJ…] │
├─ 工单 meta + 检测批次 + 任务 ─────────────────────────────────────┤
└───────────────────────────────────────────────────────────────────┘
```
---
## 4. 前端实现要点
### 4.1 新增工具(`web-admin/src/utils/qc-work-order-groups.ts`)
```typescript
export type QcWorkOrderProtocolGroupModel = {
groupKey: string
protocolId: string
protocolNo: string
receptionSource: 'cell_storage' | 'research_donation'
sampleGroups: QcWorkOrderSampleGroupModel[]
workOrders: QcWorkOrder[] // 组内全部工单(扁平)
}
export function groupInboundQcWorkOrdersByProtocol(
rows: QcWorkOrder[]
): QcWorkOrderProtocolGroupModel[]
```
实现步骤:
1. 按 `collectionProtocolId`(及回退键)聚合工单。
2. 每组内调用现有 `groupInboundQcWorkOrders` 得到 `sampleGroups`。
3. 协议组排序:组内最新工单 `createTime DESC`(与列表默认排序一致)。
### 4.2 新增组件
**`QcWorkOrderProtocolGroup.vue`**
- Props / Emits 与 `QcWorkOrderSampleGroup` 对齐(`configure` / `view` / `command` / `tasks-changed`)。
- 内含样本 Tab 状态 `activeSampleKey`(`sampleCode` 或回退 `id`)。
- 渲染选中样本的 `QcWorkOrderSampleGroup`(`hideContextBar=true`)。
### 4.3 修改组件
| 文件 | 变更 |
|------|------|
| `QcWorkOrderSampleGroup.vue` | 新增 `hideContextBar?: boolean`,为 true 时不渲染 `qc-sample-row__ctx` |
| `QcWorkOrderCardList.vue` | `cardMetaUsesInboundGroups` 时渲染 `QcWorkOrderProtocolGroup` 而非直接 `QcWorkOrderSampleGroup` |
| `qc-work-order-card-meta.ts` | 新增 `buildProtocolGroupContextSubtitle(group)`;细胞存储/捐赠分别用协议/捐赠文案 |
### 4.4 「所有」Tab
**不变。** `buildAllTabListItems` 中 inbound 仍可按样本合并(或维持现状);本需求不修改 `all` Tab 布局。
### 4.5 我的质检任务
与 manage 共用 `QcWorkOrderCardList` + 协议分组;`pageMode='mine'` 时卡片仅查看、任务区 `taskListMode='mine'`。
前端 mine 关键词过滤补充 `protocolNo` 字段(`qc-mine-task-groups.ts` → `keywordMatchesWorkOrder`,inbound 组)。
---
## 5. 后端变更
### 5.1 列表关键词 — 协议号(必须)
`list_work_orders` 在现有关键词 OR 条件中增加:
```python
BizQcWorkOrder.protocol_no.like(like)
```
适用于入库类及「所有」Tab 跨类型搜索。
### 5.2 筛选 placeholder
入库 Tab 关键词 placeholder 更新为:
`工单号/协议号/样本编码/入库编码`
(细胞存储写「协议号」,科研捐赠写「捐赠单号」——按 Tab 文案区分。)
### 5.3 分页边界
- 分页单位仍为 **工单条数**(`pageSize` 默认 100)。
- 同一协议多样本可能**跨页**:当前页仅展示本页工单所属的样本 Tab;未出现在本页的样本不出 Tab。**可接受**,二期可选「按协议 ID 拉全量样本工单再分组」(非本期)。
---
## 6. 与检测批次的关系
检测批次在 **工单卡片内部**(`QcWorkOrderBatchSubList`),与协议/样本分组**正交**:
```
协议 → 样本 Tab → 触发来源 Tab → 工单 → 批次 → 任务
```
本需求不修改批次配置 API 与 UI。
---
## 7. 验收标准
1. 【质检工单】细胞存储 / 科研捐赠 Tab:同一 `collectionProtocolId` 下多样本合并为 **一张协议组卡片**。
2. 协议组内 **样本 Tab** 可切换;切换后展示对应样本工单与任务。
3. 同样本多工单(接收 + 生产)仍显示 **触发来源 Tab**,行为与改动前一致。
4. 单样本协议组 **不显示** 样本 Tab 行,直接展示工单区。
5. 细胞存储协议组 **协议费用条** 组级展示一次;科研捐赠不展示费用条。
6. 【我的质检任务】同上,且仅查看 + 任务操作。
7. 关键词输入协议号 / 捐赠单号可筛出该协议下全部相关工单。
8. 「所有」Tab、出库 Tab、临时 Tab 列表行为 **回归不变**。
9. 配置检测 / 调整检测 / 查看弹窗 **回归不变**。
---
## 8. 已确认决策
| 问题 | 决策 |
|------|------|
| 是否按协议合并多样本? | **是** |
| 样本切换交互? | **Tab(pill)**,复用现网触发来源 Tab 样式 |
| 单样本是否仍显示 Tab? | **否**,隐藏样本 Tab 行 |
| 两页是否一致? | **是** |
| 「所有」Tab 是否协议分组? | **否** |
| 分页是否改为按协议? | **否**(本期) |
| 协议费用条位置? | **协议组头一次**(仅细胞存储) |
---
## 9. 实现阶段建议
| 阶段 | 内容 | 预估 |
|------|------|------|
| P1 | `groupInboundQcWorkOrdersByProtocol` + `QcWorkOrderProtocolGroup` + SampleGroup `hideContextBar` | 1~1.5 人天 |
| P2 | CardList 接入 + card-meta 协议组头 + placeholder | 0.5 人天 |
| P3 | 后端 `protocol_no` 关键词 + mine 前端关键词 | 0.5 人天 |
| P4 | 两页联调、回归六 Tab + 所有 Tab | 0.5~1 人天 |
| **合计** | | **2.5~3.5 人天** |
---
## 10. 下一步
1. 评审本文档 — 无遗漏后进入实现计划(`writing-plans` → `docs/superpowers/plans/`)。
2. 可选:同步 OpenSpec capability `qc-work-order-protocol-group`(与 `ui-conventions` 卡片分组约定对齐)。
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-06-25
@@ -0,0 +1,36 @@
# 质检检测批次 — 技术设计
权威产品决策见 `docs/superpowers/specs/2026-06-25-qc-inspection-batch-design.md`。
## 架构
工单 → `biz_qc_work_order_inspection_batch` → lab/third 明细(`batch_id`)→ `biz_qc_task` → `biz_qc_task_assignee`(从 `biz_qc_batch_assignee` 同步)。
批次无状态字段;进度由任务聚合。
## 数据表
- `biz_qc_work_order_inspection_batch`
- `biz_qc_batch_assignee`
- `biz_qc_work_order_lab_item.batch_id`
- `biz_qc_work_order_third_item.batch_id`
- 可选 `biz_qc_task.batch_id` 冗余
## API
- `POST /qc-work-orders/{id}/configure` — body `batches[]`;扁平 `labItemIds` 兼容为单批次
- `GET /qc-work-orders/{id}/inspection-batches` — 批次 + tasks 嵌套
## 同步策略
按批次 ID 比对:增删改 lab/third 行;任务 assignee 调用 `sync_assignees_for_task` 与批次负责人一致。
## 前端
- `index.vue` 配置弹窗:批次卡片
- `QcWorkOrderBatchSubList.vue`:折叠批次 + 内嵌任务卡片
- `qc-mine-task-groups.ts`:工单下按批次分组
## 迁移
SQL 脚本为每个已配置工单创建 `B01` 默认批次并回填 `batch_id`。
@@ -0,0 +1,31 @@
## Why
质检工单配置检测时,每个检测主数据项目在同一工单内只能选一次,负责人需按项目逐一指派,列表平铺展示任务,无法满足客户「多批次、跨批重复项目、批次级负责人」的质检组织方式。
## What Changes
- 新增 **检测批次** 实体:工单下可配置多个批次,`ypz` 后可追加批次
- 批次内可选 lab + 第三方项目(批次内不重复,跨批次可重复同一主数据项目)
- **批次级多人负责人**,同步至批次内全部任务的 `biz_qc_task_assignee`
- **BREAKING**:配置检测 API 请求体改为 `batches[]`(保留扁平字段短期兼容转单批次)
- 新增 `GET /qc-work-orders/{id}/inspection-batches` 供列表展开
- 配置/调整检测弹窗改为批次卡片 UI
- 质检工单、我的质检任务:列表先展示批次,展开后展示检测项目/任务
- DDL + 历史数据迁移(默认批次 1)
## Capabilities
### New Capabilities
- `qc-inspection-batch`: 检测批次数据模型、配置/调整 API、批次列表 API、迁移与任务 assignee 同步
### Modified Capabilities
(无 `openspec/specs/` 基线;行为变更记录于本变更 spec)
## Impact
- **后端**:`models.py`、`qc_work_order.py`、`qc.py` API;新 `qc_inspection_batch.py` CRUD
- **SQL**:`docs/sql/` 新表 + alter + 迁移脚本
- **前端**:`qc-work-order/index.vue` 配置弹窗、`QcWorkOrderTaskSubList` / 新 `QcWorkOrderBatchSubList`、`qc-mine-task-groups.ts`、`qc-work-order.ts` API
- **文档**:`docs/superpowers/specs/2026-06-25-qc-inspection-batch-design.md` 为实现权威
@@ -0,0 +1,51 @@
### Requirement: 检测批次数据模型
系统 SHALL 提供 `biz_qc_work_order_inspection_batch` 与 `biz_qc_batch_assignee` 表;lab/third 明细表 SHALL 包含 `batch_id` 外键。
#### Scenario: 新建批次
- **WHEN** 用户在 `dpz` 或 `ypz` 工单上提交配置且包含新批次
- **THEN** 系统创建批次记录,`batch_no` 为 `{work_order_no}-Bnn`,`batch_name` 为 `YYYYMMDD检测`(同日多批递增 `-2` 后缀)
### Requirement: 批次内项目唯一与跨批重复
系统 SHALL 允许同一 `lab_test_item_id` / `third_party_item_id` 出现在同一工单的不同批次;同一批次内 SHALL NOT 重复同一主数据项目 ID。
#### Scenario: 批次内重复项目
- **WHEN** 提交的配置在同一批次 lab 列表含重复 ID
- **THEN** 返回 400 校验错误
### Requirement: 批次负责人
每个批次 SHALL 配置至少一名负责人(多名允许);配置保存时 SHALL 将该负责人集合同步至批次内全部未删除任务的 `biz_qc_task_assignee`。
#### Scenario: 修改批次负责人
- **WHEN** 调整检测修改某批次负责人
- **THEN** 该批次下所有任务 assignee 更新,「我的质检任务」对新负责人可见
### Requirement: 配置检测 API
`POST /web/admin/qc-work-orders/{id}/configure` SHALL 接受 `batches[]`;每批含 `assigneeUserIds`、`hasLab`、`hasThirdParty`、`labItemIds`、`thirdPartyItemIds`。`dpz` 首次配置后工单状态为 `ypz`;`ypz` 可追加批次。
### Requirement: 批次列表 API
`GET /web/admin/qc-work-orders/{id}/inspection-batches` SHALL 返回批次列表,每项含 `tasks` 嵌套(结构与任务列表项一致)及 `taskCount` / `completedTaskCount`。
### Requirement: 列表 UI 批次层级
质检工单与我的质检任务页面 SHALL 在工单展开区先展示检测批次,展开批次后展示检测项目任务卡片;任务级操作(开始、填写结果)不变。
### Requirement: 只读质检信息批次展示
协议单/捐赠单/入库放行/出库放行等查看弹窗中的质检信息、以及质检工单「查看」弹窗 SHALL 按检测批次分组展示任务;多批次时以 Tab 或分节展示批次名、批次编号、负责人与进度。
#### Scenario: 协议样本卡质检信息
- **WHEN** 用户在协议单查看弹窗打开含多批次质检工单的样本卡
- **THEN** 质检信息区按批次 Tab 展示各批任务摘要,可跳转任务详情
### Requirement: 详情内嵌 inspectionBatches
工单详情、`qcInfo`(样本关联摘要)、出库质检详情 SHALL 可选内嵌 `inspectionBatches` 字段,结构与 `GET .../inspection-batches` 一致,以减少前端重复请求。
@@ -0,0 +1,36 @@
# Tasks: qc-inspection-batch
## 1. Database & Models
- [x] 1.1 `docs/sql/qc_inspection_batch.sql` — 新表 + alter `batch_id` + 数据迁移
- [x] 1.2 `models.py` — `BizQcWorkOrderInspectionBatch`、`BizQcBatchAssignee`;明细/任务 `batch_id`
- [x] 1.3 更新 `待同步到生产的sql脚本`;`migrate_qc_inspection_batch_data.sql` 历史迁移
- [ ] 1.4 合并到 `docs/sql/zkxw.sql` 全量基线(待下次库导出)
## 2. Backend
- [x] 2.1 `crud/qc_inspection_batch.py` — 批次 CRUD、同步 lab/third、assignee、configure
- [x] 2.2 `configure_work_order` 改为批次数组;扁平 body 兼容单批
- [x] 2.3 `GET /qc-work-orders/{id}/inspection-batches`
- [x] 2.4 工单详情 / qcInfo / outbound-detail 内嵌 `inspectionBatches`
## 3. Frontend — 配置弹窗
- [x] 3.1 `qc-work-order.ts` / `qc-inspection-batch.ts` — 类型与 API
- [x] 3.2 `QcWorkOrderConfigureBatches.vue` + `index.vue` 批次卡片配置 UI
- [x] 3.3 校验:批次负责人、批次内不重复、至少一项检测
## 4. Frontend — 列表与只读展示
- [x] 4.1 `QcWorkOrderBatchSubList.vue`
- [x] 4.2 `QcWorkOrderSingleCard` 改用批次列表(SampleGroup 经 SingleCard 继承)
- [x] 4.3 我的任务:`QcWorkOrderBatchSubList` 按 `batchId` 分组 preset 任务
- [x] 4.4 `SampleQcBatchReadonly` + `SampleQcInfoCards` 批次 Tab(协议/捐赠/放行等查看弹窗)
- [x] 4.5 工单查看弹窗六 Tab:`QcWorkOrderSystemViewDialog` 按批次分节
- [x] 4.6 任务详情弹窗补充「检测批次」字段;`loadQcInspectionBatches` 优先内嵌数据
## 5. Verification
- [ ] 5.1 执行迁移 SQL(本地)
- [ ] 5.2 手工验收:双批次、跨批重复项目、追加批次、我的任务可见性
- [ ] 5.3 手工验收:协议单/放行查看弹窗质检信息按批次展示;工单查看弹窗批次分节
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+164 -137
View File
@@ -1,11 +1,11 @@
# Graph Report - src (2026-06-25)
# Graph Report - src (2026-06-26)
## Corpus Check
- 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.
- Large corpus: 811 files · ~539,420 words. Semantic extraction will be expensive (many Claude tokens). Consider running on a subfolder, or use --no-semantic to run AST-only.
## Summary
- 2624 nodes · 3636 edges · 53 communities detected
- Extraction: 78% EXTRACTED · 22% INFERRED · 0% AMBIGUOUS · INFERRED: 785 edges (avg confidence: 0.8)
- 2705 nodes · 3770 edges · 58 communities detected
- Extraction: 78% EXTRACTED · 22% INFERRED · 0% AMBIGUOUS · INFERRED: 825 edges (avg confidence: 0.8)
- Token cost: 0 input · 0 output
## Community Hubs (Navigation)
@@ -45,57 +45,62 @@
- [[_COMMUNITY_Community 33|Community 33]]
- [[_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 48|Community 48]]
- [[_COMMUNITY_Community 49|Community 49]]
- [[_COMMUNITY_Community 50|Community 50]]
- [[_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 58|Community 58]]
- [[_COMMUNITY_Community 63|Community 63]]
- [[_COMMUNITY_Community 65|Community 65]]
- [[_COMMUNITY_Community 60|Community 60]]
- [[_COMMUNITY_Community 64|Community 64]]
- [[_COMMUNITY_Community 66|Community 66]]
- [[_COMMUNITY_Community 72|Community 72]]
- [[_COMMUNITY_Community 73|Community 73]]
## God Nodes (most connected - your core abstractions)
1. `get()` - 268 edges
2. `push()` - 64 edges
3. `emit()` - 27 edges
1. `get()` - 275 edges
2. `push()` - 70 edges
3. `emit()` - 31 edges
4. `WebSocketClient` - 26 edges
5. `MenuProcessor` - 16 edges
6. `handleDynamicRoutes()` - 16 edges
7. `withFullscreenLoading()` - 15 edges
8. `VersionManager` - 15 edges
9. `scopeQuery()` - 15 edges
10. `set()` - 15 edges
5. `handleDynamicRoutes()` - 18 edges
6. `set()` - 17 edges
7. `MenuProcessor` - 16 edges
8. `withFullscreenLoading()` - 15 edges
9. `VersionManager` - 15 edges
10. `scopeQuery()` - 15 edges
## Surprising Connections (you probably didn't know these)
- `handleBatchDelete()` --calls--> `withFullscreenLoading()` [INFERRED]
views/basic-info/customer/index.vue → utils/fullscreen-loading.ts
- `buildInboundMetaFields()` --calls--> `push()` [INFERRED]
utils/production-work-order-card-meta.ts → views/operations/storage-facility/index.vue
- `subscribeTokenRefresh()` --calls--> `push()` [INFERRED]
utils/http/index.ts → views/operations/storage-facility/index.vue
- `handleUnauthorizedResponse()` --calls--> `fetchRefreshToken()` [INFERRED]
utils/http/index.ts → api/auth.ts
- `fetchLeaveList()` --calls--> `get()` [INFERRED]
api/leave.ts → views/production/process/ProcessRouteTab.vue
- `fetchLeaveDetail()` --calls--> `get()` [INFERRED]
api/leave.ts → views/production/process/ProcessRouteTab.vue
- `fmtMoney()` --calls--> `formatMoneyDisplay()` [INFERRED]
components/business/clinical-schedule-payment-detail/index.vue → utils/money-format.ts
- `formatAmount()` --calls--> `formatMoneyDisplay()` [INFERRED]
views/pilot-service/clinical-payment-confirm/RefundConfirmTab.vue → utils/money-format.ts
- `defaultInstallmentRatios()` --calls--> `push()` [INFERRED]
utils/protocol-payment-installment.ts → views/operations/storage-facility/index.vue
## Communities
### Community 0 - "Community 0"
Cohesion: 0.01
Nodes (134): fetchAccessLogList(), fetchActiveRegions(), fetchCategoryList(), fetchCategoryParameters(), fetchCellProductPriceList(), fetchChannelAccount(), fetchChannelAgreementPrices(), fetchChannelAgreements() (+126 more)
Nodes (137): fetchAccessLogList(), fetchActiveRegions(), fetchCategoryList(), fetchCategoryParameters(), fetchCellProductPriceList(), fetchChannelAccount(), fetchChannelAgreementPrices(), fetchChannelAgreements() (+129 more)
### Community 1 - "Community 1"
Cohesion: 0.03
Nodes (70): fetchCellSampleTypeList(), fetchCheckupInstitutionInvoices(), fetchCheckupInstitutionList(), fetchCheckupInstitutionProjects(), fetchCreateCellSampleType(), fetchCreateCheckupInstitution(), fetchCreateCheckupInstitutionInvoice(), fetchCreateCheckupInstitutionProject() (+62 more)
Cohesion: 0.02
Nodes (86): fetchUploadAttachment(), fetchMyProductionTasks(), fetchMyQcTaskList(), fetchQcTaskDetail(), fetchQcTaskList(), handleReportUpload(), handleReportUpload(), walk() (+78 more)
### Community 2 - "Community 2"
Cohesion: 0.02
@@ -103,244 +108,266 @@ Nodes (42): fetchAllConsumablePacks(), fetchAllConsumables(), fetchAllConsumable
### Community 3 - "Community 3"
Cohesion: 0.03
Nodes (33): fetchGetMenuList(), ComponentLoader, IframeRouteManager, MenuProcessor, RouteRegistry, RouteTransformer, useAppMode(), useAuth() (+25 more)
Nodes (54): fetchCreateCustomer(), fetchCustomerList(), fetchDeleteCustomer(), fetchUpdateCustomer(), updateInfusionScheduleStatus(), handleBatchDelete(), handleDelete(), handleDropdownCommand() (+46 more)
### Community 4 - "Community 4"
Cohesion: 0.03
Nodes (44): handleBlur(), handleFocus(), handleInput(), formatAmount(), formatAmount(), formatProtocolAmount(), fmtMoney(), buildBaseFields() (+36 more)
Nodes (33): fetchGetMenuList(), ComponentLoader, IframeRouteManager, MenuProcessor, RouteRegistry, RouteTransformer, useAppMode(), useAuth() (+25 more)
### Community 5 - "Community 5"
Cohesion: 0.03
Nodes (56): handleBlur(), handleFocus(), handleInput(), fetchFullmoonTodoList(), fetchUpdateFullmoonTodoInsurance(), fetchCollectionProtocolAuditList(), fetchCollectionProtocolDetail(), fetchCollectionProtocolList() (+48 more)
### Community 6 - "Community 6"
Cohesion: 0.03
Nodes (52): fetchCheckupInstitutionInvoices(), fetchCheckupInstitutionList(), fetchCheckupInstitutionProjects(), fetchCreateCheckupInstitution(), fetchCreateCheckupInstitutionInvoice(), fetchCreateCheckupInstitutionProject(), fetchUpdateCheckupInstitution(), fetchUpdateCheckupInstitutionInvoice() (+44 more)
### Community 7 - "Community 7"
Cohesion: 0.04
Nodes (72): batchSavePilotCheckup(), deletePilotCheckupActualItem(), fetchCheckupInstitutionPackageDetail(), fetchCheckupInstitutionPackages(), fetchPilotCheckupDetail(), markPilotCheckupChecked(), savePilotCheckup(), savePilotCheckupActualItems() (+64 more)
### Community 6 - "Community 6"
Cohesion: 0.04
Nodes (53): fetchCreateCustomer(), fetchCustomerList(), fetchDeleteCustomer(), fetchUpdateCustomer(), updateInfusionScheduleStatus(), fetchProductionTaskList(), handleAdd(), handleDelete() (+45 more)
### Community 7 - "Community 7"
Cohesion: 0.05
Nodes (57): fetchUploadAttachment(), FireworkSystem, handleReportUpload(), handleReportUpload(), walk(), async(), handleAttachmentChange(), push() (+49 more)
### Community 8 - "Community 8"
Cohesion: 0.04
Nodes (42): fetchMyProductionTasks(), fetchMyQcTaskList(), fetchQcTaskDetail(), fetchQcTaskList(), for(), set(), annotateForkEdges(), chainApiToElements() (+34 more)
Nodes (35): fetchClinicalPaymentDetail(), fetchClinicalPayments(), fetchClinicalReceiptConfirmed(), fetchClinicalReceiptPending(), fetchClinicalRefundConfirmed(), fetchClinicalRefundPending(), fetchOrderAdjusts(), fetchOrderDetail() (+27 more)
### Community 9 - "Community 9"
Cohesion: 0.04
Nodes (44): fetchFullmoonTodoList(), fetchUpdateFullmoonTodoInsurance(), fetchCollectionProtocolAuditList(), fetchCollectionProtocolDetail(), fetchCollectionProtocolList(), fetchCreateProtocolInvoiceRequest(), fetchInvoiceEligiblePayments(), fetchPaymentDetailList() (+36 more)
Cohesion: 0.05
Nodes (33): useSettingsHandlers(), useSettingsPanel(), useSettingsState(), RoutePermissionValidator, useCeremony(), useTheme(), if(), walk() (+25 more)
### Community 10 - "Community 10"
Cohesion: 0.04
Nodes (37): fetchCreateMenu(), fetchDeleteMenu(), fetchMenuTree(), fetchRoleOptions(), fetchUpdateMenu(), collectIds(), buildInboundBasicSection(), buildInboundCellStorageViewSections() (+29 more)
Cohesion: 0.06
Nodes (38): buildBaseFields(), buildFeeSection(), buildInboundBasicSection(), buildInboundCellStorageProductionViewSections(), buildInboundResearchDonationProductionViewSections(), buildOutboundBasicExperimentProductionViewSections(), buildOutboundClinicalProductionViewSections(), buildOutboundDetail() (+30 more)
### Community 11 - "Community 11"
Cohesion: 0.04
Nodes (36): fetchAttachmentDownloadUrl(), qcAttachmentFullUrl(), qcDownloadAttachment(), qcPreviewAttachment(), handleDownloadAttachment(), attachmentFullUrl(), handleAttachmentDownload(), handleAttachmentPreview() (+28 more)
### Community 12 - "Community 12"
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 15 - "Community 15"
### Community 12 - "Community 12"
Cohesion: 0.05
Nodes (30): useSettingsHandlers(), useSettingsPanel(), useSettingsState(), RoutePermissionValidator, useCeremony(), useTheme(), collectAllPostBindDeptIds(), collectPostBindDeptSubtreeIds() (+22 more)
Nodes (35): fetchAttachmentDownloadUrl(), qcAttachmentFullUrl(), qcDownloadAttachment(), qcPreviewAttachment(), handleDownloadAttachment(), attachmentFullUrl(), handleAttachmentDownload(), handleAttachmentPreview() (+27 more)
### Community 16 - "Community 16"
### Community 13 - "Community 13"
Cohesion: 0.06
Nodes (36): fetchUserList(), fetchAddTaskStep(), fetchCopyTaskStep(), fetchDeleteTaskStep(), fetchDispatchProductionTaskSteps(), fetchForwardProductionTask(), fetchProductionTaskDetail(), fetchReassignProductionTask() (+28 more)
Nodes (37): fetchUserList(), fetchAddTaskStep(), fetchCopyTaskStep(), fetchDeleteTaskStep(), fetchDispatchProductionTaskSteps(), fetchForwardProductionTask(), fetchProductionTaskDetail(), fetchProductionTaskList() (+29 more)
### Community 17 - "Community 17"
### Community 14 - "Community 14"
Cohesion: 0.05
Nodes (13): fmtMoney(), auditorDisplay(), dash(), ensureUnitsInOptions(), expressName(), formatYm(), formatYmd(), sampleTypeName() (+5 more)
### Community 15 - "Community 15"
Cohesion: 0.04
Nodes (21): fetchAvailableMaterialBatches(), fetchMyProductionMaterialConsumptions(), fetchMyProductionMaterialUsageList(), fetchProductionMaterialBatchList(), fetchProductionMaterialBatchOptions(), fetchProductionMaterialConsumptions(), fetchProductionMaterialFormula(), fetchProductionMaterialLinkedStepUsages() (+13 more)
### Community 16 - "Community 16"
Cohesion: 0.09
Nodes (41): applyFqParamsChange(), buildFqAmounts(), countByStatus(), evaluateFqParamsChange(), formatMoneySummary(), generateInitialFqDetails(), getPaidInstallmentRows(), isSameFqParams() (+33 more)
### Community 17 - "Community 17"
Cohesion: 0.06
Nodes (25): fetchCreateMenu(), fetchDeleteMenu(), fetchMenuTree(), fetchRoleOptions(), fetchUpdateMenu(), collectIds(), postorder(), preorder() (+17 more)
### Community 18 - "Community 18"
Cohesion: 0.06
Nodes (20): fetchQcMaterialBatchList(), fetchQcMaterialBatchOptions(), fetchQcMaterialList(), fetchQcMaterialOptions(), fetchUpdateQcMaterialBatch(), fetchMarkQcMaterialUsageFullyUsed(), financeAttachmentsToUrlString(), ensureTabMounted() (+12 more)
### Community 19 - "Community 19"
Cohesion: 0.1
Nodes (2): WebSocketClient, TableCache
### Community 19 - "Community 19"
Cohesion: 0.08
Nodes (32): toLogin(), base64ToBytes(), bytesToBase64(), decryptJson(), deriveKey(), encryptJson(), fromBase64Url(), hexToBytes() (+24 more)
### Community 20 - "Community 20"
Cohesion: 0.07
Nodes (18): buildInboundBasicSection(), buildInboundCellStorageViewSections(), buildInboundResearchDonationViewSections(), buildReceptionSection(), buildTransportSection(), formatCheckResults(), formatMetric(), appendTaskResultSections() (+10 more)
### Community 21 - "Community 21"
Cohesion: 0.09
Nodes (31): base64ToBytes(), bytesToBase64(), decryptJson(), deriveKey(), encryptJson(), fromBase64Url(), hexToBytes(), sha256Hex() (+23 more)
### Community 22 - "Community 22"
Cohesion: 0.08
Nodes (27): fetchUserOptions(), fetchSampleReceptionCheckItemList(), fetchSampleReceptionCheckItemsActive(), fetchAllocateLabStorageCodes(), fetchSampleReceptionBySampleCode(), fetchSampleReceptionDetail(), fetchSampleReceptionPendingList(), fetchSampleReceptionReceivedList() (+19 more)
### Community 21 - "Community 21"
Cohesion: 0.08
Nodes (20): maskIdCard(), buildBasicExperimentApplicationFields(), buildDefaultApplicationFields(), buildOutboundPilotContextSections(), applicationStatusLabel(), applicationStatusTagType(), cellProductStatusLabel(), cellProductStatusTagType() (+12 more)
### Community 22 - "Community 22"
### Community 23 - "Community 23"
Cohesion: 0.07
Nodes (17): fetchEthnicList(), applyDonationBundleToForm(), createEmptyTransport(), ensureEthnicOptionInList(), handleOpenForAdd(), loadEthnicOptions(), normalizeSampleRow(), resetForm() (+9 more)
### Community 23 - "Community 23"
### Community 24 - "Community 24"
Cohesion: 0.09
Nodes (27): completePilotApplication(), deletePilotApplication(), fetchPilotApplicationList(), startPilotApplication(), voidPilotApplication(), handleBatchComplete(), handleBatchStart(), handleBatchVoid() (+19 more)
### Community 24 - "Community 24"
### Community 25 - "Community 25"
Cohesion: 0.07
Nodes (16): fetchDeleteMaterialBatch(), fetchMaterialBatchList(), fetchMaterialBatchOptions(), fetchMaterialList(), fetchMaterialOptions(), fetchUpdateMaterialBatch(), fetchMaterialUsageList(), fetchMyMaterialUsageList() (+8 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.1
Nodes (28): fetchCellProductSpecList(), fetchCellProductTypeList(), fetchMaxPassageProductSeq(), handleAdd(), resetForm(), applyProductCodeToLines(), calcExpiryFromFreeze(), createEmptyProductLine() (+20 more)
### Community 27 - "Community 27"
Cohesion: 0.11
Nodes (21): fetchGetMenuCodes(), fetchGetUserInfo(), fetchLogin(), fetchRefreshToken(), fetchUpdateProfile(), fetchUploadProfileAvatar(), fetchUserInfo(), buildWebEncryptedLoginBody() (+13 more)
### 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.09
Nodes (11): fetchMineQcWorkOrderTabUnfinishedCounts(), fetchOutboundQcDetail(), fetchQcWorkOrderDetail(), fetchQcWorkOrderList(), fetchQcWorkOrderTabUnfinishedCounts(), reload(), reloadTabCounts(), handleMenuJump() (+3 more)
### Community 29 - "Community 29"
Cohesion: 0.12
Nodes (15): addCopyButton(), highlightCode(), insertLineNumbers(), isBlockProcessed(), markBlockAsProcessed(), processBlock(), setupHighlightDirective(), setupRippleDirective() (+7 more)
### Community 29 - "Community 29"
### Community 30 - "Community 30"
Cohesion: 0.1
Nodes (5): normalizeStepSubTables(), parseEquipmentIds(), syncEquipmentNamesFromOptions(), formatPlannedStartDate(), normalizePlannedStartDate()
### Community 31 - "Community 31"
Cohesion: 0.13
Nodes (18): fetchCellSampleTypeList(), fetchCreateCellSampleType(), fetchDeleteCellSampleType(), fetchUpdateCellSampleType(), handleAdd(), handleDelete(), handleDropdownCommand(), handleEdit() (+10 more)
### Community 32 - "Community 32"
Cohesion: 0.11
Nodes (11): fetchOutboundReleaseMaterial(), fetchOutboundReleaseReviewDetail(), fetchOutboundReleaseReviewList(), fetchReleaseReviewList(), handleReset(), handleSearch(), handleToggleSelect(), loadData() (+3 more)
### Community 30 - "Community 30"
### Community 33 - "Community 33"
Cohesion: 0.12
Nodes (8): fetchMineProductionWorkOrderTabStepCounts(), fetchProductionWorkOrderCellProductOptions(), fetchProductionWorkOrderDetail(), fetchProductionWorkOrderList(), fetchProductionWorkOrderQcDispatchOptions(), fetchProductionWorkOrderTabStepCounts(), collectVisibleProductionMenuBadgeCodes(), refreshProductionMenuBadges()
### Community 34 - "Community 34"
Cohesion: 0.16
Nodes (13): handleChineseDatetimePaste(), parseChineseDatetimeText(), isMaternalBloodEmpty(), matchCellSampleType(), normalizeLine(), parseDatetime(), parseDeliveryMode(), parseDonationStandardImportText() (+5 more)
### Community 31 - "Community 31"
### Community 35 - "Community 35"
Cohesion: 0.16
Nodes (14): fetchCreateEnvDetectItem(), fetchDeleteEnvDetectItem(), fetchEnvDetectItemDetail(), fetchEnvDetectItemList(), fetchEnvDetectItemsActive(), fetchUpdateEnvDetectItem(), handleAdd(), handleBatchDelete() (+6 more)
### Community 32 - "Community 32"
### Community 36 - "Community 36"
Cohesion: 0.16
Nodes (10): useTable(), useTableImpl(), getColumnChecks(), useTableColumns(), createErrorHandler(), createSmartDebounce(), defaultResponseAdapter(), extractPagination() (+2 more)
### Community 33 - "Community 33"
### Community 37 - "Community 37"
Cohesion: 0.27
Nodes (14): useChart(), useChartComponent(), useChartOps(), colourBlend(), getCssVar(), getDarkColor(), getLightColor(), handleElementThemeColor() (+6 more)
### Community 34 - "Community 34"
### Community 38 - "Community 38"
Cohesion: 0.33
Nodes (13): buildProtocolPaymentFlowSteps(), dash(), financeReceiptTime(), formatAmount(), hasFinanceReceipt(), hasFinanceRefund(), hasReceiptSubmit(), hasRefundSubmit() (+5 more)
### Community 35 - "Community 35"
### Community 39 - "Community 39"
Cohesion: 0.19
Nodes (6): fetchServiceOrderTxnList(), handleReset(), handleSearch(), handleTabChange(), loadData(), switchTab()
### Community 37 - "Community 37"
### Community 41 - "Community 41"
Cohesion: 0.15
Nodes (4): fetchNextDonationSampleCode(), fetchResearchDonationAuditList(), fetchResearchDonationDetail(), fetchResearchDonationList()
### Community 38 - "Community 38"
Cohesion: 0.18
Nodes (3): normalizeStepSubTables(), parseEquipmentIds(), syncEquipmentNamesFromOptions()
### Community 39 - "Community 39"
### Community 42 - "Community 42"
Cohesion: 0.24
Nodes (6): buildInboundMetaFields(), getCardMetaConfig(), getCardMetaConfigForLegacyTab(), parseReceptionTime(), receptionOver24HoursLabel(), receptionOver24HoursTagType()
### Community 40 - "Community 40"
### Community 43 - "Community 43"
Cohesion: 0.42
Nodes (9): buildPaymentFlowSteps(), dash(), formatAmount(), hasFinanceReceipt(), hasFinanceRefund(), hasReceiptSubmit(), hasRefundSubmit(), receiptSubmitTime() (+1 more)
### Community 41 - "Community 41"
### Community 44 - "Community 44"
Cohesion: 0.25
Nodes (2): handleTouchEnd(), setTransition()
### Community 44 - "Community 44"
### Community 45 - "Community 45"
Cohesion: 0.28
Nodes (1): FireworkSystem
### Community 49 - "Community 49"
Cohesion: 0.39
Nodes (6): isIgnorableScriptError(), normalizeErrorMessage(), registerPromiseErrorHandler(), registerResourceErrorHandler(), scriptErrorHandler(), setupErrorHandle()
### Community 45 - "Community 45"
### Community 50 - "Community 50"
Cohesion: 0.36
Nodes (1): TableHeightCalculator
### Community 48 - "Community 48"
### Community 52 - "Community 52"
Cohesion: 0.33
Nodes (2): logApiCryptoBootInfo(), maskSecret()
### Community 49 - "Community 49"
Cohesion: 0.29
Nodes (2): fetchLeaveDetail(), fetchLeaveList()
### Community 53 - "Community 53"
Cohesion: 0.48
Nodes (5): buildPrefilledLabStorageCode(), formatYear(), formatYm(), normalizeSampleTypeName(), normalizeSourceType()
### Community 50 - "Community 50"
### Community 54 - "Community 54"
Cohesion: 0.47
Nodes (3): isPaymentDueWithinWarnWindow(), parsePaymentDueYmd(), startOfLocalDay()
### Community 52 - "Community 52"
### Community 55 - "Community 55"
Cohesion: 0.5
Nodes (2): formatProtocolDocDeleteConfirm(), formatProtocolDocSubject()
### Community 53 - "Community 53"
### Community 56 - "Community 56"
Cohesion: 0.5
Nodes (2): focusInput(), handleKeydown()
### Community 54 - "Community 54"
### Community 57 - "Community 57"
Cohesion: 0.67
Nodes (2): if(), logSidebarMenu()
### Community 57 - "Community 57"
### Community 60 - "Community 60"
Cohesion: 0.5
Nodes (1): fetchSampleSmsNotificationList()
Nodes (1): fetchStorageEntrySmsList()
### Community 58 - "Community 58"
Cohesion: 0.5
Nodes (1): fetchQcSmsList()
### Community 63 - "Community 63"
### Community 64 - "Community 64"
Cohesion: 1.0
Nodes (2): getSettingDefaults(), resetToDefaults()
### Community 65 - "Community 65"
### Community 66 - "Community 66"
Cohesion: 1.0
Nodes (2): copyTextToClipboard(), copyTextWithFallback()
### Community 72 - "Community 72"
Cohesion: 1.0
Nodes (2): emptyTabCounts(), useProductionWorkOrderTabStepCounts()
### Community 73 - "Community 73"
Cohesion: 1.0
Nodes (2): emptyTabCounts(), useQcWorkOrderTabUnfinishedCounts()
## Knowledge Gaps
- **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`
- **Thin community `Community 19`** (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`
- **Thin community `Community 44`** (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 45`** (8 nodes): `TableHeightCalculator`, `.calculate()`, `.calculateOffset()`, `.calculatePaginationOffset()`, `.constructor()`, `.getHeaderHeight()`, `useTableHeight()`, `useTableHeight.ts`
- **Thin community `Community 45`** (9 nodes): `FireworkSystem`, `.constructor()`, `.createFirework()`, `.createParticle()`, `.getAvailableParticle()`, `.initializePool()`, `.preloadAllImages()`, `.preloadImage()`, `index.vue`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 48`** (7 nodes): `App.vue`, `main.ts`, `refreshButtonAutoIcons()`, `refreshPanelTitleIcons()`, `logApiCryptoBootInfo()`, `maskSecret()`, `console.ts`
- **Thin community `Community 50`** (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 49`** (7 nodes): `fetchCreateLeave()`, `fetchDeleteLeave()`, `fetchLeaveDetail()`, `fetchLeaveList()`, `fetchSubmitLeave()`, `fetchUpdateLeave()`, `leave.ts`
- **Thin community `Community 52`** (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 52`** (5 nodes): `formatProtocolDocBatchDeleteConfirm()`, `formatProtocolDocDeleteConfirm()`, `formatProtocolDocSubject()`, `getProtocolDocTypeLabel()`, `protocol-doc-label.ts`
- **Thin community `Community 55`** (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`** (5 nodes): `focusInput()`, `handleKeydown()`, `if()`, `search()`, `index.vue`
- **Thin community `Community 56`** (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`** (4 nodes): `if()`, `logSidebarMenu()`, `index.vue`, `SidebarSubmenu.vue`
- **Thin community `Community 57`** (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 57`** (4 nodes): `fetchGenerateSms()`, `fetchSampleSmsNotificationList()`, `fetchToggleSmsNotified()`, `sample-sms-notification.ts`
- **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 58`** (4 nodes): `fetchGenerateQcSms()`, `fetchQcSmsList()`, `fetchToggleQcSmsNotified()`, `qc-sms-notification.ts`
- **Thin community `Community 64`** (3 nodes): `getSettingDefaults()`, `resetToDefaults()`, `setting.ts`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 63`** (3 nodes): `getSettingDefaults()`, `resetToDefaults()`, `setting.ts`
- **Thin community `Community 66`** (3 nodes): `copyTextToClipboard()`, `copyTextWithFallback()`, `copy-text.ts`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 65`** (3 nodes): `copyTextToClipboard()`, `copyTextWithFallback()`, `copy-text.ts`
- **Thin community `Community 72`** (3 nodes): `emptyTabCounts()`, `useProductionWorkOrderTabStepCounts()`, `useProductionWorkOrderTabStepCounts.ts`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 73`** (3 nodes): `emptyTabCounts()`, `useQcWorkOrderTabUnfinishedCounts()`, `useQcWorkOrderTabUnfinishedCounts.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 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?**
_`push()` has 63 INFERRED edges - model-reasoned connections that need verification._
- **Are the 26 inferred relationships involving `emit()` (e.g. with `handleSubmit()` and `handleBlur()`) actually correct?**
_`emit()` has 26 INFERRED edges - model-reasoned connections that need verification._
- **Why does `get()` connect `Community 0` to `Community 1`, `Community 2`, `Community 3`, `Community 5`, `Community 6`, `Community 7`, `Community 8`, `Community 9`, `Community 11`, `Community 13`, `Community 15`, `Community 16`, `Community 17`, `Community 18`, `Community 20`, `Community 22`, `Community 23`, `Community 24`, `Community 25`, `Community 26`, `Community 27`, `Community 28`, `Community 31`, `Community 32`, `Community 33`, `Community 35`, `Community 39`, `Community 41`, `Community 60`?**
_High betweenness centrality (0.318) - this node is a cross-community bridge._
- **Why does `push()` connect `Community 1` to `Community 0`, `Community 2`, `Community 3`, `Community 5`, `Community 6`, `Community 7`, `Community 9`, `Community 10`, `Community 11`, `Community 13`, `Community 14`, `Community 16`, `Community 17`, `Community 19`, `Community 20`, `Community 21`, `Community 23`, `Community 24`, `Community 26`, `Community 28`, `Community 33`, `Community 42`, `Community 45`?**
_High betweenness centrality (0.185) - this node is a cross-community bridge._
- **Are the 274 inferred relationships involving `get()` (e.g. with `buildNodeCodePathFromFlat()` and `collectLeafFacilityNodesInTreeOrder()`) actually correct?**
_`get()` has 274 INFERRED edges - model-reasoned connections that need verification._
- **Are the 69 inferred relationships involving `push()` (e.g. with `refreshQcMenuBadges()` and `collectLeafFacilityNodesInTreeOrder()`) actually correct?**
_`push()` has 69 INFERRED edges - model-reasoned connections that need verification._
- **Are the 30 inferred relationships involving `emit()` (e.g. with `handleSubmit()` and `handleBlur()`) actually correct?**
_`emit()` has 30 INFERRED edges - model-reasoned connections that need verification._
- **Are the 11 inferred relationships involving `handleDynamicRoutes()` (e.g. with `.getMenuList()` and `.validateMenuList()`) actually correct?**
_`handleDynamicRoutes()` has 11 INFERRED edges - model-reasoned connections that need verification._
- **Should `Community 0` be split into smaller, more focused modules?**
_Cohesion score 0.01 - nodes in this community are weakly interconnected._
+4 -4
View File
@@ -57,12 +57,12 @@
</div>
<div id="legend"></div>
</div>
<div id="stats">2624 nodes &middot; 3636 edges &middot; 318 communities</div>
<div id="stats">2705 nodes &middot; 3770 edges &middot; 324 communities</div>
</div>
<script>
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
const RAW_NODES = [{"id": "app_vue", "label": "App.vue", "color": {"background": "#E15759", "border": "#E15759", "highlight": {"background": "#ffffff", "border": "#E15759"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "App.vue", "community": 52, "community_name": "Community 52", "source_file": "App.vue", "file_type": "code", "degree": 3}, {"id": "src_app_refreshbuttonautoicons", "label": "refreshButtonAutoIcons()", "color": {"background": "#E15759", "border": "#E15759", "highlight": {"background": "#ffffff", "border": "#E15759"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "refreshButtonAutoIcons()", "community": 52, "community_name": "Community 52", "source_file": "App.vue", "file_type": "code", "degree": 1}, {"id": "src_app_refreshpaneltitleicons", "label": "refreshPanelTitleIcons()", "color": {"background": "#E15759", "border": "#E15759", "highlight": {"background": "#ffffff", "border": "#E15759"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "refreshPanelTitleIcons()", "community": 52, "community_name": "Community 52", "source_file": "App.vue", "file_type": "code", "degree": 1}, {"id": "main_ts", "label": "main.ts", "color": {"background": "#E15759", "border": "#E15759", "highlight": {"background": "#ffffff", "border": "#E15759"}}, "size": 10.4, "font": {"size": 0, "color": "#ffffff"}, "title": "main.ts", "community": 52, "community_name": "Community 52", "source_file": "main.ts", "file_type": "code", "degree": 4}, {"id": "src_main_masksecret", "label": "maskSecret()", "color": {"background": "#E15759", "border": "#E15759", "highlight": {"background": "#ffffff", "border": "#E15759"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "maskSecret()", "community": 52, "community_name": "Community 52", "source_file": "main.ts", "file_type": "code", "degree": 2}, {"id": "src_main_logapicryptobootinfo", "label": "logApiCryptoBootInfo()", "color": {"background": "#E15759", "border": "#E15759", "highlight": {"background": "#ffffff", "border": "#E15759"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "logApiCryptoBootInfo()", "community": 52, "community_name": "Community 52", "source_file": "main.ts", "file_type": "code", "degree": 2}, {"id": "env_d_ts", "label": "env.d.ts", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "env.d.ts", "community": 105, "community_name": "Community 105", "source_file": "env.d.ts", "file_type": "code", "degree": 0}, {"id": "shims_loose_d_ts", "label": "shims-loose.d.ts", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "shims-loose.d.ts", "community": 106, "community_name": "Community 106", "source_file": "shims-loose.d.ts", "file_type": "code", "degree": 0}, {"id": "directives_index_ts", "label": "index.ts", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "index.ts", "community": 29, "community_name": "Community 29", "source_file": "directives/index.ts", "file_type": "code", "degree": 1}, {"id": "directives_index_setupglobdirectives", "label": "setupGlobDirectives()", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.5, "font": {"size": 0, "color": "#ffffff"}, "title": "setupGlobDirectives()", "community": 29, "community_name": "Community 29", "source_file": "directives/index.ts", "file_type": "code", "degree": 5}, {"id": "directives_core_roles_ts", "label": "roles.ts", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "roles.ts", "community": 29, "community_name": "Community 29", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 3}, {"id": "core_roles_checkrolepermission", "label": "checkRolePermission()", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "checkRolePermission()", "community": 29, "community_name": "Community 29", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 2}, {"id": "core_roles_removeelement", "label": "removeElement()", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "removeElement()", "community": 29, "community_name": "Community 29", "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": 389}, {"cid": 1, "color": "#F28E2B", "label": "Community 1", "count": 147}, {"cid": 2, "color": "#E15759", "label": "Community 2", "count": 104}, {"cid": 3, "color": "#76B7B2", "label": "Community 3", "count": 96}, {"cid": 4, "color": "#59A14F", "label": "Community 4", "count": 95}, {"cid": 5, "color": "#EDC948", "label": "Community 5", "count": 91}, {"cid": 6, "color": "#B07AA1", "label": "Community 6", "count": 90}, {"cid": 7, "color": "#FF9DA7", "label": "Community 7", "count": 89}, {"cid": 8, "color": "#9C755F", "label": "Community 8", "count": 62}, {"cid": 9, "color": "#BAB0AC", "label": "Community 9", "count": 58}, {"cid": 10, "color": "#4E79A7", "label": "Community 10", "count": 58}, {"cid": 11, "color": "#F28E2B", "label": "Community 11", "count": 55}, {"cid": 12, "color": "#E15759", "label": "Community 12", "count": 51}, {"cid": 13, "color": "#76B7B2", "label": "Community 13", "count": 51}, {"cid": 14, "color": "#59A14F", "label": "Community 14", "count": 50}, {"cid": 15, "color": "#EDC948", "label": "Community 15", "count": 48}, {"cid": 16, "color": "#B07AA1", "label": "Community 16", "count": 47}, {"cid": 17, "color": "#FF9DA7", "label": "Community 17", "count": 44}, {"cid": 18, "color": "#9C755F", "label": "Community 18", "count": 42}, {"cid": 19, "color": "#BAB0AC", "label": "Community 19", "count": 42}, {"cid": 20, "color": "#4E79A7", "label": "Community 20", "count": 39}, {"cid": 21, "color": "#F28E2B", "label": "Community 21", "count": 39}, {"cid": 22, "color": "#E15759", "label": "Community 22", "count": 39}, {"cid": 23, "color": "#76B7B2", "label": "Community 23", "count": 35}, {"cid": 24, "color": "#59A14F", "label": "Community 24", "count": 34}, {"cid": 25, "color": "#EDC948", "label": "Community 25", "count": 33}, {"cid": 26, "color": "#B07AA1", "label": "Community 26", "count": 32}, {"cid": 27, "color": "#FF9DA7", "label": "Community 27", "count": 29}, {"cid": 28, "color": "#9C755F", "label": "Community 28", "count": 24}, {"cid": 29, "color": "#BAB0AC", "label": "Community 29", "count": 22}, {"cid": 30, "color": "#4E79A7", "label": "Community 30", "count": 22}, {"cid": 31, "color": "#F28E2B", "label": "Community 31", "count": 22}, {"cid": 32, "color": "#E15759", "label": "Community 32", "count": 22}, {"cid": 33, "color": "#76B7B2", "label": "Community 33", "count": 20}, {"cid": 34, "color": "#59A14F", "label": "Community 34", "count": 19}, {"cid": 35, "color": "#EDC948", "label": "Community 35", "count": 19}, {"cid": 36, "color": "#B07AA1", "label": "Community 36", "count": 17}, {"cid": 37, "color": "#FF9DA7", "label": "Community 37", "count": 16}, {"cid": 38, "color": "#9C755F", "label": "Community 38", "count": 14}, {"cid": 39, "color": "#BAB0AC", "label": "Community 39", "count": 14}, {"cid": 40, "color": "#4E79A7", "label": "Community 40", "count": 13}, {"cid": 41, "color": "#F28E2B", "label": "Community 41", "count": 13}, {"cid": 42, "color": "#E15759", "label": "Community 42", "count": 11}, {"cid": 43, "color": "#76B7B2", "label": "Community 43", "count": 10}, {"cid": 44, "color": "#59A14F", "label": "Community 44", "count": 9}, {"cid": 45, "color": "#EDC948", "label": "Community 45", "count": 9}, {"cid": 46, "color": "#B07AA1", "label": "Community 46", "count": 9}, {"cid": 47, "color": "#FF9DA7", "label": "Community 47", "count": 9}, {"cid": 48, "color": "#9C755F", "label": "Community 48", "count": 9}, {"cid": 49, "color": "#BAB0AC", "label": "Community 49", "count": 8}, {"cid": 50, "color": "#4E79A7", "label": "Community 50", "count": 8}, {"cid": 51, "color": "#F28E2B", "label": "Community 51", "count": 8}, {"cid": 52, "color": "#E15759", "label": "Community 52", "count": 7}, {"cid": 53, "color": "#76B7B2", "label": "Community 53", "count": 7}, {"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": 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",Line truncated
// HTML-escape helper — prevents XSS when injecting graph data into innerHTML
function esc(s) {
File diff suppressed because it is too large. Load diff
+84 -65
View File
@@ -17,10 +17,10 @@
"/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": 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/import/components.d.ts": 1782406001.435002,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/auto-imports.d.ts": 1782405955.9372323,
"/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/types/router/index.ts": 1782394569.8806834,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/locales/index.ts": 1781272556.3657222,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/config/pilot-biz-type.ts": 1781682539.4897008,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/config/setting.ts": 1781406298.257571,
@@ -40,6 +40,7 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/mock/temp/commentDetail.ts": 1781272556.3772519,
"/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/qc-menu-badge.ts": 1782396392.0954244,
"/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": 1782289322.9593837,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/production-work-order-tabs.ts": 1781600464.3251936,
@@ -47,25 +48,30 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/user-avatar.ts": 1781403605.517818,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/fullscreen-loading.ts": 1781272556.3849921,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/qc-material-ui.ts": 1781525352.6976836,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/qc-work-order-tabs.ts": 1781600462.9964879,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/qc-work-order-card-meta.ts": 1781698792.9167962,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/qc-work-order-tabs.ts": 1782399957.5092957,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/qc-work-order-card-meta.ts": 1782400129.6397395,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/qc-work-order-category.ts": 1782393809.3917818,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/router.ts": 1781272556.3875713,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/production-mine-task-groups.ts": 1781583493.924234,
"/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/qc-mine-task-groups.ts": 1782399957.8029892,
"/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": 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/sample-reception-lab-storage-code.ts": 1782378453.1031013,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/outbound-qc-pilot-view-sections.ts": 1781566346.7618802,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/qc-work-order-groups.ts": 1781607964.911379,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/qc-work-order-groups.ts": 1782400129.6394563,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/qc-inspection-batch.ts": 1782398602.0427291,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/equipment-select.ts": 1781524048.96866,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/qc-item-param-options.ts": 1782380049.7449021,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/clinical-payment-flow-steps.ts": 1781566916.7893076,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/donation-standard-import-parse.ts": 1782181911.7421873,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/copy-text.ts": 1781272556.4018052,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/tree-path.ts": 1781272556.402468,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/mask-sensitive.ts": 1781272556.4029639,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/production-labels.ts": 1781272556.4033675,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/production-labels.ts": 1782404305.713515,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/production-menu-badge.ts": 1782404667.995363,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/numeric-input-parse.ts": 1782179343.5953708,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/index.ts": 1781272556.410244,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/money-format.ts": 1781272556.4107764,
@@ -167,10 +173,10 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-chat-window/index.vue": 1781272556.562926,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-page-content/index.vue": 1781272556.56442,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-menus/art-horizontal-menu/index.vue": 1781272556.5652099,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-menus/art-horizontal-menu/widget/HorizontalSubmenu.vue": 1781272556.5676432,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-menus/art-horizontal-menu/widget/HorizontalSubmenu.vue": 1782404678.3207965,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-menus/art-mixed-menu/index.vue": 1781401124.1983705,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-menus/art-sidebar-menu/index.vue": 1781406299.7525744,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-menus/art-sidebar-menu/widget/SidebarSubmenu.vue": 1781272556.5851753,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-menus/art-sidebar-menu/widget/SidebarSubmenu.vue": 1782404695.2500448,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-fireworks-effect/index.vue": 1781272556.5941117,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-global-search/index.vue": 1781272556.597667,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-notification/index.vue": 1781272556.6005983,
@@ -197,12 +203,12 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/sample-transport-form-section/index.vue": 1782206210.4586368,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/protocol-finance-confirm-fields/index.vue": 1781585388.6311953,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/protocol-finance-confirm-fields/finance-attachments.ts": 1781272556.6256514,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-task-view-dialog/index.vue": 1781590356.7882614,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-task-view-dialog/index.vue": 1782398594.1845114,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/clinical-payment-confirm-timeline/index.vue": 1781751829.1650536,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/outbound-material-readonly-panel/index.vue": 1781604188.1794512,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/dialog-form-body/index.vue": 1781272556.6276903,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/clinical-payment-submit-readonly/index.vue": 1781566692.993364,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/sample-qc-info-cards/index.vue": 1781596037.4093332,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/sample-qc-info-cards/index.vue": 1782398260.9411232,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/order-operate-local-dialog/index.vue": 1781272556.6297834,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/product-view-dialog/index.vue": 1781563767.2879,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/sample-production-info-cards/index.vue": 1781585388.6388853,
@@ -211,8 +217,10 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/work-order-remark-dialog/index.vue": 1781563767.2910764,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/sample-qc-info-grouped-panel/index.vue": 1781596014.9588113,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/clinical-schedule-payment-detail/index.vue": 1781585388.6415968,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/sample-qc-batch-readonly/index.vue": 1782398610.3519263,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/outbound-material-form-sections/index.vue": 1781598316.2599268,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/protocol-payment-detail-readonly/index.vue": 1781440987.1200492,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-item-param-select-editor/index.vue": 1782380052.8264449,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/system-view-dialog/index.vue": 1781592191.1258788,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/production-task-view-dialog/index.vue": 1781272556.644192,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/order-operate-dialog-bridge/index.vue": 1781272556.644745,
@@ -221,7 +229,7 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/hooks/core/useFastEnter.ts": 1781272556.6454022,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/hooks/core/useTableColumns.ts": 1781272556.6461287,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/hooks/core/useTable.ts": 1781272556.6477242,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/hooks/core/useMenuPerm.ts": 1782120928.5337958,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/hooks/core/useMenuPerm.ts": 1782405945.5294456,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/hooks/core/useHeaderBar.ts": 1781272556.6492157,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/hooks/core/useAuth.ts": 1781272556.6495874,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/hooks/core/useLayoutHeight.ts": 1781272556.6500459,
@@ -235,14 +243,14 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/sample-reception-check-item.ts": 1781272556.654672,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production.ts": 1781497438.3206656,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/infusion-schedule-handover.ts": 1781272556.6558568,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/outbound-release-review.ts": 1781603141.9232655,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/outbound-release-review.ts": 1782398621.3842177,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-third-party-item.ts": 1781272556.6567156,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/env-detect-record.ts": 1781272556.6569912,
"/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/env-detect-record.ts": 1782375300.5909576,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts": 1782398606.4492025,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-material.ts": 1782404996.1897874,
"/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": 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/production-work-order.ts": 1782404662.9687421,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-task-result.ts": 1782379634.2324347,
"/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,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/pre-transit-sample.ts": 1782204427.8344586,
@@ -259,8 +267,8 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/sample-sms-notification.ts": 1781272556.666838,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/material-stock-check.ts": 1781427280.4992144,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/research-donation.ts": 1782182859.3901165,
"/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/qc-task.ts": 1782398334.3162003,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-item-param.ts": 1782380054.2964334,
"/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": 1782362264.303446,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-material-stock-check.ts": 1781424012.5471919,
@@ -271,7 +279,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": 1782350342.5611486,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts": 1782398601.5849254,
"/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,
@@ -356,25 +364,27 @@
"/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": 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/index.vue": 1782404705.735053,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts": 1782404253.840776,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue": 1782404245.9286346,
"/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": 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": 1782351614.9751787,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/SampleReceptionDialog.vue": 1782377442.3260844,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskExecutionDialog.vue": 1782401948.7290232,
"/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": 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": 1782351634.8770185,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/WorkOrderTaskSubList.vue": 1782401972.0912113,
"/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": 1782358112.4315796,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/ProductionWorkOrderCardList.vue": 1782403069.155186,
"/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/WorkOrderTaskStepSubList.vue": 1782404256.625954,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/ProductionWorkOrderTabBar.vue": 1782403774.1374023,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchStepDialog.vue": 1782404217.6559534,
"/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/ProductionWorkOrderTabPanel.vue": 1782403798.0853126,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/tabs/InboundCellStorageTab.vue": 1781583380.9191706,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/tabs/OutboundBasicExperimentTab.vue": 1781583381.738445,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/tabs/InboundResearchDonationTab.vue": 1781583380.9646196,
@@ -383,6 +393,7 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/tabs/OutboundClinicalTab.vue": 1781583382.4319067,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/composables/production-view-sections.ts": 1781583319.8749113,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/composables/useProductionViewDialog.ts": 1781587764.7598283,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/composables/useProductionWorkOrderTabStepCounts.ts": 1782403760.178825,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/composables/useProductionWorkOrderTabPage.ts": 1781600314.4294987,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/view-dialogs/InboundResearchDonationProductionViewDialog.vue": 1781583332.0303154,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/view-dialogs/OutboundClinicalProductionViewDialog.vue": 1781583335.85169,
@@ -396,66 +407,72 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/consumable/index.vue": 1781563767.346933,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/consumable/ConsumablePackTab.vue": 1781563767.3461943,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/cleanroom-garment/index.vue": 1782265247.376364,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-material/index.vue": 1781519269.9141352,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-material/index.vue": 1782405078.1976075,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-material/MaterialConsumptionTab.vue": 1781502903.0563822,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-material/ProductionMaterialConfiguredCatalogDialog.vue": 1781498587.3288593,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-material/ProductionMaterialUsageTab.vue": 1781513793.4925437,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-material/ProductionMaterialStockCheckTab.vue": 1781508293.4167504,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-material/ProductionMaterialPrepTab.vue": 1781513807.0808678,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/sample-reception-completion/index.vue": 1781272556.844081,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/my-production-task/index.vue": 1781583391.5411932,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/my-production-task/index.vue": 1782404707.528094,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/equipment/index.vue": 1781563767.347355,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process/index.vue": 1781599788.6457248,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process/ProcessRouteTab.vue": 1781588546.7932389,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-category/index.vue": 1781585388.7112913,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-category/OutputTypeTab.vue": 1781563767.348404,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/index/index.vue": 1781272556.8548205,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue": 1781592807.1040754,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderTaskResultPanel.vue": 1781589482.0890527,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderSingleCard.vue": 1781699027.2649422,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue": 1782394624.8193831,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderTaskResultPanel.vue": 1782398341.1529968,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderProtocolGroup.vue": 1782400499.8822792,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderSingleCard.vue": 1782399918.5252447,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderConfigureBatches.vue": 1782386066.318798,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderFeeSummaryBar.vue": 1781585388.7152023,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderSampleGroup.vue": 1781766463.2300944,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderTaskSubList.vue": 1781590356.5447705,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderCardList.vue": 1781595924.5325,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderSampleGroup.vue": 1782399917.944618,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderTaskSubList.vue": 1782392417.2151616,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderTabBar.vue": 1782394192.4055598,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderBatchSubList.vue": 1782401185.925739,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderCardList.vue": 1782400042.2199035,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderProtocolPaymentPanel.vue": 1781588255.9076052,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/tabs/OutboundPilotTab.vue": 1781582034.0446215,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/tabs/InboundCellStorageTab.vue": 1781582031.9110096,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/tabs/QcWorkOrderTabPanel.vue": 1781608023.8214188,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/tabs/QcWorkOrderTabPanel.vue": 1782394239.6593502,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/tabs/OutboundBasicExperimentTab.vue": 1781582033.9975233,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/tabs/InboundResearchDonationTab.vue": 1781582033.9975069,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/tabs/AllTab.vue": 1782393769.7060475,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/tabs/TemporaryTab.vue": 1781582036.0799434,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/tabs/tab-registry.ts": 1781582169.4766586,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/tabs/tab-registry.ts": 1782393772.4243252,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/tabs/OutboundClinicalTab.vue": 1781582035.6614258,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/composables/useOutboundQcViewDialog.ts": 1781589519.6184735,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/composables/useQcWorkOrderTaskViewSections.ts": 1781589505.5582125,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/composables/useInboundQcViewDialog.ts": 1781589515.800703,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/composables/useOutboundQcViewDialog.ts": 1782398591.849214,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/composables/useQcWorkOrderTaskViewSections.ts": 1782398280.5764718,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/composables/useQcWorkOrderTabUnfinishedCounts.ts": 1782394182.2860413,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/composables/useInboundQcViewDialog.ts": 1782398606.799624,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/composables/useQcViewAttachmentHelpers.ts": 1781581579.8782947,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/composables/inbound-view-sections.ts": 1781608057.8810816,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/composables/useQcWorkOrderTabPage.ts": 1781608043.655311,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/view-dialogs/QcWorkOrderSystemViewDialog.vue": 1781589553.6371684,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/composables/inbound-view-sections.ts": 1782398282.921316,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/composables/useQcWorkOrderTabPage.ts": 1782397823.6269462,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/view-dialogs/QcWorkOrderSystemViewDialog.vue": 1782398380.8308663,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/view-dialogs/OutboundQcProductTable.vue": 1781604200.1464918,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/view-dialogs/OutboundQcDiagnosisAttachments.vue": 1781581843.4276094,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/view-dialogs/view-dialog-registry.ts": 1781582168.445552,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/view-dialogs/InboundResearchDonationViewDialog.vue": 1781589531.009138,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/view-dialogs/OutboundBasicExperimentViewDialog.vue": 1781604200.1465678,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/view-dialogs/OutboundClinicalViewDialog.vue": 1781604200.291363,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/view-dialogs/InboundCellStorageViewDialog.vue": 1781589526.0101316,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/view-dialogs/TemporaryQcViewDialog.vue": 1781589531.7431345,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/view-dialogs/OutboundPilotViewDialog.vue": 1781604200.1465576,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception-check-item/index.vue": 1781563767.3726647,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/lab-test-item/index.vue": 1781585388.7276096,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/lab-test-item/ThirdPartyCompanyTab.vue": 1781761242.3112986,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/view-dialogs/view-dialog-registry.ts": 1782393780.8839016,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/view-dialogs/InboundResearchDonationViewDialog.vue": 1782398319.51777,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/view-dialogs/OutboundBasicExperimentViewDialog.vue": 1782398324.891622,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/view-dialogs/OutboundClinicalViewDialog.vue": 1782398323.6604831,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/view-dialogs/InboundCellStorageViewDialog.vue": 1782398316.2496862,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/view-dialogs/TemporaryQcViewDialog.vue": 1782398320.0266304,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/view-dialogs/OutboundPilotViewDialog.vue": 1782398324.4457653,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception-check-item/index.vue": 1782405995.2313125,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/lab-test-item/index.vue": 1782379670.980952,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/lab-test-item/ThirdPartyCompanyTab.vue": 1782379714.340431,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/my-qc-material-usage/index.vue": 1781518516.1543977,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-material/index.vue": 1781690353.3115087,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-material/QcMaterialStockCheckTab.vue": 1781563767.3635929,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-material/QcMaterialUsageTab.vue": 1782120970.241405,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-material/index.vue": 1782405945.9069998,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-material/QcMaterialStockCheckTab.vue": 1782405946.1329827,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-material/QcMaterialUsageTab.vue": 1782405945.6418679,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-material/QcMaterialBatchAttachmentDialog.vue": 1781690342.0885053,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/pre-transit-sample/index.vue": 1782206029.583706,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/pre-transit-sample/PreTransitProtocolCardList.vue": 1782263777.2928922,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue": 1782198775.9162123,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue": 1782378331.5120492,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/SampleReceptionCardList.vue": 1781585388.7346976,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/my-qc-task/index.vue": 1781582341.1215634,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/env-detect-record/index.vue": 1781585388.736309,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/my-qc-task/index.vue": 1782394624.819391,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/env-detect-record/index.vue": 1782377015.4291072,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/env-detect-item/index.vue": 1781563767.360739,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue": 1781272556.8975937,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/index.vue": 1781272556.8987281,
@@ -522,11 +539,13 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/basic-info/collection-region/CollectionProtocolNoTab.vue": 1781563767.3099859,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/assets/svg/loading.ts": 1781272558.486104,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/store/index.ts": 1781272559.227605,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/qc-menu-badge.ts": 1782394567.2213001,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/worktab.ts": 1781351182.1456184,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/table.ts": 1781272559.2465377,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/menu.ts": 1781272559.2476027,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/setting.ts": 1781661173.9923356,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/user.ts": 1782190001.5644245,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/production-menu-badge.ts": 1782404664.0879827,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/user.ts": 1782404740.3322744,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/routesAlias.ts": 1781272559.2590332,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/index.ts": 1781272559.4304278,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/core/RouteTransformer.ts": 1781272559.268035,
@@ -542,7 +561,7 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/modules/exception.ts": 1781272559.549901,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/routes/staticRoutes.ts": 1781272559.596198,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/routes/asyncRoutes.ts": 1781272559.619745,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/guards/beforeEach.ts": 1782190002.5828512,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/guards/beforeEach.ts": 1782404681.821045,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/guards/afterEach.ts": 1781272559.7019026,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/assets/svg/my-copy.svg": 1778582067.9349968,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/assets/svg/search.svg": 1778582067.9393654,
+1
View File
@@ -73,6 +73,7 @@
"file-saver": "^2.0.5",
"highlight.js": "^11.10.0",
"jsencrypt": "^3.3.2",
"jszip": "^3.10.1",
"mitt": "^3.0.1",
"nprogress": "^0.2.0",
"ohash": "^2.0.11",
+3
View File
@@ -62,6 +62,9 @@ importers:
jsencrypt:
specifier: ^3.3.2
version: 3.5.4
jszip:
specifier: ^3.10.1
version: 3.10.1
mitt:
specifier: ^3.0.1
version: 3.0.1
+3
View File
@@ -12,6 +12,7 @@ export type EnvDetectRecord = {
id: string
recordNo: string
detectTime: string
resultTime?: string
inspectorIds: string[]
inspectorNames: string[]
inspectorDisplay: string
@@ -48,6 +49,7 @@ export function fetchEnvDetectRecordDetail(id: string) {
export function fetchCreateEnvDetectRecord(data: {
detectTime: string
resultTime?: string
inspectorIds: string[]
envDetectItemId: string
resultDesc: string
@@ -64,6 +66,7 @@ export function fetchUpdateEnvDetectRecord(
id: string,
data: {
detectTime?: string
resultTime?: string
inspectorIds?: string[]
envDetectItemId?: string
resultDesc?: string
@@ -6,6 +6,7 @@ import type {
OutboundQcDetailInfusionSchedule,
OutboundQcDetailProduct
} from '@/api/qc-work-order'
import type { QcInspectionBatch } from '@/utils/qc-inspection-batch'
type PageList<T> = {
list: T[]
@@ -82,19 +83,24 @@ export type OutboundProductionInfo = {
export type OutboundQcTask = {
id?: string
taskNo?: string
itemName?: string
taskType?: string
taskTypeLabel?: string
taskStatus?: string
taskStatusLabel?: string
conclusion?: string
conclusionLabel?: string
batchId?: string
}
export type OutboundQcInfo = {
id?: string
workOrderNo?: string
workOrderStatus?: string
workOrderStatusLabel?: string
tasks?: OutboundQcTask[]
inspectionBatches?: QcInspectionBatch[]
}
export type OutboundReleaseReviewDetail = {
+3
View File
@@ -39,6 +39,7 @@ export type ProductionMaterialBatch = {
stockInNo: string
stockInDate: string
expireDate: string
productionDate?: string
catalogNo: string
batchNo: string
manufacturer: string
@@ -72,6 +73,7 @@ export type ProductionMaterialBatchOption = {
stockInNo: string
stockInDate?: string
expireDate?: string
productionDate?: string
materialId: string
materialCode: string
materialName: string
@@ -92,6 +94,7 @@ export type StockInPayload = {
stockInNo: string
stockInDate: string
expireDate: string
productionDate?: string
catalogNo: string
batchNo: string
manufacturer: string
@@ -1,4 +1,5 @@
import request from '@/utils/http'
import type { ProductionWorkOrderTabKey } from '@/utils/production-work-order-tabs'
import type {
QcWorkOrderReceptionDetail,
OutboundQcDetailApplication,
@@ -188,3 +189,50 @@ export function fetchCreateQcFromProduction(
data
})
}
export type ProductionWorkOrderTabStepCount = {
overdueIncomplete: number
unassigned: number
}
export type ProductionWorkOrderTabStepCounts = Record<
ProductionWorkOrderTabKey,
ProductionWorkOrderTabStepCount
>
export function formatProductionWorkOrderTabStepBadge(count: ProductionWorkOrderTabStepCount): string {
const fmt = (n: number) => (n > 99 ? '99+' : String(n))
return `${fmt(count.overdueIncomplete)}+${fmt(count.unassigned)}`
}
export function isProductionWorkOrderTabStepBadgeZero(count: ProductionWorkOrderTabStepCount): boolean {
return (count.overdueIncomplete || 0) === 0 && (count.unassigned || 0) === 0
}
/** 汇总可见 Tab 的步骤角标(左侧菜单展示各 Tab 数值之和) */
export function sumProductionWorkOrderTabStepCounts(
counts: Partial<ProductionWorkOrderTabStepCounts> | null | undefined,
tabKeys: ProductionWorkOrderTabKey[]
): ProductionWorkOrderTabStepCount {
return tabKeys.reduce(
(acc, key) => {
const c = counts?.[key]
acc.overdueIncomplete += c?.overdueIncomplete || 0
acc.unassigned += c?.unassigned || 0
return acc
},
{ overdueIncomplete: 0, unassigned: 0 }
)
}
export function fetchProductionWorkOrderTabStepCounts() {
return request.get<ProductionWorkOrderTabStepCounts>({
url: '/web/admin/production-work-orders/tab-step-counts'
})
}
export function fetchMineProductionWorkOrderTabStepCounts() {
return request.get<ProductionWorkOrderTabStepCounts>({
url: '/web/admin/production-work-orders/mine-tab-step-counts'
})
}
+4
View File
@@ -6,8 +6,12 @@ export type QcItemParam = {
paramCode: string
paramUnit?: string
paramType: string
options?: string
defaultValue?: string
requiredFlag: string
orderNum?: number
/** 表单编辑单选选项文本,逗号分隔(仅前端) */
_optionText?: string
}
export function fetchQcItemParams(itemSource: string, itemId: string) {
+3
View File
@@ -7,6 +7,9 @@ export type QcTaskResultItem = {
paramCode?: string
paramUnit?: string
paramType: string
options?: string
defaultValue?: string
requiredFlag?: string
paramValue: string
}
+3
View File
@@ -78,6 +78,9 @@ export type QcTask = {
assigneeUserIds?: string[]
assigneeUserNames?: string
sourceLineId?: string
batchId?: string
batchName?: string
batchNo?: string
remark?: string
createTime?: string
updateTime?: string
+35 -4
View File
@@ -1,4 +1,6 @@
import request from '@/utils/http'
import type { QcInspectionBatch } from '@/utils/qc-inspection-batch'
import type { QcWorkOrderTabKey } from '@/utils/qc-work-order-tabs'
type PageList<T> = {
list: T[]
@@ -176,6 +178,25 @@ export type QcWorkOrderDetail = QcWorkOrder & {
assigneeUserNames?: string
attachmentFiles?: Array<{ id?: string; fileUrl?: string; fileName?: string }>
}>
inspectionBatches?: QcInspectionBatch[]
}
export type QcWorkOrderTabUnfinishedCounts = Record<QcWorkOrderTabKey, number>
export function formatQcWorkOrderTabBadgeCount(count: number): string {
return count > 99 ? '99+' : String(count)
}
export function fetchQcWorkOrderTabUnfinishedCounts() {
return request.get<QcWorkOrderTabUnfinishedCounts>({
url: '/web/admin/qc-work-orders/tab-unfinished-counts'
})
}
export function fetchMineQcWorkOrderTabUnfinishedCounts() {
return request.get<QcWorkOrderTabUnfinishedCounts>({
url: '/web/admin/qc-work-orders/mine-tab-unfinished-counts'
})
}
export function fetchQcWorkOrderList(params?: {
@@ -207,10 +228,19 @@ export function fetchQcWorkOrderDetail(id: string) {
export function fetchConfigureQcWorkOrder(
id: string,
data: {
hasLab: boolean
hasThirdParty: boolean
labItemIds: string[]
thirdPartyItemIds: string[]
batches?: Array<{
id?: string
batchName?: string
assigneeUserIds: string[]
hasLab: boolean
hasThirdParty: boolean
labItemIds: string[]
thirdPartyItemIds: string[]
}>
hasLab?: boolean
hasThirdParty?: boolean
labItemIds?: string[]
thirdPartyItemIds?: string[]
labItemAssignees?: Record<string, string[]>
thirdPartyItemAssignees?: Record<string, string[]>
}
@@ -317,6 +347,7 @@ export type OutboundQcDetail = {
infusionSchedule?: OutboundQcDetailInfusionSchedule
products?: OutboundQcDetailProduct[]
tasks?: QcWorkOrderDetail['tasks']
inspectionBatches?: QcInspectionBatch[]
}
export function fetchOutboundQcDetail(id: string) {
@@ -1,4 +1,5 @@
import request from '@/utils/http'
import type { QcInspectionBatch } from '@/utils/qc-inspection-batch'
import type { QcWorkOrderReceptionDetail } from '@/api/qc-work-order'
type PageList<T> = {
@@ -74,15 +75,19 @@ export type ReceptionInfo = {
export type QcInfoTask = {
id?: string
taskNo?: string
itemName?: string
taskType?: string
taskTypeLabel?: string
taskStatus?: string
taskStatusLabel?: string
conclusion?: string
conclusionLabel?: string
batchId?: string
}
export type QcInfo = {
id?: string
workOrderNo?: string
workOrderStatus?: string
workOrderStatusLabel?: string
@@ -98,6 +103,7 @@ export type QcInfo = {
productionWorkOrderId?: string
productionWorkOrderNo?: string
tasks?: QcInfoTask[]
inspectionBatches?: QcInspectionBatch[]
}
export type ProductionInfoTask = {
+27
View File
@@ -276,6 +276,16 @@ body {
text-align: center;
background: #fd4e4e;
border-radius: 4px;
&--zero {
background: #94a3b8;
}
&--dual {
min-width: 34px;
padding: 0 6px;
letter-spacing: -0.02em;
}
}
@keyframes breathe {
@@ -520,6 +530,23 @@ body {
border-color: var(--primary, #10b981);
}
&__badge--zero {
background: #94a3b8;
}
&__badge--dual {
min-width: 34px;
padding: 0 6px;
font-size: 10px;
letter-spacing: -0.02em;
}
&__btn.is-active &__badge--zero {
background: rgba(255, 255, 255, 0.92);
color: var(--primary-dark, #059669);
border-color: var(--primary, #10b981);
}
// el-tabs 结构
&.el-tabs {
> .el-tabs__header {
@@ -0,0 +1,67 @@
<template>
<div class="qc-param-select-editor">
<div class="qc-param-select-editor__head">
单选选项
<span class="form-label-tip">多个选项用逗号分隔,第一个为默认选项</span>
</div>
<el-input
v-model="param._optionText"
placeholder="例如:阴性,阳性,待复核"
@input="syncOptions"
/>
</div>
</template>
<script setup lang="ts">
import { watch } from 'vue'
import type { QcItemParam } from '@/api/qc-item-param'
import {
formatQcParamOptionText,
parseQcParamOptionText
} from '@/utils/qc-item-param-options'
const param = defineModel<QcItemParam>('param', { required: true })
function ensureOptionText() {
if (param.value._optionText === undefined) {
param.value._optionText = formatQcParamOptionText(
param.value.options,
param.value.defaultValue
)
}
}
function syncOptions() {
const list = parseQcParamOptionText(param.value._optionText)
param.value.options = list.length ? JSON.stringify(list) : undefined
param.value.defaultValue = list[0] || undefined
}
watch(
() => param.value.paramType,
() => {
if (param.value.paramType === 'select') {
ensureOptionText()
}
},
{ immediate: true }
)
</script>
<style scoped lang="scss">
.qc-param-select-editor {
padding: 8px 0 4px;
display: flex;
flex-direction: column;
gap: 8px;
}
.qc-param-select-editor__head {
font-size: 12px;
color: var(--mint-text-muted, #64748b);
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 4px;
}
</style>
@@ -110,6 +110,7 @@
qcTaskConclusionLabel
} from '@/utils/qc-labels'
import { downloadAttachmentFile } from '@/utils/download-attachment'
import { fetchQcInspectionBatches, loadQcInspectionBatches } from '@/utils/qc-inspection-batch'
const props = defineProps<{
visible: boolean
@@ -140,6 +141,8 @@
const loading = ref(false)
const viewDetail = ref<QcTaskDetail | null>(null)
const viewMaterialUsages = ref<ViewMaterialUsageRow[]>([])
const viewBatchName = ref('')
const viewBatchNo = ref('')
const minioBaseUrl = (import.meta.env.VITE_MINIO_BASE_URL || '').replace(/\/$/, '')
function materialRowsFromApi(items: QcTaskMaterialUsageItem[]): ViewMaterialUsageRow[] {
@@ -162,11 +165,28 @@
if (!vis || !id) {
viewDetail.value = null
viewMaterialUsages.value = []
viewBatchName.value = ''
viewBatchNo.value = ''
return
}
loading.value = true
try {
const detail = await fetchQcTaskDetail(id)
viewBatchName.value = ''
viewBatchNo.value = ''
const batchId = (detail.batchId || '').trim()
const workOrderId = (detail.workOrderId || '').trim()
if (batchId && workOrderId) {
try {
const batches = await loadQcInspectionBatches(workOrderId)
const batch = batches.find((b) => b.id === batchId)
viewBatchName.value = batch?.batchName || ''
viewBatchNo.value = batch?.batchNo || ''
} catch {
viewBatchName.value = ''
viewBatchNo.value = ''
}
}
try {
const results = await fetchQcTaskResults(id)
;(detail as QcTaskDetail).results = results || []
@@ -188,6 +208,8 @@
} catch {
viewDetail.value = null
viewMaterialUsages.value = []
viewBatchName.value = ''
viewBatchNo.value = ''
} finally {
loading.value = false
}
@@ -360,6 +382,14 @@
tagType: qcBizTypeTagType(d.qcBizType || '')
},
{ label: '工单号', value: d.workOrderNo || '—' },
...(viewBatchName.value || viewBatchNo.value
? [
{
label: '检测批次',
value: [viewBatchName.value, viewBatchNo.value].filter(Boolean).join(' · ')
}
]
: []),
{
label: '工单状态',
value: d.workOrderStatus ? workOrderStatusLabel(d.workOrderStatus) : '—',
@@ -0,0 +1,314 @@
<!-- 样本查看 — 质检信息按检测批次只读展示 -->
<template>
<div class="sample-qc-batch-readonly" v-loading="loading">
<template v-if="batchSections.length">
<el-tabs
v-model="activeBatchKey"
class="panel-pill-tabs panel-pill-tabs--start sample-qc-batch-readonly__tabs"
>
<el-tab-pane
v-for="section in batchSections"
:key="section.key"
:name="section.key"
>
<template #label>
<span class="sample-qc-batch-readonly__tab-label">{{ section.batchName }}</span>
<span class="sample-qc-batch-readonly__tab-count">
{{ section.completedTaskCount }}/{{ section.taskCount }}
</span>
</template>
<div
v-if="section.batchNo || section.assigneeUserNames"
class="sample-qc-batch-readonly__meta"
>
<span v-if="section.batchNo" class="sample-qc-batch-readonly__meta-item">{{
section.batchNo
}}</span>
<span v-if="section.assigneeUserNames" class="sample-qc-batch-readonly__meta-item">
负责人:{{ section.assigneeUserNames }}
</span>
</div>
<div class="qc-detail-cards qc-detail-cards--tasks">
<div
v-for="task in section.tasks"
:key="task.id || `${section.key}-${task.taskNo}`"
class="qc-detail-card qc-detail-card--task"
>
<div class="qc-detail-card__header">
<span class="qc-detail-card__name">{{ task.itemName || '—' }}</span>
<el-tag :type="taskTypeTagType(task.taskType || '')" size="small">{{
task.taskTypeLabel || taskTypeLabel(task.taskType || '')
}}</el-tag>
</div>
<div class="qc-detail-card__body">
<div class="qc-kv">
<span class="qc-kv__label">任务号</span>
<span class="qc-kv__value">{{ task.taskNo || '—' }}</span>
</div>
<div class="qc-kv">
<span class="qc-kv__label">任务状态</span>
<span class="qc-kv__value">
<el-tag :type="taskStatusTagType(task.taskStatus)" size="small">
{{ task.taskStatusLabel || taskStatusLabel(task.taskStatus || '') }}
</el-tag>
</span>
</div>
<div class="qc-kv">
<span class="qc-kv__label">检测结论</span>
<span class="qc-kv__value">
<el-tag
v-if="task.conclusion"
:type="qcTaskConclusionTagType(task.conclusion)"
size="small"
>
{{ task.conclusionLabel || qcTaskConclusionLabel(task.conclusion) }}
</el-tag>
<span v-else class="qc-kv__muted">—</span>
</span>
</div>
</div>
<div v-if="task.id" class="qc-detail-card__foot">
<button type="button" class="qc-detail-card__link" @click.stop="emit('view-task', task)">
点击查看详情
</button>
</div>
</div>
</div>
</el-tab-pane>
</el-tabs>
</template>
<div v-else-if="!loading" class="sample-qc-batch-readonly__empty hint-muted">
暂无检测批次或任务
</div>
</div>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue'
import {
taskTypeLabel,
taskTypeTagType,
taskStatusLabel,
qcTaskConclusionLabel,
qcTaskConclusionTagType
} from '@/utils/qc-labels'
import { taskStatusTagType } from '@/utils/status-tag-registry'
import { loadQcInspectionBatches, resolveBatchSections,
type QcBatchTaskSection,
type QcInspectionBatch
} from '@/utils/qc-inspection-batch'
export type SampleQcBatchTask = {
id?: string
taskNo?: string
taskType?: string
taskTypeLabel?: string
itemName?: string
taskStatus?: string
taskStatusLabel?: string
conclusion?: string
conclusionLabel?: string
batchId?: string
}
defineOptions({ name: 'SampleQcBatchReadonly' })
const props = defineProps<{
workOrderId?: string
tasks?: SampleQcBatchTask[]
inspectionBatches?: QcInspectionBatch[]
}>()
const emit = defineEmits<{
'view-task': [task: SampleQcBatchTask]
}>()
const loading = ref(false)
const batches = ref<QcInspectionBatch[]>([])
const batchSections = ref<QcBatchTaskSection<SampleQcBatchTask>[]>([])
const activeBatchKey = ref('')
function ensureActiveBatchKey(sections: QcBatchTaskSection<SampleQcBatchTask>[]) {
if (!sections.length) {
activeBatchKey.value = ''
return
}
const keys = new Set(sections.map((s) => s.key))
if (!keys.has(activeBatchKey.value)) {
activeBatchKey.value = sections[0].key
}
}
function syncSections() {
batchSections.value = resolveBatchSections(props.inspectionBatches || batches.value, props.tasks)
ensureActiveBatchKey(batchSections.value)
}
async function loadBatches() {
const woId = (props.workOrderId || '').trim()
if (props.inspectionBatches?.length) {
batches.value = []
syncSections()
return
}
if (!woId) {
batches.value = []
syncSections()
return
}
loading.value = true
try {
batches.value = await loadQcInspectionBatches(woId)
} catch {
batches.value = []
} finally {
loading.value = false
syncSections()
}
}
watch(
() => [props.workOrderId, props.tasks, props.inspectionBatches] as const,
() => {
if (props.inspectionBatches?.length) {
batches.value = []
syncSections()
return
}
if (props.workOrderId) {
loadBatches()
return
}
syncSections()
},
{ immediate: true, deep: true }
)
</script>
<style scoped lang="scss">
.sample-qc-batch-readonly__tabs {
&.panel-pill-tabs.el-tabs > :deep(.el-tabs__header) {
margin: 0 0 10px;
}
&.panel-pill-tabs.el-tabs :deep(.el-tabs__nav-scroll) {
justify-content: flex-start;
}
&.panel-pill-tabs.el-tabs :deep(.el-tabs__item) {
min-width: 96px;
padding: 6px 14px !important;
}
}
.sample-qc-batch-readonly__tab-label {
margin-right: 6px;
}
.sample-qc-batch-readonly__tab-count {
font-size: 11px;
font-weight: 500;
opacity: 0.85;
}
.sample-qc-batch-readonly__meta {
display: flex;
flex-wrap: wrap;
gap: 8px 16px;
margin-bottom: 8px;
font-size: 12px;
color: var(--mint-text-muted, #64748b);
}
.sample-qc-batch-readonly__empty {
padding: 8px 0;
font-size: 13px;
}
.qc-detail-cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: 10px;
&--tasks {
margin-bottom: 0;
}
}
.qc-detail-card {
border: 1px solid #e2e8f0;
border-radius: 8px;
background: #fff;
overflow: hidden;
}
.qc-detail-card__header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 8px;
padding: 10px 12px;
border-bottom: 1px solid #f1f5f9;
}
.qc-detail-card__name {
font-size: 13px;
font-weight: 600;
color: #1e293b;
text-align: left;
flex: 1;
word-break: break-word;
}
.qc-detail-card__body {
padding: 10px 12px;
display: flex;
flex-direction: column;
gap: 8px;
}
.qc-detail-card__foot {
padding: 2px 12px 8px;
text-align: right;
border-top: 1px dashed #f1f5f9;
}
.qc-detail-card__link {
padding: 0;
border: none;
background: none;
font-size: 11px;
line-height: 1.4;
color: #047857;
cursor: pointer;
&:hover {
color: #065f46;
text-decoration: underline;
}
}
.qc-kv__muted {
color: var(--mint-text-muted, #334155);
font-weight: 400;
}
.qc-kv {
display: flex;
align-items: flex-start;
gap: 8px;
font-size: 12px;
}
.qc-kv__label {
color: var(--mint-text-muted, #334155);
flex-shrink: 0;
min-width: 56px;
}
.qc-kv__value {
color: #1e293b;
font-weight: 500;
word-break: break-word;
}
</style>
@@ -32,56 +32,20 @@
</div>
</div>
<div v-if="qcInfo.workOrderStatus === 'dpz' && !qcInfo.tasks?.length" class="sample-qc-hint">
<div
v-if="qcInfo.workOrderStatus === 'dpz' && !hasTaskContent"
class="sample-qc-hint"
>
工单待配置检测项目
</div>
<div v-if="qcInfo.tasks?.length" class="qc-detail-cards qc-detail-cards--tasks">
<div
v-for="task in qcInfo.tasks"
:key="task.id"
class="qc-detail-card qc-detail-card--task"
>
<div class="qc-detail-card__header">
<span class="qc-detail-card__name">{{ task.itemName || '—' }}</span>
<el-tag :type="taskTypeTagType(task.taskType || '')" size="small">{{
task.taskTypeLabel || taskTypeLabel(task.taskType || '')
}}</el-tag>
</div>
<div class="qc-detail-card__body">
<div class="qc-kv">
<span class="qc-kv__label">任务号</span>
<span class="qc-kv__value">{{ task.taskNo || '—' }}</span>
</div>
<div class="qc-kv">
<span class="qc-kv__label">任务状态</span>
<span class="qc-kv__value">
<el-tag :type="taskStatusTagType(task.taskStatus)" size="small">
{{ task.taskStatusLabel || taskStatusLabel(task.taskStatus || '') }}
</el-tag>
</span>
</div>
<div class="qc-kv">
<span class="qc-kv__label">检测结论</span>
<span class="qc-kv__value">
<el-tag
v-if="task.conclusion"
:type="qcTaskConclusionTagType(task.conclusion)"
size="small"
>
{{ task.conclusionLabel || qcTaskConclusionLabel(task.conclusion) }}
</el-tag>
<span v-else class="qc-kv__muted">—</span>
</span>
</div>
</div>
<div class="qc-detail-card__foot">
<button type="button" class="qc-detail-card__link" @click.stop="openTaskView(task)">
点击查看详情
</button>
</div>
</div>
</div>
<SampleQcBatchReadonly
v-if="hasTaskContent"
:work-order-id="qcInfo.id"
:tasks="qcInfo.tasks"
:inspection-batches="qcInfo.inspectionBatches"
@view-task="openTaskView"
/>
<div v-else-if="qcInfo.workOrderStatus !== 'dpz'" class="sample-qc-hint"> 暂无检测任务 </div>
</template>
@@ -98,17 +62,11 @@
<script setup lang="ts">
import { computed, ref } from 'vue'
import {
qcBizTypeLabel,
workOrderStatusLabel,
taskTypeLabel,
taskTypeTagType,
taskStatusLabel,
qcTaskConclusionLabel,
qcTaskConclusionTagType
} from '@/utils/qc-labels'
import { workOrderStatusTagType, taskStatusTagType } from '@/utils/status-tag-registry'
import { qcBizTypeLabel, workOrderStatusLabel } from '@/utils/qc-labels'
import { workOrderStatusTagType } from '@/utils/status-tag-registry'
import type { QcInspectionBatch } from '@/utils/qc-inspection-batch'
import QcTaskViewDialog from '@/components/business/qc-task-view-dialog/index.vue'
import SampleQcBatchReadonly from '@/components/business/sample-qc-batch-readonly/index.vue'
export type SampleQcTask = {
id?: string
@@ -124,9 +82,11 @@
conclusionLabel?: string
completedTime?: string
remark?: string
batchId?: string
}
export type SampleQcInfo = {
id?: string
workOrderNo?: string
workOrderStatus?: string
workOrderStatusLabel?: string
@@ -147,6 +107,7 @@
itemName?: string
companyName?: string
}>
inspectionBatches?: QcInspectionBatch[]
tasks?: SampleQcTask[]
}
@@ -172,6 +133,11 @@
const taskViewVisible = ref(false)
const taskViewId = ref('')
const hasTaskContent = computed(
() =>
!!(props.qcInfo?.inspectionBatches?.length || props.qcInfo?.tasks?.length)
)
const emptyHint = computed(() => {
const rec = props.sampleReception
if (!rec) {
@@ -236,74 +202,6 @@
min-width: 140px;
}
.qc-detail-cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: 10px;
&--tasks {
margin-bottom: 0;
}
}
.qc-detail-card {
border: 1px solid #e2e8f0;
border-radius: 8px;
background: #fff;
overflow: hidden;
}
.qc-detail-card__header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 8px;
padding: 10px 12px;
border-bottom: 1px solid #f1f5f9;
}
.qc-detail-card__name {
font-size: 13px;
font-weight: 600;
color: #1e293b;
text-align: left;
flex: 1;
word-break: break-word;
}
.qc-detail-card__body {
padding: 10px 12px;
display: flex;
flex-direction: column;
gap: 8px;
}
.qc-detail-card__foot {
padding: 2px 12px 8px;
text-align: right;
border-top: 1px dashed #f1f5f9;
}
.qc-detail-card__link {
padding: 0;
border: none;
background: none;
font-size: 11px;
line-height: 1.4;
color: #047857;
cursor: pointer;
&:hover {
color: #065f46;
text-decoration: underline;
}
}
.qc-kv__muted {
color: var(--mint-text-muted, #334155);
font-weight: 400;
}
.qc-kv {
display: flex;
align-items: flex-start;
@@ -4,8 +4,12 @@
<ArtSvgIcon :icon="item.meta.icon" :color="theme?.iconColor" class="mr-1 text-lg" />
<span class="text-md">{{ formatMenuTitle(item.meta.title) }}</span>
<div v-if="item.meta.showBadge" class="art-badge art-badge-horizontal" />
<div v-if="item.meta.showTextBadge" class="art-text-badge">
{{ item.meta.showTextBadge }}
<div
v-if="resolveMenuTextBadge(item)"
class="art-text-badge"
:class="menuTextBadgeClass(item)"
>
{{ resolveMenuTextBadge(item) }}
</div>
</template>
@@ -38,8 +42,8 @@
class="art-badge"
:style="{ right: level === 0 ? '10px' : '20px' }"
/>
<div v-if="item.meta.showTextBadge && level !== 0" class="art-text-badge">
{{ item.meta.showTextBadge }}
<div v-if="resolveMenuTextBadge(item)" class="art-text-badge" :class="menuTextBadgeClass(item)">
{{ resolveMenuTextBadge(item) }}
</div>
</ElMenuItem>
</template>
@@ -49,6 +53,12 @@
import { AppRouteRecord } from '@/types/router'
import { handleMenuJump } from '@/utils/navigation'
import { formatMenuTitle } from '@/utils/router'
import { storeToRefs } from 'pinia'
import { useQcMenuBadgeStore } from '@/store/modules/qc-menu-badge'
import { useProductionMenuBadgeStore } from '@/store/modules/production-menu-badge'
import { resolveQcMenuBadgeCode } from '@/utils/qc-menu-badge'
import { resolveProductionMenuBadgeCode } from '@/utils/production-menu-badge'
import { formatQcWorkOrderTabBadgeCount } from '@/api/qc-work-order'
const props = defineProps({
item: {
@@ -67,6 +77,41 @@
})
const emit = defineEmits(['close'])
const qcMenuBadgeStore = useQcMenuBadgeStore()
const productionMenuBadgeStore = useProductionMenuBadgeStore()
const { loadedMenuCodes, badgeByMenuCode } = storeToRefs(qcMenuBadgeStore)
const { loadedMenuCodes: productionLoadedMenuCodes } = storeToRefs(productionMenuBadgeStore)
function resolveMenuTextBadge(item: AppRouteRecord): string {
const productionMenuCode = resolveProductionMenuBadgeCode(item)
if (productionMenuCode && productionLoadedMenuCodes.value.includes(productionMenuCode)) {
return productionMenuBadgeStore.getBadgeText(productionMenuCode)
}
const menuCode = resolveQcMenuBadgeCode(item)
if (menuCode && loadedMenuCodes.value.includes(menuCode)) {
const count = badgeByMenuCode.value[menuCode] ?? 0
return formatQcWorkOrderTabBadgeCount(count)
}
if (item.meta.showTextBadge && props.level !== 0) {
return item.meta.showTextBadge
}
return ''
}
function menuTextBadgeClass(item: AppRouteRecord): Record<string, boolean> {
const productionMenuCode = resolveProductionMenuBadgeCode(item)
if (productionMenuCode && productionLoadedMenuCodes.value.includes(productionMenuCode)) {
return {
'art-text-badge--zero': productionMenuBadgeStore.isBadgeZero(productionMenuCode),
'art-text-badge--dual': true
}
}
const menuCode = resolveQcMenuBadgeCode(item)
if (menuCode && loadedMenuCodes.value.includes(menuCode)) {
return { 'art-text-badge--zero': (badgeByMenuCode.value[menuCode] ?? 0) === 0 }
}
return {}
}
// 过滤后的子菜单项(不包含隐藏的)
const filteredChildren = computed(() => {
@@ -157,6 +157,7 @@ $popup-menu-radius: 6px;
// 菜单高度
.el-sub-menu__title,
.el-menu-item {
position: relative;
display: flex !important;
align-items: center;
min-width: 0;
@@ -165,6 +166,15 @@ $popup-menu-radius: 6px;
line-height: $menu-height !important;
overflow: hidden !important;
.menu-title-wrap {
position: relative;
display: flex;
flex: 1;
align-items: center;
min-width: 0;
padding-right: 28px;
}
.menu-name {
flex-shrink: 1;
min-width: 0;
@@ -54,12 +54,18 @@
/>
<template #title>
<span class="menu-name">
{{ formatMenuTitle(item.meta.title) }}
</span>
<div v-if="item.meta.showBadge" class="art-badge" />
<div v-if="item.meta.showTextBadge && (level > 0 || menuOpen)" class="art-text-badge">
{{ item.meta.showTextBadge }}
<div class="menu-title-wrap">
<span class="menu-name">
{{ formatMenuTitle(item.meta.title) }}
</span>
<div v-if="item.meta.showBadge" class="art-badge" />
<div
v-if="resolveMenuTextBadge(item)"
class="art-text-badge"
:class="menuTextBadgeClass(item)"
>
{{ resolveMenuTextBadge(item) }}
</div>
</div>
</template>
</ElMenuItem>
@@ -73,6 +79,11 @@
import { filterSidebarRoutes } from '@/utils/router/sidebar-menu-filter'
import { handleMenuJump } from '@/utils/navigation'
import { useSettingStore } from '@/store/modules/setting'
import { useQcMenuBadgeStore } from '@/store/modules/qc-menu-badge'
import { useProductionMenuBadgeStore } from '@/store/modules/production-menu-badge'
import { resolveQcMenuBadgeCode } from '@/utils/qc-menu-badge'
import { resolveProductionMenuBadgeCode } from '@/utils/production-menu-badge'
import { formatQcWorkOrderTabBadgeCount } from '@/api/qc-work-order'
interface MenuTheme {
iconColor?: string
@@ -107,8 +118,12 @@
const emit = defineEmits<Emits>()
const settingStore = useSettingStore()
const qcMenuBadgeStore = useQcMenuBadgeStore()
const productionMenuBadgeStore = useProductionMenuBadgeStore()
const { menuOpen } = storeToRefs(settingStore)
const { loadedMenuCodes, badgeByMenuCode } = storeToRefs(qcMenuBadgeStore)
const { loadedMenuCodes: productionLoadedMenuCodes } = storeToRefs(productionMenuBadgeStore)
/**
* 过滤后的菜单项列表
@@ -116,6 +131,38 @@
*/
const filteredMenuItems = computed(() => filterSidebarRoutes(props.list || []))
function resolveMenuTextBadge(item: AppRouteRecord): string {
const productionMenuCode = resolveProductionMenuBadgeCode(item)
if (productionMenuCode && productionLoadedMenuCodes.value.includes(productionMenuCode)) {
return productionMenuBadgeStore.getBadgeText(productionMenuCode)
}
const menuCode = resolveQcMenuBadgeCode(item)
if (menuCode && loadedMenuCodes.value.includes(menuCode)) {
const count = badgeByMenuCode.value[menuCode] ?? 0
return formatQcWorkOrderTabBadgeCount(count)
}
const staticBadge = item.meta.showTextBadge
if (staticBadge && (props.level > 0 || menuOpen.value)) {
return staticBadge
}
return ''
}
function menuTextBadgeClass(item: AppRouteRecord): Record<string, boolean> {
const productionMenuCode = resolveProductionMenuBadgeCode(item)
if (productionMenuCode && productionLoadedMenuCodes.value.includes(productionMenuCode)) {
return {
'art-text-badge--zero': productionMenuBadgeStore.isBadgeZero(productionMenuCode),
'art-text-badge--dual': true
}
}
const menuCode = resolveQcMenuBadgeCode(item)
if (menuCode && loadedMenuCodes.value.includes(menuCode)) {
return { 'art-text-badge--zero': (badgeByMenuCode.value[menuCode] ?? 0) === 0 }
}
return {}
}
/**
* 跳转到指定页面
* @param item 菜单项数据
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_composables_useqcworkordertabpage_ts", "label": "useQcWorkOrderTabPage.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/composables/useQcWorkOrderTabPage.ts", "source_location": "L1"}, {"id": "composables_useqcworkordertabpage_shouldapplytriggersourcefilter", "label": "shouldApplyTriggerSourceFilter()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/composables/useQcWorkOrderTabPage.ts", "source_location": "L26"}, {"id": "composables_useqcworkordertabpage_useqcworkordertabpage", "label": "useQcWorkOrderTabPage()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/composables/useQcWorkOrderTabPage.ts", "source_location": "L33"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_composables_useqcworkordertabpage_ts", "target": "vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/composables/useQcWorkOrderTabPage.ts", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_composables_useqcworkordertabpage_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/views/quality-inspection/qc-work-order/composables/useQcWorkOrderTabPage.ts", "source_location": "L2", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_composables_useqcworkordertabpage_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_qc_task", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/composables/useQcWorkOrderTabPage.ts", "source_location": "L3", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_composables_useqcworkordertabpage_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_qc_mine_task_groups", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/composables/useQcWorkOrderTabPage.ts", "source_location": "L4", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_composables_useqcworkordertabpage_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_qc_work_order_tabs", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/composables/useQcWorkOrderTabPage.ts", "source_location": "L12", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_composables_useqcworkordertabpage_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_qc_work_order_category", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/composables/useQcWorkOrderTabPage.ts", "source_location": "L23", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_composables_useqcworkordertabpage_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_qc_work_order_card_meta", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/composables/useQcWorkOrderTabPage.ts", "source_location": "L24", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_composables_useqcworkordertabpage_ts", "target": "composables_useqcworkordertabpage_shouldapplytriggersourcefilter", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/composables/useQcWorkOrderTabPage.ts", "source_location": "L26", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_composables_useqcworkordertabpage_ts", "target": "composables_useqcworkordertabpage_useqcworkordertabpage", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/composables/useQcWorkOrderTabPage.ts", "source_location": "L33", "weight": 1.0}], "raw_caLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_api_storage_release_review_ts", "label": "storage-release-review.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L1"}, {"id": "api_storage_release_review_fetchstoragereleasereviewlist", "label": "fetchStorageReleaseReviewList()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L137"}, {"id": "api_storage_release_review_fetchstoragereleasereviewdetail", "label": "fetchStorageReleaseReviewDetail()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L151"}, {"id": "api_storage_release_review_submitstoragereleasereview", "label": "submitStorageReleaseReview()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L157"}, {"id": "api_storage_release_review_batchreviewstoragerelease", "label": "batchReviewStorageRelease()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L171"}, {"id": "api_storage_release_review_storesample", "label": "storeSample()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L183"}, {"id": "api_storage_release_review_batchstoresample", "label": "batchStoreSample()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L189"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_storage_release_review_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/storage-release-review.ts", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_storage_release_review_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/storage-release-review.ts", "source_location": "L2", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_storage_release_review_ts", "target": "api_storage_release_review_fetchstoragereleasereviewlist", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L137", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_storage_release_review_ts", "target": "api_storage_release_review_fetchstoragereleasereviewdetail", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L151", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_storage_release_review_ts", "target": "api_storage_release_review_submitstoragereleasereview", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L157", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_storage_release_review_ts", "target": "api_storage_release_review_batchreviewstoragerelease", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L171", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_storage_release_review_ts", "target": "api_storage_release_review_storesample", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L183", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_storage_release_review_ts", "target": "api_storage_release_review_batchstoresample", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L189", "weight": 1.0}], "raw_calls": [{"caller_nid": "api_storage_release_review_fetchstoragereleasereviewlist", "callee": "get", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L145"}, {"caller_nid": "api_storage_release_review_fetchstoragereleasereviewdetail", "callee": "get", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L152"}, {"caller_nid": "api_storage_release_revLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_api_outbound_release_review_ts", "label": "outbound-release-review.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/outbound-release-review.ts", "source_location": "L1"}, {"id": "api_outbound_release_review_fetchoutboundreleasereviewlist", "label": "fetchOutboundReleaseReviewList()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/outbound-release-review.ts", "source_location": "L164"}, {"id": "api_outbound_release_review_fetchoutboundreleasereviewdetail", "label": "fetchOutboundReleaseReviewDetail()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/outbound-release-review.ts", "source_location": "L182"}, {"id": "api_outbound_release_review_submitoutboundreleasereview", "label": "submitOutboundReleaseReview()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/outbound-release-review.ts", "source_location": "L188"}, {"id": "api_outbound_release_review_batchreviewoutboundrelease", "label": "batchReviewOutboundRelease()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/outbound-release-review.ts", "source_location": "L202"}, {"id": "api_outbound_release_review_fetchoutboundreleasematerial", "label": "fetchOutboundReleaseMaterial()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/outbound-release-review.ts", "source_location": "L214"}, {"id": "api_outbound_release_review_fetchreleasereviewlist", "label": "fetchReleaseReviewList()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/outbound-release-review.ts", "source_location": "L220"}, {"id": "api_outbound_release_review_updatereleasereview", "label": "updateReleaseReview()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/outbound-release-review.ts", "source_location": "L226"}, {"id": "api_outbound_release_review_deletereleasereview", "label": "deleteReleaseReview()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/outbound-release-review.ts", "source_location": "L233"}, {"id": "api_outbound_release_review_saveoutboundreleasematerial", "label": "saveOutboundReleaseMaterial()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/outbound-release-review.ts", "source_location": "L239"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_outbound_release_review_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/outbound-release-review.ts", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_outbound_release_review_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/outbound-release-review.ts", "source_location": "L2", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_outbound_release_review_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_qc_inspection_batch", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/outbound-release-review.ts", "source_location": "L9", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_outbound_release_review_ts", "target": "api_outbound_release_review_fetchoutboundreleasereviewlist", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/outbound-release-review.ts", "source_location": "L164", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_outbound_release_review_ts", "target": "api_outbound_release_review_fetchoutboundreleasereviewdetail", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/outbound-release-review.ts", "source_location": "L182", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_outbound_release_review_ts", "target": "api_outbound_release_review_submitoutboundreleasereview", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/outbound-release-review.ts", "source_location": "L188", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_outbound_release_review_ts", "target": "api_outbound_release_review_batchreviewoutboundrelease", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/outbound-release-review.ts", "source_locaLine truncated
Loaded 100 of 323 files, more files were not shown because too many files have changed in this diff. Show more