提交
This commit is contained in:
1 parent
ffc89d9b57
commit
c2cba3e668
53 files changed
+11794
-6264
No files matched your search
BIN
Binary file not shown.
+1
-1
@@ -1 +1 @@
|
||||
47712
|
||||
6907
|
||||
@@ -48,6 +48,10 @@ from app.services.pl_cert_service import (
|
||||
nearest_expire_date,
|
||||
save_owner_certs,
|
||||
)
|
||||
from app.services.pl_cert_required_service import (
|
||||
validate_personnel_required_certs,
|
||||
validate_vehicle_save_certs,
|
||||
)
|
||||
from app.services.pl_carrier_service import (
|
||||
carrier_eligible_for_transport,
|
||||
carrier_qualification_complete,
|
||||
@@ -409,6 +413,15 @@ def create_vehicle(
|
||||
if existing:
|
||||
raise HTTPException(status_code=400, detail="车牌号已存在")
|
||||
|
||||
validate_vehicle_save_certs(
|
||||
db,
|
||||
tenant_no,
|
||||
vehicle_type=payload.vehicle_type,
|
||||
trailer_no=payload.trailer_no,
|
||||
cert_rows=payload.cert_list,
|
||||
personnel_rows=payload.personnel_list,
|
||||
)
|
||||
|
||||
vehicle = PlVehicle(
|
||||
id=generate_uuid(),
|
||||
tenant_no=tenant_no,
|
||||
@@ -434,6 +447,7 @@ def create_vehicle(
|
||||
tenant_no,
|
||||
user_id=user.id,
|
||||
dept_id=user.dept_id,
|
||||
vehicle_type=payload.vehicle_type,
|
||||
)
|
||||
save_owner_certs(
|
||||
db,
|
||||
@@ -483,6 +497,26 @@ def update_vehicle(
|
||||
exclude_unset=True, exclude={"personnel_list", "cert_list"}
|
||||
)
|
||||
|
||||
effective_vehicle_type = data.get("vehicle_type", vehicle.vehicle_type)
|
||||
effective_trailer_no = data.get("trailer_no", vehicle.trailer_no)
|
||||
effective_cert_rows = (
|
||||
cert_list
|
||||
if cert_list is not None
|
||||
else list_owner_certs(db, tenant_no, "vehicle", vehicle.id)
|
||||
)
|
||||
effective_personnel_rows = personnel_list
|
||||
if effective_personnel_rows is None:
|
||||
effective_personnel_rows = load_vehicle_personnel_rows(db, tenant_no, vehicle.id)
|
||||
|
||||
validate_vehicle_save_certs(
|
||||
db,
|
||||
tenant_no,
|
||||
vehicle_type=effective_vehicle_type,
|
||||
trailer_no=effective_trailer_no,
|
||||
cert_rows=effective_cert_rows,
|
||||
personnel_rows=effective_personnel_rows,
|
||||
)
|
||||
|
||||
for key, value in data.items():
|
||||
setattr(vehicle, key, value)
|
||||
|
||||
@@ -497,6 +531,7 @@ def update_vehicle(
|
||||
tenant_no,
|
||||
user_id=user.id,
|
||||
dept_id=user.dept_id,
|
||||
vehicle_type=effective_vehicle_type,
|
||||
)
|
||||
if cert_list is not None:
|
||||
save_owner_certs(
|
||||
@@ -581,6 +616,9 @@ def _personnel_to_detail(
|
||||
def list_personnel(
|
||||
q: Optional[str] = Query(default=None, description="模糊搜索姓名/手机号"),
|
||||
status: Optional[str] = Query(default=None, description="状态"),
|
||||
personnel_affiliation: Optional[str] = Query(
|
||||
default=None, alias="personnelAffiliation", description="人员归属 self/carrier"
|
||||
),
|
||||
page: int = Query(default=1, ge=1),
|
||||
page_size: int = Query(default=20, ge=1, le=100, alias="pageSize"),
|
||||
db: Session = Depends(get_db),
|
||||
@@ -595,6 +633,9 @@ def list_personnel(
|
||||
if status:
|
||||
query = query.filter(PlPersonnel.status == status)
|
||||
|
||||
if personnel_affiliation:
|
||||
query = query.filter(PlPersonnel.personnel_affiliation == personnel_affiliation)
|
||||
|
||||
if q:
|
||||
query = query.filter(
|
||||
or_(
|
||||
@@ -626,6 +667,12 @@ def create_personnel(
|
||||
) -> PersonnelDetailRead:
|
||||
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
|
||||
|
||||
validate_personnel_required_certs(
|
||||
payload.cert_list,
|
||||
payload.personnel_type,
|
||||
subject_label=f"「{payload.personnel_name}」",
|
||||
)
|
||||
|
||||
personnel = PlPersonnel(
|
||||
id=generate_uuid(),
|
||||
tenant_no=tenant_no,
|
||||
@@ -633,6 +680,8 @@ def create_personnel(
|
||||
phone_no=payload.phone_no,
|
||||
age=payload.age,
|
||||
id_card_no=payload.id_card_no,
|
||||
personnel_type=payload.personnel_type,
|
||||
personnel_affiliation=payload.personnel_affiliation,
|
||||
status=payload.status,
|
||||
remark=payload.remark,
|
||||
create_by=user.id,
|
||||
@@ -684,6 +733,20 @@ def update_personnel(
|
||||
cert_list = payload.cert_list
|
||||
data = payload.model_dump(exclude_unset=True, exclude={"cert_list"})
|
||||
|
||||
effective_personnel_type = data.get("personnel_type", personnel.personnel_type)
|
||||
effective_cert_rows = (
|
||||
cert_list
|
||||
if cert_list is not None
|
||||
else list_owner_certs(db, tenant_no, "personnel", personnel.id)
|
||||
)
|
||||
subject_name = data.get("personnel_name", personnel.personnel_name)
|
||||
|
||||
validate_personnel_required_certs(
|
||||
effective_cert_rows,
|
||||
effective_personnel_type,
|
||||
subject_label=f"「{subject_name}」",
|
||||
)
|
||||
|
||||
for key, value in data.items():
|
||||
setattr(personnel, key, value)
|
||||
|
||||
|
||||
@@ -72,6 +72,7 @@ from app.services.pl_plan_service import (
|
||||
sync_transport_prefill_for_plan,
|
||||
update_plan_product_certs,
|
||||
validate_create_full,
|
||||
validate_hazard_cert_ready_for_transport,
|
||||
)
|
||||
|
||||
router = APIRouter(prefix="/plan", tags=["运输计划-计划管理"])
|
||||
@@ -1079,6 +1080,19 @@ def archive_plan(
|
||||
|
||||
already_archived = plan.archive_status == "archived"
|
||||
|
||||
if not already_archived:
|
||||
products = (
|
||||
db.query(PlPlanProduct)
|
||||
.filter(
|
||||
PlPlanProduct.plan_id == plan.id,
|
||||
PlPlanProduct.tenant_no == tenant_no,
|
||||
PlPlanProduct.del_flag == 0,
|
||||
)
|
||||
.order_by(PlPlanProduct.order_num)
|
||||
.all()
|
||||
)
|
||||
validate_hazard_cert_ready_for_transport(db, tenant_no, plan, products)
|
||||
|
||||
if payload is None:
|
||||
if already_archived:
|
||||
raise HTTPException(status_code=400, detail="计划已归档,请提交归档表单以调整内容")
|
||||
|
||||
@@ -90,6 +90,18 @@ class PlPersonnel(Base):
|
||||
phone_no = Column(String(32), nullable=True, comment="手机号(非必填)")
|
||||
age = Column(Integer, nullable=True, comment="年龄")
|
||||
id_card_no = Column(String(32), nullable=True, comment="身份证号")
|
||||
personnel_type = Column(
|
||||
String(20),
|
||||
nullable=False,
|
||||
default="both",
|
||||
comment="人员类型 driver-驾驶员 escort-押运员 both-驾驶员兼押运员",
|
||||
)
|
||||
personnel_affiliation = Column(
|
||||
String(20),
|
||||
nullable=False,
|
||||
default="self",
|
||||
comment="人员归属 self-山峡 carrier-非山峡",
|
||||
)
|
||||
status = Column(CHAR(1), nullable=False, default="1", comment="状态 1-在职 0-停用")
|
||||
tenant_no = Column(String(64), nullable=False, default="shanxia2.0", comment="租户编号")
|
||||
order_num = Column(Integer, nullable=True, comment="排序号")
|
||||
|
||||
@@ -272,6 +272,18 @@ class PersonnelBase(BaseModel):
|
||||
phone_no: Optional[str] = Field(default=None, max_length=32, alias="phoneNo", description="手机号")
|
||||
age: Optional[int] = Field(default=None, description="年龄")
|
||||
id_card_no: Optional[str] = Field(default=None, max_length=32, alias="idCardNo", description="身份证号")
|
||||
personnel_type: str = Field(
|
||||
...,
|
||||
max_length=20,
|
||||
alias="personnelType",
|
||||
description="人员类型 driver/escort/both",
|
||||
)
|
||||
personnel_affiliation: str = Field(
|
||||
default="self",
|
||||
max_length=20,
|
||||
alias="personnelAffiliation",
|
||||
description="人员归属 self-山峡 carrier-非山峡",
|
||||
)
|
||||
status: str = Field(default="1", max_length=1, description="状态 1-在职 0-停用")
|
||||
remark: Optional[str] = None
|
||||
|
||||
@@ -286,6 +298,10 @@ class PersonnelUpdate(BaseModel):
|
||||
phone_no: Optional[str] = Field(default=None, max_length=32, alias="phoneNo")
|
||||
age: Optional[int] = None
|
||||
id_card_no: Optional[str] = Field(default=None, max_length=32, alias="idCardNo")
|
||||
personnel_type: Optional[str] = Field(default=None, max_length=20, alias="personnelType")
|
||||
personnel_affiliation: Optional[str] = Field(
|
||||
default=None, max_length=20, alias="personnelAffiliation"
|
||||
)
|
||||
status: Optional[str] = Field(default=None, max_length=1)
|
||||
remark: Optional[str] = None
|
||||
cert_list: Optional[list[CertRow]] = Field(default=None, alias="certList")
|
||||
|
||||
@@ -0,0 +1,314 @@
|
||||
"""运输计划 - 必备证书校验"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Iterable, Optional, Sequence
|
||||
|
||||
from fastapi import HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.models.pl_models import PlPersonnel
|
||||
from app.schemas.pl.base_data import CertRow, VehiclePersonnelRow
|
||||
from app.services.pl_cert_service import list_owner_certs
|
||||
|
||||
VEHICLE_TYPE_SELF = "self"
|
||||
VEHICLE_TYPE_CARRIER = "carrier"
|
||||
|
||||
SELF_HEAD_REQUIRED = ("行驶证", "营运证", "保险单")
|
||||
SELF_TRAILER_REQUIRED = ("行驶证", "营运证", "保险单", "罐检报告")
|
||||
|
||||
CARRIER_HEAD_REQUIRED = ("行驶证", "营运证")
|
||||
CARRIER_TRAILER_REQUIRED = ("行驶证", "营运证")
|
||||
|
||||
PERSONNEL_DRIVER_REQUIRED = ("身份证", "驾驶证", "从业资格证")
|
||||
PERSONNEL_ESCORT_REQUIRED = ("身份证", "从业资格证")
|
||||
|
||||
VEHICLE_PART_HEAD = "head"
|
||||
VEHICLE_PART_TRAILER = "trailer"
|
||||
|
||||
|
||||
def match_required_cert_name(actual_name: str | None, required_name: str) -> bool:
|
||||
name = (actual_name or "").strip()
|
||||
if not name:
|
||||
return False
|
||||
if required_name == "从业资格证":
|
||||
return (
|
||||
name == "从业资格证"
|
||||
or name == "危化品从业资格证"
|
||||
or "从业资格证" in name
|
||||
)
|
||||
return name == required_name
|
||||
|
||||
|
||||
def _cert_row_complete(cert: CertRow | dict, required_name: str) -> bool:
|
||||
if isinstance(cert, dict):
|
||||
cert_name = cert.get("cert_name") or cert.get("certName")
|
||||
cert_no = cert.get("cert_no") or cert.get("certNo")
|
||||
expire_date = cert.get("expire_date") or cert.get("expireDate")
|
||||
attachment_urls = cert.get("attachment_urls") or cert.get("attachmentUrls")
|
||||
else:
|
||||
cert_name = cert.cert_name
|
||||
cert_no = cert.cert_no
|
||||
expire_date = cert.expire_date
|
||||
attachment_urls = cert.attachment_urls
|
||||
|
||||
if not match_required_cert_name(cert_name, required_name):
|
||||
return False
|
||||
if not (cert_no or "").strip():
|
||||
return False
|
||||
if not expire_date:
|
||||
return False
|
||||
if not (attachment_urls or "").strip():
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def _find_cert_for_required(
|
||||
cert_rows: Sequence[CertRow | dict],
|
||||
required_name: str,
|
||||
*,
|
||||
vehicle_part: str | None = None,
|
||||
) -> CertRow | dict | None:
|
||||
for cert in cert_rows:
|
||||
if isinstance(cert, dict):
|
||||
name = cert.get("cert_name") or cert.get("certName")
|
||||
part = cert.get("vehicle_part") or cert.get("vehiclePart")
|
||||
else:
|
||||
name = cert.cert_name
|
||||
part = cert.vehicle_part
|
||||
if vehicle_part is not None and (part or VEHICLE_PART_HEAD) != vehicle_part:
|
||||
continue
|
||||
if match_required_cert_name(name, required_name):
|
||||
return cert
|
||||
return None
|
||||
|
||||
|
||||
def _missing_field_labels(cert: CertRow | dict | None, required_name: str) -> list[str]:
|
||||
if cert is None:
|
||||
return ["证号", "有效期", "附件"]
|
||||
missing: list[str] = []
|
||||
if isinstance(cert, dict):
|
||||
cert_no = cert.get("cert_no") or cert.get("certNo")
|
||||
expire_date = cert.get("expire_date") or cert.get("expireDate")
|
||||
attachment_urls = cert.get("attachment_urls") or cert.get("attachmentUrls")
|
||||
cert_name = cert.get("cert_name") or cert.get("certName")
|
||||
else:
|
||||
cert_no = cert.cert_no
|
||||
expire_date = cert.expire_date
|
||||
attachment_urls = cert.attachment_urls
|
||||
cert_name = cert.cert_name
|
||||
|
||||
if not match_required_cert_name(cert_name, required_name):
|
||||
return ["证号", "有效期", "附件"]
|
||||
if not (cert_no or "").strip():
|
||||
missing.append("证号")
|
||||
if not expire_date:
|
||||
missing.append("有效期")
|
||||
if not (attachment_urls or "").strip():
|
||||
missing.append("附件")
|
||||
return missing
|
||||
|
||||
|
||||
def collect_required_cert_errors(
|
||||
cert_rows: Sequence[CertRow | dict],
|
||||
required_names: Iterable[str],
|
||||
*,
|
||||
part_label: str = "",
|
||||
) -> list[str]:
|
||||
errors: list[str] = []
|
||||
prefix = f"{part_label}" if part_label else ""
|
||||
vehicle_part = None
|
||||
if part_label in ("主车", "head"):
|
||||
vehicle_part = VEHICLE_PART_HEAD
|
||||
elif part_label in ("挂车", "trailer"):
|
||||
vehicle_part = VEHICLE_PART_TRAILER
|
||||
|
||||
for required_name in required_names:
|
||||
cert = _find_cert_for_required(
|
||||
cert_rows, required_name, vehicle_part=vehicle_part
|
||||
)
|
||||
missing = _missing_field_labels(cert, required_name)
|
||||
if missing:
|
||||
label = f"{prefix}「{required_name}」" if prefix else f"「{required_name}」"
|
||||
errors.append(f"{label}缺少:{'、'.join(missing)}")
|
||||
return errors
|
||||
|
||||
|
||||
def get_required_personnel_cert_names(personnel_type: str | None) -> tuple[str, ...]:
|
||||
ptype = (personnel_type or "").strip()
|
||||
if ptype == "escort":
|
||||
return PERSONNEL_ESCORT_REQUIRED
|
||||
if ptype in ("driver", "both"):
|
||||
return PERSONNEL_DRIVER_REQUIRED
|
||||
return PERSONNEL_DRIVER_REQUIRED
|
||||
|
||||
|
||||
def validate_personnel_type_role_compat(personnel_type: str | None, role: str | None) -> None:
|
||||
ptype = (personnel_type or "").strip()
|
||||
r = (role or "").strip()
|
||||
if not ptype or not r:
|
||||
return
|
||||
if ptype == "driver" and r == "escort":
|
||||
raise HTTPException(status_code=400, detail="驾驶员类型人员不可关联为押运员")
|
||||
if ptype == "escort" and r == "driver":
|
||||
raise HTTPException(status_code=400, detail="押运员类型人员不可关联为驾驶员")
|
||||
|
||||
|
||||
def validate_personnel_required_certs(
|
||||
cert_rows: Sequence[CertRow | dict],
|
||||
personnel_type: str | None,
|
||||
*,
|
||||
subject_label: str = "",
|
||||
) -> None:
|
||||
ptype = (personnel_type or "").strip()
|
||||
if not ptype:
|
||||
raise HTTPException(status_code=400, detail="人员类型不能为空")
|
||||
required = get_required_personnel_cert_names(ptype)
|
||||
errors = collect_required_cert_errors(cert_rows, required)
|
||||
if errors:
|
||||
prefix = f"人员{subject_label}" if subject_label else "人员"
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"必备证书未齐全:{prefix}{';'.join(errors)}",
|
||||
)
|
||||
|
||||
|
||||
def validate_self_vehicle_required_certs(
|
||||
cert_rows: Sequence[CertRow | dict],
|
||||
*,
|
||||
trailer_no: str | None = None,
|
||||
) -> None:
|
||||
errors: list[str] = []
|
||||
errors.extend(
|
||||
collect_required_cert_errors(
|
||||
cert_rows, SELF_HEAD_REQUIRED, part_label="主车"
|
||||
)
|
||||
)
|
||||
if (trailer_no or "").strip():
|
||||
errors.extend(
|
||||
collect_required_cert_errors(
|
||||
cert_rows, SELF_TRAILER_REQUIRED, part_label="挂车"
|
||||
)
|
||||
)
|
||||
if errors:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"必备证书未齐全:{';'.join(errors)}",
|
||||
)
|
||||
|
||||
|
||||
def validate_carrier_vehicle_required_certs(
|
||||
cert_rows: Sequence[CertRow | dict],
|
||||
*,
|
||||
trailer_no: str | None = None,
|
||||
) -> None:
|
||||
errors: list[str] = []
|
||||
errors.extend(
|
||||
collect_required_cert_errors(
|
||||
cert_rows, CARRIER_HEAD_REQUIRED, part_label="主车"
|
||||
)
|
||||
)
|
||||
if (trailer_no or "").strip():
|
||||
errors.extend(
|
||||
collect_required_cert_errors(
|
||||
cert_rows, CARRIER_TRAILER_REQUIRED, part_label="挂车"
|
||||
)
|
||||
)
|
||||
if errors:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"必备证书未齐全:{';'.join(errors)}",
|
||||
)
|
||||
|
||||
|
||||
def validate_vehicle_personnel_affiliation(
|
||||
db: Session,
|
||||
tenant_no: str,
|
||||
vehicle_type: str | None,
|
||||
personnel_rows: Sequence[VehiclePersonnelRow],
|
||||
) -> None:
|
||||
vtype = (vehicle_type or "").strip()
|
||||
if vtype not in (VEHICLE_TYPE_SELF, VEHICLE_TYPE_CARRIER):
|
||||
return
|
||||
label = "山峡" if vtype == VEHICLE_TYPE_SELF else "非山峡"
|
||||
for row in personnel_rows:
|
||||
personnel_id = (row.personnel_id or "").strip()
|
||||
if not personnel_id:
|
||||
continue
|
||||
personnel = (
|
||||
db.query(PlPersonnel)
|
||||
.filter(
|
||||
PlPersonnel.id == personnel_id,
|
||||
PlPersonnel.tenant_no == tenant_no,
|
||||
PlPersonnel.del_flag == 0,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if not personnel:
|
||||
continue
|
||||
affiliation = (personnel.personnel_affiliation or VEHICLE_TYPE_SELF).strip()
|
||||
if affiliation != vtype:
|
||||
name = (row.personnel_name or personnel.personnel_name or personnel_id).strip()
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"关联人员「{name}」归属与车辆类型不匹配,{label}车辆仅可关联{label}人员",
|
||||
)
|
||||
|
||||
|
||||
def validate_vehicle_personnel_cert_links(
|
||||
db: Session,
|
||||
tenant_no: str,
|
||||
personnel_rows: Sequence[VehiclePersonnelRow],
|
||||
) -> None:
|
||||
for row in personnel_rows:
|
||||
personnel_id = (row.personnel_id or "").strip()
|
||||
if not personnel_id:
|
||||
continue
|
||||
personnel = (
|
||||
db.query(PlPersonnel)
|
||||
.filter(
|
||||
PlPersonnel.id == personnel_id,
|
||||
PlPersonnel.tenant_no == tenant_no,
|
||||
PlPersonnel.del_flag == 0,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if not personnel:
|
||||
continue
|
||||
validate_personnel_type_role_compat(personnel.personnel_type, row.role)
|
||||
cert_rows = list_owner_certs(db, tenant_no, "personnel", personnel_id)
|
||||
role = (row.role or "").strip()
|
||||
required = (
|
||||
PERSONNEL_DRIVER_REQUIRED
|
||||
if role == "driver"
|
||||
else PERSONNEL_ESCORT_REQUIRED
|
||||
)
|
||||
errors = collect_required_cert_errors(cert_rows, required)
|
||||
if errors:
|
||||
name = (row.personnel_name or personnel.personnel_name or personnel_id).strip()
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=(
|
||||
f"关联人员「{name}」必备证书未齐全:{';'.join(errors)}"
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def validate_vehicle_save_certs(
|
||||
db: Session,
|
||||
tenant_no: str,
|
||||
*,
|
||||
vehicle_type: str | None,
|
||||
trailer_no: str | None,
|
||||
cert_rows: Sequence[CertRow | dict],
|
||||
personnel_rows: Sequence[VehiclePersonnelRow] | None = None,
|
||||
) -> None:
|
||||
vtype = (vehicle_type or "").strip()
|
||||
if vtype == VEHICLE_TYPE_SELF:
|
||||
validate_self_vehicle_required_certs(cert_rows, trailer_no=trailer_no)
|
||||
elif vtype == VEHICLE_TYPE_CARRIER:
|
||||
validate_carrier_vehicle_required_certs(cert_rows, trailer_no=trailer_no)
|
||||
if vtype in (VEHICLE_TYPE_SELF, VEHICLE_TYPE_CARRIER) and personnel_rows:
|
||||
validate_vehicle_personnel_affiliation(
|
||||
db, tenant_no, vtype, personnel_rows
|
||||
)
|
||||
validate_vehicle_personnel_cert_links(db, tenant_no, personnel_rows)
|
||||
@@ -18,6 +18,7 @@ def upsert_personnel(
|
||||
*,
|
||||
user_id: str,
|
||||
dept_id: str,
|
||||
vehicle_type: str = "self",
|
||||
) -> str:
|
||||
"""按 personnelId → phone → name 弱匹配 upsert,返回 personnel_id。"""
|
||||
personnel: Optional[PlPersonnel] = None
|
||||
@@ -65,12 +66,18 @@ def upsert_personnel(
|
||||
personnel.update_by = user_id
|
||||
return personnel.id
|
||||
|
||||
affiliation = (vehicle_type or "self").strip()
|
||||
if affiliation not in ("self", "carrier"):
|
||||
affiliation = "self"
|
||||
|
||||
personnel = PlPersonnel(
|
||||
id=generate_uuid(),
|
||||
tenant_no=tenant_no,
|
||||
personnel_name=name,
|
||||
phone_no=phone,
|
||||
age=row.age,
|
||||
personnel_type="both",
|
||||
personnel_affiliation=affiliation,
|
||||
status="1",
|
||||
create_by=user_id,
|
||||
create_dept_id=dept_id,
|
||||
@@ -88,6 +95,7 @@ def save_vehicle_personnel(
|
||||
*,
|
||||
user_id: str,
|
||||
dept_id: str,
|
||||
vehicle_type: str = "self",
|
||||
) -> None:
|
||||
"""软删旧关联后批量插入;校验默认司机唯一。"""
|
||||
default_count = sum(
|
||||
@@ -107,7 +115,12 @@ def save_vehicle_personnel(
|
||||
raise HTTPException(status_code=400, detail="仅司机角色可设为默认")
|
||||
|
||||
personnel_id = upsert_personnel(
|
||||
db, tenant_no, row, user_id=user_id, dept_id=dept_id
|
||||
db,
|
||||
tenant_no,
|
||||
row,
|
||||
user_id=user_id,
|
||||
dept_id=dept_id,
|
||||
vehicle_type=vehicle_type,
|
||||
)
|
||||
link = PlVehiclePersonnel(
|
||||
id=generate_uuid(),
|
||||
|
||||
@@ -122,6 +122,7 @@
|
||||
"120": "Community 120",
|
||||
"121": "Community 121",
|
||||
"122": "Community 122",
|
||||
"123": "Community 123",
|
||||
"124": "Community 124",
|
||||
"125": "Community 125",
|
||||
"126": "Community 126",
|
||||
@@ -148,6 +149,7 @@
|
||||
"147": "Community 147",
|
||||
"148": "Community 148",
|
||||
"149": "Community 149",
|
||||
"150": "Community 150",
|
||||
"151": "Community 151",
|
||||
"152": "Community 152",
|
||||
"153": "Community 153",
|
||||
@@ -155,9 +157,5 @@
|
||||
"155": "Community 155",
|
||||
"156": "Community 156",
|
||||
"157": "Community 157",
|
||||
"158": "Community 158",
|
||||
"159": "Community 159",
|
||||
"160": "Community 160",
|
||||
"162": "Community 162",
|
||||
"169": "Community 169"
|
||||
"162": "Community 162"
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
# Graph Report - backend (2026-07-12)
|
||||
|
||||
## Corpus Check
|
||||
- 245 files · ~172,306 words
|
||||
- 248 files · ~173,671 words
|
||||
- Verdict: corpus is large enough that graph structure adds value.
|
||||
|
||||
## Summary
|
||||
- 5436 nodes · 38642 edges · 161 communities (159 shown, 2 thin omitted)
|
||||
- Extraction: 51% EXTRACTED · 49% INFERRED · 0% AMBIGUOUS · INFERRED: 18828 edges (avg confidence: 0.5)
|
||||
- 5502 nodes · 38811 edges · 159 communities (157 shown, 2 thin omitted)
|
||||
- Extraction: 51% EXTRACTED · 49% INFERRED · 0% AMBIGUOUS · INFERRED: 18852 edges (avg confidence: 0.5)
|
||||
- Token cost: 0 input · 0 output
|
||||
|
||||
## Graph Freshness
|
||||
- Built from commit: `7b1d4c87`
|
||||
- Built from commit: `ffc89d9b`
|
||||
- Run `git rev-parse HEAD` and compare to check if the graph is stale.
|
||||
- Run `graphify update .` after code changes (no API cost).
|
||||
|
||||
@@ -138,6 +138,7 @@
|
||||
- [[_COMMUNITY_Community 120|Community 120]]
|
||||
- [[_COMMUNITY_Community 121|Community 121]]
|
||||
- [[_COMMUNITY_Community 122|Community 122]]
|
||||
- [[_COMMUNITY_Community 123|Community 123]]
|
||||
- [[_COMMUNITY_Community 124|Community 124]]
|
||||
- [[_COMMUNITY_Community 125|Community 125]]
|
||||
- [[_COMMUNITY_Community 126|Community 126]]
|
||||
@@ -154,6 +155,7 @@
|
||||
- [[_COMMUNITY_Community 138|Community 138]]
|
||||
- [[_COMMUNITY_Community 148|Community 148]]
|
||||
- [[_COMMUNITY_Community 149|Community 149]]
|
||||
- [[_COMMUNITY_Community 150|Community 150]]
|
||||
- [[_COMMUNITY_Community 151|Community 151]]
|
||||
- [[_COMMUNITY_Community 152|Community 152]]
|
||||
- [[_COMMUNITY_Community 153|Community 153]]
|
||||
@@ -161,11 +163,7 @@
|
||||
- [[_COMMUNITY_Community 155|Community 155]]
|
||||
- [[_COMMUNITY_Community 156|Community 156]]
|
||||
- [[_COMMUNITY_Community 157|Community 157]]
|
||||
- [[_COMMUNITY_Community 158|Community 158]]
|
||||
- [[_COMMUNITY_Community 159|Community 159]]
|
||||
- [[_COMMUNITY_Community 160|Community 160]]
|
||||
- [[_COMMUNITY_Community 162|Community 162]]
|
||||
- [[_COMMUNITY_Community 169|Community 169]]
|
||||
|
||||
## God Nodes (most connected - your core abstractions)
|
||||
1. `Session` - 1521 edges
|
||||
@@ -191,139 +189,139 @@
|
||||
- `Any` --uses--> `OperLogMeta` [INFERRED]
|
||||
scripts/migrate_dibang_data.py → app/core/oper_log_context.py
|
||||
|
||||
## Communities (161 total, 2 thin omitted)
|
||||
## Communities (159 total, 2 thin omitted)
|
||||
|
||||
### Community 0 - "Community 0"
|
||||
Cohesion: 0.10
|
||||
Nodes (112): date, DbUserContext, int, Session, str, VehicleMaintenanceApplyCreate, VehicleMaintenanceApplyRead, VehicleMaintenanceApplyReminder (+104 more)
|
||||
Cohesion: 0.12
|
||||
Nodes (102): date, DbUserContext, int, Session, str, VehicleMaintenanceApplyCreate, VehicleMaintenanceApplyRead, VehicleMaintenanceApplyReminder (+94 more)
|
||||
|
||||
### Community 1 - "Community 1"
|
||||
Cohesion: 0.35
|
||||
Nodes (219): Any, bool, str, UploadFile, CRM 线索管理 Pydantic Schemas, CRM 客户管理 Pydantic Schemas, CRM 商机管理 Pydantic Schemas, CRM 客户公海池规则 Pydantic Schemas (+211 more)
|
||||
|
||||
### Community 2 - "Community 2"
|
||||
Cohesion: 0.22
|
||||
Nodes (29): Any, int, Session, str, create_instance(), create_process_def(), create_task(), get_all_tasks_for_business() (+21 more)
|
||||
Cohesion: 0.08
|
||||
Nodes (54): CrmContract, CrmContractProductPriceTier, date, datetime, Decimal, Query, _apply_contract_entity_fields(), _apply_contract_scalar_fields() (+46 more)
|
||||
|
||||
### Community 3 - "Community 3"
|
||||
Cohesion: 0.07
|
||||
Nodes (58): CRM 合同 API 路由 — GET/PUT/DELETE/POST 对齐原 Java CRM, str, CRM 合同 Pydantic Schemas — 对照 CrmContractBo / CrmContractVo, ContractApprovalActionBody, ContractApprovalApproveBody, ContractApprovalByContractBody, ContractApprovalExportBody, ContractApprovalListBody (+50 more)
|
||||
Cohesion: 0.06
|
||||
Nodes (53): str, CRM 合同 Pydantic Schemas — 对照 CrmContractBo / CrmContractVo, ContractApprovalActionBody, ContractApprovalApproveBody, ContractApprovalByContractBody, ContractApprovalExportBody, ContractApprovalListBody, ContractApprovalListQuery (+45 more)
|
||||
|
||||
### Community 4 - "Community 4"
|
||||
Cohesion: 0.07
|
||||
Nodes (112): bool, DbUserContext, int, 运输计划管理 - 基础数据 API(承运商、车辆、人员), PlCarrier, PlVehicle, Session, str (+104 more)
|
||||
Cohesion: 0.05
|
||||
Nodes (109): DbUserContext, int, 运输计划管理 - 基础数据 API(承运商、车辆、人员), PlVehicle, Session, str, VehicleRead, date (+101 more)
|
||||
|
||||
### Community 5 - "Community 5"
|
||||
Cohesion: 0.21
|
||||
Nodes (36): register_quote_routes(), bool, CRM 报价单 CRUD — 对照 CrmQuoteServiceImpl.java(P2 全量), Session, str, accept_quote(), create_quote(), _enrich_contact_fields() (+28 more)
|
||||
Cohesion: 0.16
|
||||
Nodes (44): register_quote_routes(), bool, CRM 报价单 CRUD — 对照 CrmQuoteServiceImpl.java(P2 全量), Session, str, accept_quote(), create_quote(), _enrich_contact_fields() (+36 more)
|
||||
|
||||
### Community 6 - "Community 6"
|
||||
Cohesion: 0.05
|
||||
Nodes (78): 运输计划管理 - 基础数据 Schema(承运商、车辆、人员、证书), Session, str, TransportInventoryLinkRead, str, TransportSiteLinkRead, CarrierAuditReject, CarrierBase (+70 more)
|
||||
Cohesion: 0.16
|
||||
Nodes (12): Decimal, str, VehicleRechargeApplyCreate, VehicleRechargeApplyListResponse, VehicleRechargeApplyOption, VehicleRechargeApplyRead, VehicleRechargeApplyUpdate, VehicleRechargeDisburseBoardListResponse (+4 more)
|
||||
|
||||
### Community 7 - "Community 7"
|
||||
Cohesion: 0.06
|
||||
Nodes (63): int, PlPlan, PlTransport, Session, str, UploadFile, Any, PlPlanTransport (+55 more)
|
||||
Nodes (163): bool, PlCarrier, VehicleCreate, VehicleUpdate, bool, DbUserContext, float, int (+155 more)
|
||||
|
||||
### Community 8 - "Community 8"
|
||||
Cohesion: 0.04
|
||||
Nodes (118): Any, bool, CRM 产品模块 CRUD(产品分类 + 产品 + 附件), int, Session, str, SysOss, int (+110 more)
|
||||
Nodes (117): Any, bool, CRM 产品模块 CRUD(产品分类 + 产品 + 附件), int, Session, str, SysOss, int (+109 more)
|
||||
|
||||
### Community 9 - "Community 9"
|
||||
Cohesion: 0.07
|
||||
Nodes (72): CertRead, date, int, Session, str, int, Session, str (+64 more)
|
||||
Cohesion: 0.04
|
||||
Nodes (107): Any, bool, CertRow, Session, str, VehiclePersonnelRow, CertRead, CertRow (+99 more)
|
||||
|
||||
### Community 10 - "Community 10"
|
||||
Cohesion: 0.05
|
||||
Nodes (126): bool, Decimal, int, PlPlan, PlPlanProduct, PlTransport, PlVehicle, Session (+118 more)
|
||||
Cohesion: 0.03
|
||||
Nodes (137): Decimal, PlPlanProduct, Session, str, Decimal, PlPlanChange, 计划变更记录表 pl_plan_change, 计划产品明细表 pl_plan_product (+129 more)
|
||||
|
||||
### Community 11 - "Community 11"
|
||||
Cohesion: 0.11
|
||||
Nodes (98): date, DbUserContext, int, Session, str, VehicleUreaApplyCreate, VehicleUreaApplyRead, VehicleUreaApplyUpdate (+90 more)
|
||||
Nodes (101): date, DbUserContext, int, Session, str, VehicleUreaApplyCreate, VehicleUreaApplyRead, VehicleUreaApplyUpdate (+93 more)
|
||||
|
||||
### Community 12 - "Community 12"
|
||||
Cohesion: 0.06
|
||||
Nodes (169): _apply_default_invoice_info_to_customers(), category_add(), category_delete(), category_tree(), category_update(), clue_assign(), clue_check_convert(), clue_check_duplicate() (+161 more)
|
||||
Cohesion: 0.05
|
||||
Nodes (227): attachment_preview_url(), create_dept(), create_dict_data(), create_dict_type(), create_menu(), create_post(), create_role_management(), create_site() (+219 more)
|
||||
|
||||
### Community 13 - "Community 13"
|
||||
Cohesion: 0.05
|
||||
Nodes (44): get_plan_arrange_info(), patch_plan_product_certs(), 更新计划危化品产品购买证/运输证(不触发安排运输), 更新计划危化品产品购买证/运输证(不触发安排运输), 更新计划危化品产品购买证/运输证(不触发安排运输), 更新计划危化品产品购买证/运输证(不触发安排运输), 更新计划危化品产品购买证/运输证(不触发安排运输), 更新计划危化品产品购买证/运输证(不触发安排运输) (+36 more)
|
||||
Cohesion: 0.04
|
||||
Nodes (46): get_plan_arrange_info(), patch_plan_product_certs(), 更新计划危化品产品购买证/运输证(不触发安排运输), 更新计划危化品产品购买证/运输证(不触发安排运输), 更新计划危化品产品购买证/运输证(不触发安排运输), 更新计划危化品产品购买证/运输证(不触发安排运输), 更新计划危化品产品购买证/运输证(不触发安排运输), 更新计划危化品产品购买证/运输证(不触发安排运输) (+38 more)
|
||||
|
||||
### Community 14 - "Community 14"
|
||||
Cohesion: 0.05
|
||||
Nodes (74): BxUserContext, Session, str, UploadFile, Any, bool, bytes, int (+66 more)
|
||||
Cohesion: 0.06
|
||||
Nodes (60): Any, bool, bytes, int, str, Exception, bool, bytes (+52 more)
|
||||
|
||||
### Community 15 - "Community 15"
|
||||
Cohesion: 0.11
|
||||
Nodes (126): date, DbUserContext, int, PlanCreateFull, PlanDriverFeedbackUpdateBody, PlanSiteFeedbackUpdateBody, PlPlan, Session (+118 more)
|
||||
Cohesion: 0.10
|
||||
Nodes (132): date, DbUserContext, int, PlanCreateFull, PlanDriverFeedbackUpdateBody, PlanSiteFeedbackUpdateBody, PlPlan, Session (+124 more)
|
||||
|
||||
### Community 16 - "Community 16"
|
||||
Cohesion: 0.03
|
||||
Nodes (67): invoice_info_list(), 按客户ID查询开票信息(与 by-customer 等价), 按客户ID查询开票信息(与 by-customer 等价), 按客户ID查询开票信息(与 by-customer 等价), 按客户ID查询开票信息(与 by-customer 等价), 分页查询供方列表 — Java: CrmSupplierInfoController.list, 分页查询供方列表 — Java: CrmSupplierInfoController.list, 分页查询供方列表 — Java: CrmSupplierInfoController.list (+59 more)
|
||||
Cohesion: 0.02
|
||||
Nodes (104): 分页查询供方列表 — Java: CrmSupplierInfoController.list, 分页查询供方列表 — Java: CrmSupplierInfoController.list, 分页查询供方列表 — Java: CrmSupplierInfoController.list, 分页查询供方列表 — Java: CrmSupplierInfoController.list, 分页查询供方列表 — Java: CrmSupplierInfoController.list, 分页查询供方列表 — Java: CrmSupplierInfoController.list, 分页查询供方列表 — Java: CrmSupplierInfoController.list, 分页查询供方列表 — Java: CrmSupplierInfoController.list (+96 more)
|
||||
|
||||
### Community 17 - "Community 17"
|
||||
Cohesion: 0.09
|
||||
Nodes (57): Any, bool, CrmContract, datetime, int, Query, Session, str (+49 more)
|
||||
Cohesion: 0.08
|
||||
Nodes (70): CRM 合同子模块路由 — 变更/模板/审批独立页, register_contract_sub_routes(), Any, bool, CrmContract, datetime, int, Query (+62 more)
|
||||
|
||||
### Community 18 - "Community 18"
|
||||
Cohesion: 0.16
|
||||
Nodes (33): Any, bool, BxUserContext, BxWorkflowNode, float, int, ReimburseApplyOut, Session (+25 more)
|
||||
Nodes (36): Any, bool, BxUserContext, BxWorkflowNode, float, int, ReimburseApplyOut, Session (+28 more)
|
||||
|
||||
### Community 19 - "Community 19"
|
||||
Cohesion: 0.19
|
||||
Nodes (18): str, Any, bool, Session, str, CRM 模块 Excel 导出列定义(字段与字典类型对齐 Java *Vo @ExcelDictFormat)。, _yes_no(), _build_workbook() (+10 more)
|
||||
Cohesion: 0.06
|
||||
Nodes (31): 供方详情 — Java: CrmSupplierInfoController.getInfo, 供方详情 — Java: CrmSupplierInfoController.getInfo, 供方详情 — Java: CrmSupplierInfoController.getInfo, 供方详情 — Java: CrmSupplierInfoController.getInfo, 供方详情 — Java: CrmSupplierInfoController.getInfo, 供方详情 — Java: CrmSupplierInfoController.getInfo, 供方详情 — Java: CrmSupplierInfoController.getInfo, 供方详情 — Java: CrmSupplierInfoController.getInfo (+23 more)
|
||||
|
||||
### Community 20 - "Community 20"
|
||||
Cohesion: 0.08
|
||||
Nodes (40): AttachmentUploadResponse, ChangePasswordRequest, Config, LoginRequest, LoginResponse, Pydantic schemas - 用户认证, ResetPasswordRequest, UserCreate (+32 more)
|
||||
Cohesion: 0.07
|
||||
Nodes (41): AttachmentUploadResponse, ChangePasswordRequest, Config, LoginRequest, LoginResponse, Pydantic schemas - 用户认证, ResetPasswordRequest, UserCreate (+33 more)
|
||||
|
||||
### Community 21 - "Community 21"
|
||||
Cohesion: 0.11
|
||||
Nodes (57): Any, bool, int, Session, str, Any, bool, CRM 线索 CRUD(线索主表 + 转化/分配/无效/等级操作) (+49 more)
|
||||
Nodes (58): Any, bool, int, Session, str, Any, bool, CRM 线索 CRUD(线索主表 + 转化/分配/无效/等级操作) (+50 more)
|
||||
|
||||
### Community 22 - "Community 22"
|
||||
Cohesion: 0.19
|
||||
Nodes (37): bool, DbInboundOutbound, int, PublicInventoryFeedback, PublicInventoryPageRead, UploadFile, bool, int (+29 more)
|
||||
Cohesion: 0.09
|
||||
Nodes (75): bool, DbInboundOutbound, int, PublicInventoryFeedback, PublicInventoryPageRead, Session, str, UploadFile (+67 more)
|
||||
|
||||
### Community 23 - "Community 23"
|
||||
Cohesion: 0.09
|
||||
Nodes (116): date, DbUserContext, int, Session, str, VehicleRechargeApplyCreate, VehicleRechargeApplyRead, VehicleRechargeApplyUpdate (+108 more)
|
||||
Cohesion: 0.11
|
||||
Nodes (108): date, DbUserContext, int, Session, str, VehicleRechargeApplyCreate, VehicleRechargeApplyRead, VehicleRechargeApplyUpdate (+100 more)
|
||||
|
||||
### Community 24 - "Community 24"
|
||||
Cohesion: 0.25
|
||||
Nodes (18): BxUserContext, Session, str, bool, BxUserContext, Session, str, create_expense_item() (+10 more)
|
||||
|
||||
### Community 25 - "Community 25"
|
||||
Cohesion: 0.14
|
||||
Nodes (29): Any, bool, BxUserContext, BxWorkflowNode, float, int, Session, str (+21 more)
|
||||
Cohesion: 0.11
|
||||
Nodes (35): Any, bool, BxUserContext, BxWorkflowNode, float, int, Session, str (+27 more)
|
||||
|
||||
### Community 26 - "Community 26"
|
||||
Cohesion: 0.18
|
||||
Nodes (50): Any, CRM 商机 CRUD(主表 + 阶段/赢输单/统计/看板/漏斗), CrmOpportunity, date, datetime, Decimal, int, Session (+42 more)
|
||||
Nodes (49): Any, CRM 商机 CRUD(主表 + 阶段/赢输单/统计/看板/漏斗), CrmOpportunity, date, datetime, Decimal, int, Session (+41 more)
|
||||
|
||||
### Community 27 - "Community 27"
|
||||
Cohesion: 0.10
|
||||
Nodes (58): bool, date, DbInboundOutbound, DbPurchasePlan, float, PlPlan, PlPlanProduct, PlTransport (+50 more)
|
||||
Nodes (58): bool, date, datetime, DbInboundOutbound, DbPurchasePlan, float, PlPlan, PlPlanProduct (+50 more)
|
||||
|
||||
### Community 28 - "Community 28"
|
||||
Cohesion: 0.12
|
||||
Nodes (30): Session, str, _append_user_scope_conditions(), apply_data_scope(), _collect_scope_ids(), _get_child_dept_ids(), get_user_dept_id(), 数据权限过滤(V0.3:基于角色 data_scope 的查询级行级过滤) data_scope 值: 1 = 全部数据 2 = 本部门数据 3 (+22 more)
|
||||
Nodes (32): bool, Session, str, _append_user_scope_conditions(), apply_data_scope(), assert_row_in_data_scope(), _collect_scope_ids(), _get_child_dept_ids() (+24 more)
|
||||
|
||||
### Community 29 - "Community 29"
|
||||
Cohesion: 0.10
|
||||
Nodes (90): Any, register_contract_routes(), CRM 合同子模块路由 — 变更/模板/审批独立页, register_contract_sub_routes(), Any, bool, CRM 合同 CRUD — 对照 CrmContractServiceImpl.java(列表/详情 P1), Session (+82 more)
|
||||
Cohesion: 0.08
|
||||
Nodes (85): CRM 合同 API 路由 — GET/PUT/DELETE/POST 对齐原 Java CRM, register_contract_routes(), bool, CRM 合同 CRUD — 对照 CrmContractServiceImpl.java(列表/详情 P1), Session, str, batch_assign_owner(), batch_submit_approval() (+77 more)
|
||||
|
||||
### Community 30 - "Community 30"
|
||||
Cohesion: 0.16
|
||||
Nodes (51): AdvanceApplyCreate, AdvanceApplyOut, AdvanceApplyUpdate, AdvanceApprovalCreate, BxUserContext, int, Session, str (+43 more)
|
||||
Cohesion: 0.36
|
||||
Nodes (31): AdvanceApplyCreate, AdvanceApplyOut, AdvanceApplyUpdate, AdvanceApprovalCreate, BxUserContext, int, Session, str (+23 more)
|
||||
|
||||
### Community 31 - "Community 31"
|
||||
Cohesion: 0.12
|
||||
Nodes (44): 公共接口(Web 登录、刷新 Token、登出;用户模型对齐 docs/sql/shanxia2.0.sql 基线), refresh_token(), bool, Redis, Request, str, _T, bool (+36 more)
|
||||
Cohesion: 0.13
|
||||
Nodes (43): 公共接口(Web 登录、刷新 Token、登出;用户模型对齐 docs/sql/shanxia2.0.sql 基线), refresh_token(), bool, Redis, Request, str, _T, bool (+35 more)
|
||||
|
||||
### Community 32 - "Community 32"
|
||||
Cohesion: 0.07
|
||||
Nodes (118): DbUserContext, FreightSettlementDisburseBody, FreightSettlementSubmitApprovalBody, FreightSettlementUpdate, int, Session, str, bool (+110 more)
|
||||
Nodes (123): DbUserContext, FreightSettlementDisburseBody, FreightSettlementSubmitApprovalBody, FreightSettlementUpdate, int, Session, str, bool (+115 more)
|
||||
|
||||
### Community 33 - "Community 33"
|
||||
Cohesion: 0.15
|
||||
@@ -331,11 +329,11 @@ Nodes (30): Any, bool, int, Session, str, approve_quote(), can_user_approve(), c
|
||||
|
||||
### Community 34 - "Community 34"
|
||||
Cohesion: 0.04
|
||||
Nodes (48): create_plan_full(), _generate_plan_no(), 创建运输计划(完整表单:合同产品 + 可选运输安排), 创建运输计划(完整表单:合同产品 + 可选运输安排), 创建运输计划(完整表单:合同产品 + 可选运输安排), 创建运输计划(完整表单:合同产品 + 可选预选车辆,不自动安排运输), 创建运输计划(完整表单:合同产品 + 可选预选车辆,不自动安排运输), 创建运输计划(完整表单:合同产品 + 可选预选车辆,不自动安排运输) (+40 more)
|
||||
Nodes (48): PlInventoryTransaction, PlInventoryWarning, 计划-运输关联表 pl_plan_transport, 运费结算表 pl_freight_settlement, 库存流水表 pl_inventory_transaction, 运费结算表 pl_freight_settlement, 结算单-运输关联表 pl_freight_settlement_transport, 结算单-运输关联表 pl_freight_settlement_transport (+40 more)
|
||||
|
||||
### Community 35 - "Community 35"
|
||||
Cohesion: 0.21
|
||||
Nodes (60): InboundOutboundRead, bool, PurchasePlanRead, PurchasePlanWithFillLink, DbUserContext, DbWeighingSlip, Session, str (+52 more)
|
||||
Cohesion: 0.17
|
||||
Nodes (39): DbPurchasePlan, DbUserContext, Session, str, InboundOutboundRead, _apply_plan_product_update(), _apply_plan_update(), _base_query() (+31 more)
|
||||
|
||||
### Community 36 - "Community 36"
|
||||
Cohesion: 0.40
|
||||
@@ -346,24 +344,24 @@ Cohesion: 0.13
|
||||
Nodes (40): Any, bool, date, int, Session, str, Session, str (+32 more)
|
||||
|
||||
### Community 38 - "Community 38"
|
||||
Cohesion: 0.18
|
||||
Nodes (44): AdvanceIssueRequest, BxUserContext, int, ReimburseApplyOut, Session, str, datetime, str (+36 more)
|
||||
Cohesion: 0.17
|
||||
Nodes (64): AdvanceIssueRequest, BxUserContext, int, ReimburseApplyOut, Session, str, BxUserContext, int (+56 more)
|
||||
|
||||
### Community 39 - "Community 39"
|
||||
Cohesion: 0.14
|
||||
Nodes (40): DbInboundOutbound, DbUserContext, Session, str, Session, str, user_info_from_context(), _apply_inventory_product_update() (+32 more)
|
||||
Cohesion: 0.21
|
||||
Nodes (37): DbInboundOutbound, DbUserContext, Session, str, CreateLinkForPlanPayload, _apply_inventory_product_update(), _apply_inventory_update(), _base_query() (+29 more)
|
||||
|
||||
### Community 40 - "Community 40"
|
||||
Cohesion: 0.15
|
||||
Nodes (33): CRM 报价单 API 路由 — 挂接到 crm.router, CRM 报价单 Pydantic Schemas, BaseModel, QuoteAcceptBody, QuoteApprovalApproveBody, QuoteApprovalListBody, QuoteApprovalRefreshNamesBody, QuoteApprovalWithdrawBody (+25 more)
|
||||
Cohesion: 0.12
|
||||
Nodes (31): CRM 报价单 API 路由 — 挂接到 crm.router, CRM 报价单 Pydantic Schemas, QuoteAcceptBody, QuoteApprovalApproveBody, QuoteApprovalListBody, QuoteApprovalRefreshNamesBody, QuoteApprovalWithdrawBody, QuoteCreateBody (+23 more)
|
||||
|
||||
### Community 41 - "Community 41"
|
||||
Cohesion: 0.07
|
||||
Nodes (42): _db_pool_keepalive_loop(), _error_json(), lifespan(), _mask_secret(), Exception, int, Request, str (+34 more)
|
||||
Nodes (41): _db_pool_keepalive_loop(), _error_json(), lifespan(), _mask_secret(), Exception, int, Request, str (+33 more)
|
||||
|
||||
### Community 42 - "Community 42"
|
||||
Cohesion: 0.35
|
||||
Nodes (30): BxUserContext, int, Session, str, approve_reimburse(), approved_by_me(), _build_details_data(), create_reimburse() (+22 more)
|
||||
Cohesion: 0.05
|
||||
Nodes (44): Any, datetime, Decimal, calculate_quote_amount(), _decimal_value(), _parse_optional_datetime(), _period_key(), Java: statisticsOverview (+36 more)
|
||||
|
||||
### Community 43 - "Community 43"
|
||||
Cohesion: 0.13
|
||||
@@ -371,39 +369,39 @@ Nodes (37): Any, bool, Decimal, int, Session, str, batch_insert_quote_products()
|
||||
|
||||
### Community 44 - "Community 44"
|
||||
Cohesion: 0.07
|
||||
Nodes (95): Any, int, Session, str, Any, bool, CRM 客户 CRUD(客户主表 + 转移/成交/公海池操作), CrmCustomer (+87 more)
|
||||
Nodes (98): pool_rule_toggle(), Any, int, Session, str, Any, bool, CRM 客户 CRUD(客户主表 + 转移/成交/公海池操作) (+90 more)
|
||||
|
||||
### Community 45 - "Community 45"
|
||||
Cohesion: 0.13
|
||||
Nodes (15): Any, bool, int, str, get_wechat_pay_client(), get_wechat_pay_v2(), 微信支付工具模块 支持微信支付 V3 API 文档参考: https://pay.weixin.qq.com/docs/v3/apis/index.html, 统一下单接口 (V3) Args: out_trade_no: 商户订单号 total_amo (+7 more)
|
||||
|
||||
### Community 46 - "Community 46"
|
||||
Cohesion: 0.13
|
||||
Nodes (30): Any, bool, CRM 供方信息 CRUD — 对照 CrmSupplierInfoServiceImpl.java, int, Session, str, _apply_list_filters(), create_supplier_info() (+22 more)
|
||||
Cohesion: 0.18
|
||||
Nodes (11): generate_contract_no(), Java: generateContractNo — {供方编码}/X-JT + yyyyMMdd + 3位流水, Java: generateContractNo — {供方编码}/X-JT + yyyyMMdd + 3位流水, Java: generateContractNo — {供方编码}/X-JT{yyyyMMdd}{3位序号}, Java: generateContractNo — {供方编码}/X-JT{yyyyMMdd}{3位序号}, Java: generateContractNo — {供方编码}/X-JT{yyyyMMdd}{3位序号}, Java: generateContractNo — {供方编码}/X-JT{yyyyMMdd}{3位序号}, Java: generateContractNo — {供方编码}/X-JT{yyyyMMdd}{3位序号} (+3 more)
|
||||
|
||||
### Community 47 - "Community 47"
|
||||
Cohesion: 0.16
|
||||
Nodes (33): Any, bool, int, Session, str, Any, Session, str (+25 more)
|
||||
|
||||
### Community 48 - "Community 48"
|
||||
Cohesion: 0.14
|
||||
Nodes (121): date, DbUserContext, int, Session, str, bool, DbUserContext, float (+113 more)
|
||||
Cohesion: 0.16
|
||||
Nodes (94): date, DbUserContext, int, Session, str, DocumentReminderRead, DocumentReminderUpdate, InventoryLedgerResponse (+86 more)
|
||||
|
||||
### Community 49 - "Community 49"
|
||||
Cohesion: 0.24
|
||||
Nodes (20): Any, bool, str, _actor_ids(), _actor_names(), assert_row_deletable_by_creator(), assert_rows_deletable_by_creator(), can_delete_by_creator() (+12 more)
|
||||
Cohesion: 0.38
|
||||
Nodes (22): DbUserContext, int, Session, str, PlTransportAddress, 运输通用地址库 pl_transport_address, 运输通用地址库 pl_transport_address, 运输通用地址库 pl_transport_address (+14 more)
|
||||
|
||||
### Community 50 - "Community 50"
|
||||
Cohesion: 0.12
|
||||
Nodes (31): datetime, int, str, datetime, date, datetime, str, 审批轮次过滤 — 驳回后重新提交时,进度接口仅展示当前轮次。 (+23 more)
|
||||
Nodes (35): datetime, int, str, datetime, date, datetime, str, 审批轮次过滤 — 驳回后重新提交时,进度接口仅展示当前轮次。 (+27 more)
|
||||
|
||||
### Community 51 - "Community 51"
|
||||
Cohesion: 0.16
|
||||
Nodes (31): Any, bool, CrmContractProductPriceTier, date, Decimal, int, Session, str (+23 more)
|
||||
Cohesion: 0.17
|
||||
Nodes (32): Any, bool, CrmContractProductPriceTier, date, Decimal, int, Session, str (+24 more)
|
||||
|
||||
### Community 52 - "Community 52"
|
||||
Cohesion: 0.08
|
||||
Nodes (25): batch_assign_owner(), download_contract_pdf(), Java: batchAssignOwner, Java: downloadContractPdf — 返回合同文件 URL/路径, Java: batchAssignOwner, Java: batchAssignOwner, Java: batchAssignOwner, Java: batchAssignOwner (+17 more)
|
||||
Cohesion: 0.25
|
||||
Nodes (8): Java Controller TODO — 保持 pending,记录备注, Java Controller TODO — 保持 pending,记录备注, Java Controller TODO — 保持 pending,记录备注, Java Controller TODO — 保持 pending,记录备注, Java Controller TODO — 保持 pending,记录备注, Java Controller TODO — 保持 pending,记录备注, Java Controller TODO — 保持 pending,记录备注, submit_change_approval()
|
||||
|
||||
### Community 53 - "Community 53"
|
||||
Cohesion: 0.14
|
||||
@@ -414,56 +412,56 @@ Cohesion: 0.23
|
||||
Nodes (10): Any, bool, int, Session, str, camel_to_snake(), CRUDBase, get_model_attr() (+2 more)
|
||||
|
||||
### Community 55 - "Community 55"
|
||||
Cohesion: 0.23
|
||||
Nodes (30): Engine, int, Path, build_id_maps(), clip_text(), count_rows(), export_mapping(), fetch_rows() (+22 more)
|
||||
Cohesion: 0.15
|
||||
Nodes (35): Engine, Path, build_id_maps(), clip_text(), count_rows(), export_mapping(), fetch_rows(), legacy_id_to_str() (+27 more)
|
||||
|
||||
### Community 56 - "Community 56"
|
||||
Cohesion: 0.22
|
||||
Nodes (12): Any, bool, str, WfProcessInstance, _LocalCallbackFn, _dispatch_local(), 工作流回调服务 —— 流程状态变更时通知业务系统, 注册本地回调函数。当 callback_url 以 path 开头时直接调用 fn。 (+4 more)
|
||||
Nodes (29): Any, int, Session, str, create_instance(), create_process_def(), create_task(), get_all_tasks_for_business() (+21 more)
|
||||
|
||||
### Community 57 - "Community 57"
|
||||
Cohesion: 0.46
|
||||
Nodes (13): bool, date, PlCarrier, str, bool, assert_carrier_eligible_for_transport(), carrier_agreement_expired(), carrier_audit_approved() (+5 more)
|
||||
Cohesion: 0.22
|
||||
Nodes (49): InboundOutboundRead, bool, PurchasePlanRead, PurchasePlanWithFillLink, DbUserContext, DbWeighingSlip, Session, str (+41 more)
|
||||
|
||||
### Community 58 - "Community 58"
|
||||
Cohesion: 0.19
|
||||
Nodes (20): int, str, Any, bool, int, Session, str, business_type_from_path() (+12 more)
|
||||
Nodes (21): DbUserContext, Session, str, Session, str, int, Session, str (+13 more)
|
||||
|
||||
### Community 59 - "Community 59"
|
||||
Cohesion: 0.33
|
||||
Nodes (10): Any, int, Session, str, list_ethnics(), 民族码表 CRUD(全局 sys_ethnic), _row_to_dict(), 民族码表 sys_ethnic(国标 GB/T 3304,全局) (+2 more)
|
||||
Cohesion: 0.24
|
||||
Nodes (20): Any, bool, str, _actor_ids(), _actor_names(), assert_row_deletable_by_creator(), assert_rows_deletable_by_creator(), can_delete_by_creator() (+12 more)
|
||||
|
||||
### Community 60 - "Community 60"
|
||||
Cohesion: 0.22
|
||||
Nodes (9): Java: withdrawApproval, Java: withdrawApproval, Java: withdrawApproval, Java: withdrawApproval, Java: withdrawApproval, Java: withdrawApproval, Java: withdrawApproval, Java: withdrawApproval (+1 more)
|
||||
Cohesion: 0.19
|
||||
Nodes (18): str, Any, bool, Session, str, CRM 模块 Excel 导出列定义(字段与字典类型对齐 Java *Vo @ExcelDictFormat)。, _yes_no(), _build_workbook() (+10 more)
|
||||
|
||||
### Community 61 - "Community 61"
|
||||
Cohesion: 0.24
|
||||
Nodes (25): DbUserContext, float, Session, str, Session, str, DashboardCargoStat, DashboardPartnerCargoStat (+17 more)
|
||||
|
||||
### Community 62 - "Community 62"
|
||||
Cohesion: 0.10
|
||||
Nodes (21): _build_contract_filters(), export_contracts(), Java: validateContractTypeAndProductsPresence, Java: queryList for export, Java: queryList for export, Java: queryList for export, Java: queryList for export, Java: buildQueryWrapper (+13 more)
|
||||
Cohesion: 0.08
|
||||
Nodes (40): Any, Any, str, Any, 与门户 JWT / apply_data_scope 所需的 user_info 字段对齐。, _build_customer_address(), _change_row_to_dict(), contract_row_to_dict() (+32 more)
|
||||
|
||||
### Community 63 - "Community 63"
|
||||
Cohesion: 0.08
|
||||
Nodes (25): 技术栈, 1. 创建虚拟环境, 项目结构, 1. 创建虚拟环境, 2. 安装依赖, 2. 安装依赖, 3. 配置环境变量, 快速开始 (+17 more)
|
||||
|
||||
### Community 64 - "Community 64"
|
||||
Cohesion: 0.05
|
||||
Nodes (46): PlInventoryTransaction, PlInventoryWarning, 计划-运输关联表 pl_plan_transport, 运费结算表 pl_freight_settlement, 库存流水表 pl_inventory_transaction, 运费结算表 pl_freight_settlement, 结算单-运输关联表 pl_freight_settlement_transport, 结算单-运输关联表 pl_freight_settlement_transport (+38 more)
|
||||
Cohesion: 0.21
|
||||
Nodes (27): approve_task(), _assert_wf_instance_in_scope(), _business_type_label(), create_process_def(), delete_process_def(), _enrich_task_list(), get_business_all_tasks(), _get_business_form_fields() (+19 more)
|
||||
|
||||
### Community 65 - "Community 65"
|
||||
Cohesion: 0.28
|
||||
Nodes (8): date, datetime, str, _date_to_str(), _dt_to_str(), PagedQuery, PagedResponse, bx 原系统 API 响应包装(code=0 / msg / data.rows),与门户 success_response 分离。
|
||||
Cohesion: 0.12
|
||||
Nodes (33): BxUserContext, int, Session, str, Any, str, bool, BxUserContext (+25 more)
|
||||
|
||||
### Community 66 - "Community 66"
|
||||
Cohesion: 0.14
|
||||
Nodes (28): str, T, bool, date, datetime, int, Session, str (+20 more)
|
||||
Cohesion: 0.18
|
||||
Nodes (24): bool, date, datetime, int, Session, str, ReimburseApplyOut, allocate_reimburse_title_seq() (+16 more)
|
||||
|
||||
### Community 67 - "Community 67"
|
||||
Cohesion: 0.21
|
||||
Nodes (17): bool, int, Redis, Request, str, _claims_from_auth(), _coerce_post_method_override_to_get(), _get_redis_client() (+9 more)
|
||||
Cohesion: 0.20
|
||||
Nodes (18): bool, int, Redis, Request, str, _claims_from_auth(), _coerce_post_method_override_to_get(), _get_redis_client() (+10 more)
|
||||
|
||||
### Community 68 - "Community 68"
|
||||
Cohesion: 0.25
|
||||
@@ -474,8 +472,8 @@ Cohesion: 0.21
|
||||
Nodes (16): Any, int, str, BusinessException, InvalidStatusTransitionException, log_api_request(), log_delivery_operation(), log_operation() (+8 more)
|
||||
|
||||
### Community 70 - "Community 70"
|
||||
Cohesion: 0.11
|
||||
Nodes (32): Any, str, bool, BxUserContext, int, Session, str, Any (+24 more)
|
||||
Cohesion: 0.14
|
||||
Nodes (25): bool, BxUserContext, int, Session, str, Any, bool, int (+17 more)
|
||||
|
||||
### Community 71 - "Community 71"
|
||||
Cohesion: 0.16
|
||||
@@ -494,25 +492,25 @@ Cohesion: 0.06
|
||||
Nodes (88): Any, Decimal, Session, str, _advance_after_approval(), _approval_to_dict(), can_user_approve_settlement(), cancel_draft_settlement() (+80 more)
|
||||
|
||||
### Community 75 - "Community 75"
|
||||
Cohesion: 0.33
|
||||
Nodes (3): 地磅 API 路由聚合 — REST 风格,协议对齐原 simple-dibang-xiaoshou。, 地磅(db)Pydantic schemas — 对齐原 simple-dibang-xiaoshou 契约。, 地磅 API 路由聚合 — REST 风格,协议对齐原 simple-dibang-xiaoshou。
|
||||
Cohesion: 0.28
|
||||
Nodes (11): operlog_detail(), operlog_list(), 操作日志分页列表(基于 API 请求日志适配 RuoYi OperLog 结构), 操作日志分页列表(基于 API 请求日志适配 RuoYi OperLog 结构), _user_info(), Session, OperLogDetailBody, OperLogListBody (+3 more)
|
||||
|
||||
### Community 76 - "Community 76"
|
||||
Cohesion: 0.09
|
||||
Nodes (49): API 路由包 — 山峡化工生产一体化门户, create_leave(), delete_leave(), get_leave(), _leave_local_callback(), leave_workflow_callback(), list_leaves(), submit_leave() (+41 more)
|
||||
Cohesion: 0.27
|
||||
Nodes (17): create_leave(), delete_leave(), get_leave(), _leave_local_callback(), leave_workflow_callback(), list_leaves(), submit_leave(), update_leave() (+9 more)
|
||||
|
||||
### Community 77 - "Community 77"
|
||||
Cohesion: 0.33
|
||||
Nodes (3): _coerce_id(), OffsetItem, ReimburseDetailCreate
|
||||
Cohesion: 0.19
|
||||
Nodes (20): int, str, Any, bool, int, Session, str, business_type_from_path() (+12 more)
|
||||
|
||||
### Community 78 - "Community 78"
|
||||
Cohesion: 0.13
|
||||
Nodes (37): CrmContract, CrmContractProductPriceTier, date, datetime, Decimal, int, Query, contract_row_to_dict() (+29 more)
|
||||
|
||||
### Community 79 - "Community 79"
|
||||
Cohesion: 0.28
|
||||
Nodes (18): Any, int, Session, str, _build_tree(), _cascade_soft_delete(), _collect_with_ancestors(), create_dept() (+10 more)
|
||||
|
||||
### Community 79 - "Community 79"
|
||||
Cohesion: 0.34
|
||||
Nodes (16): Any, bool, int, Session, str, create_dict_type(), dict_type_code_exists(), get_dict_type() (+8 more)
|
||||
|
||||
### Community 80 - "Community 80"
|
||||
Cohesion: 0.31
|
||||
Nodes (20): Any, bool, CrmOpportunity, datetime, int, Session, str, create_opportunity_follow() (+12 more)
|
||||
@@ -534,16 +532,16 @@ Cohesion: 0.34
|
||||
Nodes (19): check_python(), create_venv(), ensure_dirs(), ensure_valid_venv(), ensure_venv_and_runtime(), install_dependencies(), is_running(), log_error() (+11 more)
|
||||
|
||||
### Community 85 - "Community 85"
|
||||
Cohesion: 0.09
|
||||
Nodes (27): Any, datetime, Decimal, _decimal_value(), _parse_optional_datetime(), _period_key(), Java: statisticsOverview, Java: statisticsOverview (+19 more)
|
||||
Cohesion: 0.29
|
||||
Nodes (16): Any, int, Session, str, BizLeaveRequest, create_leave(), get_leave(), list_leaves() (+8 more)
|
||||
|
||||
### Community 86 - "Community 86"
|
||||
Cohesion: 0.35
|
||||
Nodes (17): Any, bool, int, Session, str, _build_tree(), _cascade_soft_delete(), create_menu() (+9 more)
|
||||
|
||||
### Community 87 - "Community 87"
|
||||
Cohesion: 0.10
|
||||
Nodes (21): cancel_plan(), 作废运输计划:取消跟进状态、作废关联地磅出入库单、取消运输单。, 作废运输计划:取消跟进状态、作废关联地磅出入库单、取消运输单。, 作废运输计划:取消跟进状态、作废关联地磅出入库单、取消运输单。, 作废运输计划:取消跟进状态、作废关联地磅出入库单、取消运输单。, 作废运输计划:取消跟进状态、作废关联地磅出入库单、取消运输单。, 作废运输计划:取消跟进状态、作废关联地磅出入库单、取消运输单。, 作废运输计划:取消跟进状态、作废关联地磅出入库单、取消运输单。 (+13 more)
|
||||
Cohesion: 0.09
|
||||
Nodes (22): cancel_plan(), 作废运输计划:取消跟进状态、作废关联地磅出入库单、取消运输单。, 作废运输计划:取消跟进状态、作废关联地磅出入库单、取消运输单。, 作废运输计划:取消跟进状态、作废关联地磅出入库单、取消运输单。, 作废运输计划:取消跟进状态、作废关联地磅出入库单、取消运输单。, 作废运输计划:取消跟进状态、作废关联地磅出入库单、取消运输单。, 作废运输计划:取消跟进状态、作废关联地磅出入库单、取消运输单。, 作废运输计划:取消跟进状态、作废关联地磅出入库单、取消运输单。 (+14 more)
|
||||
|
||||
### Community 88 - "Community 88"
|
||||
Cohesion: 0.37
|
||||
@@ -554,40 +552,40 @@ Cohesion: 0.22
|
||||
Nodes (17): Any, bool, Session, str, list_share_logs(), _parse_user_agent(), CRM 报价单分享访问日志 CRUD — 对照 CrmQuoteShareLogServiceImpl.java, Java: statisticsVisits (+9 more)
|
||||
|
||||
### Community 90 - "Community 90"
|
||||
Cohesion: 0.10
|
||||
Nodes (22): date, Query, _build_quote_filters(), generate_quote_no(), _parse_optional_date(), Java: toContract — 返回 contractId, Java: toContract — 返回 contractId, Java: toContract — 返回 contractId (+14 more)
|
||||
Cohesion: 0.38
|
||||
Nodes (15): Any, bool, int, Session, str, create_site(), get_site(), list_sites() (+7 more)
|
||||
|
||||
### Community 91 - "Community 91"
|
||||
Cohesion: 0.29
|
||||
Nodes (16): Any, int, Session, str, BizLeaveRequest, create_leave(), get_leave(), list_leaves() (+8 more)
|
||||
Cohesion: 0.10
|
||||
Nodes (21): _generate_plan_no(), 生成计划编号 SXDDHYYYYMMDD######, 生成计划编号 SXDDHYYYYMMDD######, 生成计划编号 SXDDHYYYYMMDD######, 生成计划编号 SXDDHYYYYMMDD######, 生成计划编号 SXDDHYYYYMMDD######, 生成计划编号 SXDDHYYYYMMDD######, 生成计划编号 SXDDHYYYYMMDD###### (+13 more)
|
||||
|
||||
### Community 92 - "Community 92"
|
||||
Cohesion: 0.45
|
||||
Nodes (20): DbUserContext, int, Session, str, PlTransportAddress, 运输通用地址库 pl_transport_address, _address_to_read(), _build_address_query() (+12 more)
|
||||
Cohesion: 0.10
|
||||
Nodes (21): create_new_version(), Java: createNewVersion — 返回新报价单 id, Java: toContract — 返回 contractId, Java: createNewVersion — 返回新报价单 id, Java: createNewVersion — 返回新报价单 id, Java: createNewVersion — 返回新报价单 id, Java: createNewVersion — 返回新报价单 id, Java: createNewVersion — 返回新报价单 id (+13 more)
|
||||
|
||||
### Community 93 - "Community 93"
|
||||
Cohesion: 0.34
|
||||
Nodes (16): Any, bool, int, Session, str, create_tenant(), get_tenant(), list_tenants() (+8 more)
|
||||
|
||||
### Community 94 - "Community 94"
|
||||
Cohesion: 0.39
|
||||
Nodes (8): bool, BxUserContext, Session, str, 按业务类型获取工作流;已知类型不存在时自动创建空定义(无节点)。, 按业务类型获取工作流;已知类型不存在时自动创建空定义(无节点)。, WorkflowService, BxWorkflowDef
|
||||
Cohesion: 0.41
|
||||
Nodes (13): BxUserContext, Session, str, UploadFile, bind_biz(), delete_attachment(), download_file(), list_by_biz() (+5 more)
|
||||
|
||||
### Community 95 - "Community 95"
|
||||
Cohesion: 0.23
|
||||
Nodes (14): int, Session, str, _allocate_inbound_outbound_id(), _ensure_sequence_row(), max_numeric_inbound_outbound_id(), next_inbound_outbound_id(), 出入库单 id 生成 — 租户内纯数字自增,并发安全。 (+6 more)
|
||||
|
||||
### Community 96 - "Community 96"
|
||||
Cohesion: 0.17
|
||||
Nodes (20): Any, int, str, get_oper_log_meta(), OperLogMeta, 请求级操作日志元数据(ContextVar), 供异步落库线程使用,避免 ContextVar 跨线程丢失。, set_oper_log_meta() (+12 more)
|
||||
Cohesion: 0.20
|
||||
Nodes (10): Decimal, VehicleUreaApplyCreate, VehicleUreaApplyListResponse, VehicleUreaApplyRead, VehicleUreaApplyUpdate, VehicleUreaDisburseBoardListResponse, VehicleUreaDisburseCreate, VehicleUreaDisburseListResponse (+2 more)
|
||||
|
||||
### Community 97 - "Community 97"
|
||||
Cohesion: 0.38
|
||||
Nodes (15): Any, bool, int, Session, str, create_post(), get_post(), list_posts() (+7 more)
|
||||
|
||||
### Community 98 - "Community 98"
|
||||
Cohesion: 0.38
|
||||
Nodes (15): Any, bool, int, Session, str, create_site(), get_site(), list_sites() (+7 more)
|
||||
Cohesion: 0.41
|
||||
Nodes (11): Any, str, build_quote_html(), _default_value(), _format_date(), _format_discount(), _format_money(), generate_quote_pdf() (+3 more)
|
||||
|
||||
### Community 99 - "Community 99"
|
||||
Cohesion: 0.33
|
||||
@@ -599,23 +597,23 @@ Nodes (14): is_valid_dict_type(), load_portal_baseline(), load_sx(), load_zkxw()
|
||||
|
||||
### Community 101 - "Community 101"
|
||||
Cohesion: 0.22
|
||||
Nodes (17): Session, str, str, 附件上传 bizType 与 MinIO 目录映射(对齐原 CRM oss 上传语义), 优先使用显式 folder;否则按 bizType 映射默认目录。, 优先使用显式 folder;否则按 bizType 映射默认目录。, 优先使用显式 folder;否则按 bizType 映射默认目录。, resolve_upload_folder() (+9 more)
|
||||
Nodes (18): Any, bool, bytes, int, Request, str, _claims_from_auth(), _format_body_bytes() (+10 more)
|
||||
|
||||
### Community 102 - "Community 102"
|
||||
Cohesion: 0.35
|
||||
Nodes (5): Any, str, WfProcessInstance, WorkflowEngine, WfProcessInstance
|
||||
|
||||
### Community 103 - "Community 103"
|
||||
Cohesion: 0.12
|
||||
Nodes (16): int, _fill_contract_linked_count(), list_quotes(), Java: fillContractLinkedCount, Java: fillContractLinkedCount, Java: fillContractLinkedCount, Java: fillContractLinkedCount, Java: fillContractLinkedCount (+8 more)
|
||||
|
||||
### Community 103 - "Community 103"
|
||||
Cohesion: 0.41
|
||||
Nodes (11): Any, str, build_quote_html(), _default_value(), _format_date(), _format_discount(), _format_money(), generate_quote_pdf() (+3 more)
|
||||
|
||||
### Community 104 - "Community 104"
|
||||
Cohesion: 0.39
|
||||
Nodes (8): int, LeaveCreate, LeaveUpdate, Config, LeaveCreate, LeaveResponse, LeaveUpdate, Pydantic schemas - 请假申请
|
||||
Cohesion: 0.14
|
||||
Nodes (15): date, Query, _build_quote_filters(), _parse_optional_date(), Java: toContract — 返回 contractId, Java: toContract — 返回 contractId, Java: buildQueryWrapper, Java: buildQueryWrapper (+7 more)
|
||||
|
||||
### Community 105 - "Community 105"
|
||||
Cohesion: 0.16
|
||||
Nodes (25): Any, bool, bytes, int, Request, str, bool, int (+17 more)
|
||||
Cohesion: 0.17
|
||||
Nodes (20): Any, int, str, get_oper_log_meta(), OperLogMeta, 请求级操作日志元数据(ContextVar), 供异步落库线程使用,避免 ContextVar 跨线程丢失。, set_oper_log_meta() (+12 more)
|
||||
|
||||
### Community 106 - "Community 106"
|
||||
Cohesion: 0.36
|
||||
@@ -626,49 +624,53 @@ Cohesion: 0.60
|
||||
Nodes (10): Any, Session, str, _apply_create_range(), _base_query(), get_clue_level_statistics(), get_clue_overview_statistics(), get_clue_owner_statistics() (+2 more)
|
||||
|
||||
### Community 108 - "Community 108"
|
||||
Cohesion: 0.35
|
||||
Nodes (5): Any, str, WfProcessInstance, WorkflowEngine, WfProcessInstance
|
||||
Cohesion: 0.22
|
||||
Nodes (12): Any, bool, str, WfProcessInstance, _LocalCallbackFn, _dispatch_local(), 工作流回调服务 —— 流程状态变更时通知业务系统, 注册本地回调函数。当 callback_url 以 path 开头时直接调用 fn。 (+4 more)
|
||||
|
||||
### Community 109 - "Community 109"
|
||||
Cohesion: 0.14
|
||||
Nodes (22): Session, str, bool, Session, str, get_user_menu_codes(), 菜单 code 权限守卫(V0.3:通过 sys_user_role → sys_role_menu → sys_menu.perms), 读取用户授权的菜单权限标识集合(与前端 meta.menuCode / perms 对齐)。 (+14 more)
|
||||
|
||||
### Community 110 - "Community 110"
|
||||
Cohesion: 0.05
|
||||
Nodes (38): create_contract(), create_from_opportunity(), generate_contract_name(), generate_contract_no(), _normalize_framework_contract_data(), _opportunity_products_to_contract_products(), Java: normalizeFrameworkContractData, Java: createFromOpportunity — 返回合同 id (+30 more)
|
||||
Cohesion: 0.18
|
||||
Nodes (11): generate_contract_name(), Java: generateContractName, Java: generateContractName, Java: generateContractName, Java: generateContractName, Java: generateContractName, Java: generateContractName, Java: generateContractName (+3 more)
|
||||
|
||||
### Community 111 - "Community 111"
|
||||
Cohesion: 0.17
|
||||
Nodes (42): ProcessDefCreateBody, ProcessDefUpdateBody, PublishVersionBody, 审批意见:前端传 commentText,兼容 comment。, 审批意见:前端传 commentText,兼容 comment。, StartInstanceBody, TaskActionBody, Any (+34 more)
|
||||
Cohesion: 0.19
|
||||
Nodes (38): ProcessDefCreateBody, ProcessDefUpdateBody, PublishVersionBody, StartInstanceBody, Any, bool, WfProcessInstance, bool (+30 more)
|
||||
|
||||
### Community 112 - "Community 112"
|
||||
Cohesion: 0.08
|
||||
Nodes (103): attachment_download_url(), attachment_preview_url(), AttachmentDownloadUrlBody, create_dept(), create_dict_data(), create_dict_type(), create_menu(), create_post() (+95 more)
|
||||
Cohesion: 0.26
|
||||
Nodes (27): AttachmentDownloadUrlBody, DeptCreateBody, DeptUpdateBody, DictDataCreateBody, DictDataUpdateBody, DictTypeCreateBody, DictTypeUpdateBody, MenuCreateBody (+19 more)
|
||||
|
||||
### Community 113 - "Community 113"
|
||||
Cohesion: 0.10
|
||||
Nodes (20): create_from_quote(), _quote_products_to_contract_products(), Java: createFromQuote — 返回合同 id, Java: renewContract — 基础续签, Java: createFromQuote — 返回合同 id, Java: renewContract — 基础续签, Java: createFromQuote — 返回合同 id, Java: createFromQuote — 返回合同 id (+12 more)
|
||||
|
||||
### Community 114 - "Community 114"
|
||||
Cohesion: 0.19
|
||||
Nodes (11): Decimal, VehicleMaintenanceApplyCreate, VehicleMaintenanceApplyListResponse, VehicleMaintenanceApplyRead, VehicleMaintenanceApplyReminder, VehicleMaintenanceApplyUpdate, VehicleMaintenanceProcessBoardListResponse, VehicleMaintenanceProcessCreate (+3 more)
|
||||
|
||||
### Community 115 - "Community 115"
|
||||
Cohesion: 0.29
|
||||
Nodes (9): AST, expr, field_has_alias(), iter_field_assignments(), main(), bool, int, str (+1 more)
|
||||
|
||||
### Community 116 - "Community 116"
|
||||
Cohesion: 0.13
|
||||
Nodes (15): calculate_quote_amount(), create_new_version(), Java: createNewVersion — 返回新报价单 id, Java: createNewVersion — 返回新报价单 id, Java: createNewVersion — 返回新报价单 id, Java: createNewVersion — 返回新报价单 id, Java: createNewVersion — 返回新报价单 id, Java: createNewVersion — 返回新报价单 id (+7 more)
|
||||
|
||||
### Community 117 - "Community 117"
|
||||
Cohesion: 0.39
|
||||
Nodes (6): _inject_user_info_after_tenant(), patch_contract_routes(), patch_quote_routes(), patch_route_file(), Path, str
|
||||
|
||||
### Community 118 - "Community 118"
|
||||
Cohesion: 0.34
|
||||
Nodes (16): Any, bool, int, Session, str, create_dict_type(), dict_type_code_exists(), get_dict_type() (+8 more)
|
||||
|
||||
### Community 119 - "Community 119"
|
||||
Cohesion: 0.15
|
||||
Nodes (13): ORM → camelCase(单点真相), ORM → camelCase(单点真相), ORM → camelCase(单点真相), ORM → camelCase(单点真相), Java: viewByShareCode, Java: viewByShareCode, Java: viewByShareCode, Java: viewByShareCode (+5 more)
|
||||
|
||||
### Community 117 - "Community 117"
|
||||
Cohesion: 0.44
|
||||
Nodes (8): bool, int, Session, str, ApiRequestLogCRUD, SysApiRequestLog, SysApiRequestLogHis, SysApiRequestLog
|
||||
|
||||
### Community 118 - "Community 118"
|
||||
Cohesion: 0.20
|
||||
Nodes (10): create_from_opportunity(), _opportunity_products_to_contract_products(), Java: createFromOpportunity — 返回合同 id, Java: createFromOpportunity — 返回合同 id, Java: createFromOpportunity — 返回合同 id, Java: createFromOpportunity — 返回合同 id, Java: createFromOpportunity — 返回合同 id, Java: createFromOpportunity — 返回合同 id (+2 more)
|
||||
|
||||
### Community 119 - "Community 119"
|
||||
Cohesion: 0.29
|
||||
Nodes (10): datetime, int, Session, str, BizNoKind, allocate_next_biz_no(), _format_biz_no(), _prefix_for() (+2 more)
|
||||
|
||||
### Community 120 - "Community 120"
|
||||
Cohesion: 0.27
|
||||
Nodes (11): Any, bool, str, decrypt_login_payload(), ensure_initialized(), extract_login_credentials(), get_public_pem(), 登录凭据 RSA 解密(与前端 JSEncrypt PKCS#1 v1.5 兼容)。 (+3 more)
|
||||
@@ -681,6 +683,10 @@ Nodes (9): batch_submit_contract_approval(), Java: batchSubmitApproval, Java: ba
|
||||
Cohesion: 0.32
|
||||
Nodes (7): str, eq_unicode(), like_unicode(), MySQL 字符串比较/模糊匹配时统一排序规则,避免 Illegal mix of collations。, 跨表字符串等值比较(JOIN / WHERE)时统一为 utf8mb4_unicode_ci。, 将列显式设为 utf8mb4_unicode_ci。, unicode_ci()
|
||||
|
||||
### Community 123 - "Community 123"
|
||||
Cohesion: 0.33
|
||||
Nodes (10): Any, int, Session, str, list_ethnics(), 民族码表 CRUD(全局 sys_ethnic), _row_to_dict(), 民族码表 sys_ethnic(国标 GB/T 3304,全局) (+2 more)
|
||||
|
||||
### Community 124 - "Community 124"
|
||||
Cohesion: 0.18
|
||||
Nodes (9): 报销(bx)API 路由聚合 — REST 风格,协议对齐 bx 原系统。, 报销(bx)Pydantic 模型 — 响应协议与 bx 原系统保持一致, bx_health(), 报销(bx)API 路由聚合 — REST 风格,协议对齐 bx 原系统。, P0 健康检查:验证 bx 路由已挂载且响应协议为 code=0。, P0 健康检查:验证 bx 路由已挂载且响应协议为 code=0。, P0 健康检查:验证 bx 路由已挂载且响应协议为 code=0。, P0 健康检查:验证 bx 路由已挂载且响应协议为 code=0。 (+1 more)
|
||||
@@ -690,40 +696,40 @@ Cohesion: 0.22
|
||||
Nodes (9): generate_template_no(), Java: generateTemplateNo — TMPL{yyyyMMdd}{4位序号}, Java: generateTemplateNo — TMPL{yyyyMMdd}{4位序号}, Java: generateTemplateNo — TMPL{yyyyMMdd}{4位序号}, Java: generateTemplateNo — TMPL{yyyyMMdd}{4位序号}, Java: generateTemplateNo — TMPL{yyyyMMdd}{4位序号}, Java: generateTemplateNo — TMPL{yyyyMMdd}{4位序号}, Java: generateTemplateNo — TMPL{yyyyMMdd}{4位序号} (+1 more)
|
||||
|
||||
### Community 126 - "Community 126"
|
||||
Cohesion: 0.17
|
||||
Nodes (10): FreightSettlementApprovalActionBody, FreightSettlementByTransportRead, FreightSettlementCreate, FreightSettlementDisburseBody, FreightSettlementListItem, FreightSettlementSubmitApprovalBody, SettlementApprovalFlowItem, SettlementPlanSummary (+2 more)
|
||||
Cohesion: 0.05
|
||||
Nodes (48): BaseModel, AdvanceApplyOut, AdvanceApprovalCreate, AttachmentOut, PagedQuery, PagedResponse, ReimburseApplyCreate, ReimburseApplyUpdate (+40 more)
|
||||
|
||||
### Community 127 - "Community 127"
|
||||
Cohesion: 0.16
|
||||
Nodes (37): DbPurchasePlan, DbUserContext, Session, str, InboundOutboundRead, _apply_plan_product_update(), _apply_plan_update(), _base_query() (+29 more)
|
||||
Cohesion: 0.32
|
||||
Nodes (7): Session, str, apply_product_snapshot_to_weighing(), ProductSnapshot, 地磅模块 — CRM 产品解析与快照写入。, 校验 CRM 产品并返回名称/规格快照。新建单据 product_id 必填。, resolve_crm_product()
|
||||
|
||||
### Community 128 - "Community 128"
|
||||
Cohesion: 0.24
|
||||
Nodes (8): Decimal, VehicleUreaApplyCreate, VehicleUreaApplyListResponse, VehicleUreaApplyUpdate, VehicleUreaDisburseBoardListResponse, VehicleUreaDisburseCreate, VehicleUreaDisburseListResponse, VehicleUreaDisburseUpdate
|
||||
Cohesion: 0.29
|
||||
Nodes (5): API 路由包 — 山峡化工生产一体化门户, get_menu_list(), 运营端菜单接口(与 web-admin 前端静态路由 sqxp.ts 对齐,14 个一级菜单), 运营端角色守卫:admin / super_admin 可访问。, require_admin_or_super_admin()
|
||||
|
||||
### Community 129 - "Community 129"
|
||||
Cohesion: 0.18
|
||||
Nodes (11): Java: createNewVersion — 返回新报价单 id, Java: CrmQuoteServiceImpl.withdrawApproval(校验创建人), Java: CrmQuoteServiceImpl.withdrawApproval(校验创建人), Java: CrmQuoteServiceImpl.withdrawApproval(校验创建人), Java: CrmQuoteServiceImpl.withdrawApproval(校验创建人), Java: CrmQuoteServiceImpl.withdrawApproval(校验创建人), Java: CrmQuoteServiceImpl.withdrawApproval(校验创建人), Java: CrmQuoteServiceImpl.withdrawApproval(校验创建人) (+3 more)
|
||||
Cohesion: 0.38
|
||||
Nodes (6): date, datetime, str, _date_to_str(), _dt_to_str(), bx 原系统 API 响应包装(code=0 / msg / data.rows),与门户 success_response 分离。
|
||||
|
||||
### Community 130 - "Community 130"
|
||||
Cohesion: 0.07
|
||||
Nodes (34): _avatar_url_field(), _build_user_info_payload(), get_current_user_profile(), get_web_auth_menu_codes(), _jwt_user_payload(), logout(), public_quote_share(), 登出:吊销 Redis 会话(若已配置 REDIS_HOST)。 (+26 more)
|
||||
Cohesion: 0.09
|
||||
Nodes (29): _avatar_url_field(), _build_user_info_payload(), get_current_user_profile(), _jwt_user_payload(), logout(), public_quote_share(), 登出:吊销 Redis 会话(若已配置 REDIS_HOST)。, 登出:吊销 Redis 会话(若已配置 REDIS_HOST)。 (+21 more)
|
||||
|
||||
### Community 131 - "Community 131"
|
||||
Cohesion: 0.40
|
||||
Nodes (3): BaseSettings, Config, Settings
|
||||
|
||||
### Community 132 - "Community 132"
|
||||
Cohesion: 0.18
|
||||
Nodes (11): Java: statisticsStatus, Java: statisticsStatus, Java: statisticsStatus, Java: statisticsStatus, Java: statisticsStatus, Java: statisticsStatus, Java: statisticsStatus, Java: statisticsStatus (+3 more)
|
||||
Cohesion: 0.33
|
||||
Nodes (5): _coerce_optional_id(), WorkflowDefCreate, WorkflowDefUpdate, WorkflowNodeCreate, WorkflowNodeOut
|
||||
|
||||
### Community 134 - "Community 134"
|
||||
Cohesion: 0.18
|
||||
Nodes (11): 解析司机信息:优先 pl_personnel,兼容旧 pl_driver。, 解析司机信息:优先 pl_personnel,兼容旧 pl_driver。, 解析司机信息:优先 pl_personnel,兼容旧 pl_driver。, 解析司机信息:优先 pl_personnel,兼容旧 pl_driver。, 解析司机信息:优先 pl_personnel,兼容旧 pl_driver。, 解析司机信息:优先 pl_personnel,兼容旧 pl_driver。, 解析司机信息:优先 pl_personnel,兼容旧 pl_driver。, 解析司机信息:优先 pl_personnel,兼容旧 pl_driver。 (+3 more)
|
||||
Cohesion: 0.33
|
||||
Nodes (6): attachment_download_url(), 返回带 Content-Disposition 的下载 URL,保证浏览器使用原始文件名。, 返回带 Content-Disposition 的下载 URL,保证浏览器使用原始文件名。, 返回带 Content-Disposition 的下载 URL,保证浏览器使用原始文件名。, 返回带 Content-Disposition 的下载 URL,保证浏览器使用原始文件名。, 返回带 Content-Disposition 的下载 URL,保证浏览器使用原始文件名。
|
||||
|
||||
### Community 135 - "Community 135"
|
||||
Cohesion: 0.20
|
||||
Nodes (10): Java: deleteWithValidByIds — 软删除, Java: deleteWithValidByIds — 软删除, Java: deleteWithValidByIds — 软删除, Java: deleteWithValidByIds — 软删除, Java: deleteWithValidByIds — 软删除, Java: deleteWithValidByIds — 软删除, Java: deleteWithValidByIds, Java: deleteWithValidByIds — 软删除 (+2 more)
|
||||
Cohesion: 0.33
|
||||
Nodes (3): 地磅 API 路由聚合 — REST 风格,协议对齐原 simple-dibang-xiaoshou。, 地磅(db)Pydantic schemas — 对齐原 simple-dibang-xiaoshou 契约。, 地磅 API 路由聚合 — REST 风格,协议对齐原 simple-dibang-xiaoshou。
|
||||
|
||||
### Community 136 - "Community 136"
|
||||
Cohesion: 0.18
|
||||
@@ -739,11 +745,15 @@ Nodes (4): str, _coerce_id(), ExpenseItemCreate, ExpenseItemUpdate
|
||||
|
||||
### Community 149 - "Community 149"
|
||||
Cohesion: 0.33
|
||||
Nodes (5): _coerce_optional_id(), WorkflowDefCreate, WorkflowDefUpdate, WorkflowNodeCreate, WorkflowNodeOut
|
||||
Nodes (3): _coerce_id(), OffsetItem, ReimburseDetailCreate
|
||||
|
||||
### Community 150 - "Community 150"
|
||||
Cohesion: 0.40
|
||||
Nodes (5): list_roles_for_designer(), 返回角色下拉选项(label=角色名,value=角色ID UUID),供流程设计器审批人选择使用。 与 /roles 不同,此端点返回角色 UUID, 返回角色下拉选项(label=角色名,value=角色ID UUID),供流程设计器审批人选择使用。 与 /roles 不同,此端点返回角色 UUID, 返回角色下拉选项(label=角色名,value=角色ID UUID),供流程设计器审批人选择使用。 与 /roles 不同,此端点返回角色 UUID, 返回角色下拉选项(label=角色名,value=角色ID UUID),供流程设计器审批人选择使用。 与 /roles 不同,此端点返回角色 UUID
|
||||
|
||||
### Community 151 - "Community 151"
|
||||
Cohesion: 0.03
|
||||
Nodes (60): 供方全量列表(下拉)— Java: CrmSupplierInfoController.listAll, 供方全量列表(下拉)— Java: CrmSupplierInfoController.listAll, 供方全量列表(下拉)— Java: CrmSupplierInfoController.listAll, 供方全量列表(下拉)— Java: CrmSupplierInfoController.listAll, 供方全量列表(下拉)— Java: CrmSupplierInfoController.listAll, 供方全量列表(下拉)— Java: CrmSupplierInfoController.listAll, 供方全量列表(下拉)— Java: CrmSupplierInfoController.listAll, 供方全量列表(下拉)— Java: CrmSupplierInfoController.listAll (+52 more)
|
||||
Cohesion: 0.10
|
||||
Nodes (21): invoice_info_list(), 按客户ID查询开票信息(与 by-customer 等价), 按客户ID查询开票信息(与 by-customer 等价), 按客户ID查询开票信息(与 by-customer 等价), 按客户ID查询开票信息(与 by-customer 等价), 按客户ID查询开票信息(与 by-customer 等价), 按客户ID查询开票信息(与 by-customer 等价), 按客户ID查询开票信息(与 by-customer 等价) (+13 more)
|
||||
|
||||
### Community 152 - "Community 152"
|
||||
Cohesion: 0.38
|
||||
@@ -754,40 +764,20 @@ Cohesion: 0.40
|
||||
Nodes (5): Web 端登录用 RSA 公钥(PKCS#1 v1.5,与 JSEncrypt 一致), Web 端登录用 RSA 公钥(PKCS#1 v1.5,与 JSEncrypt 一致), Web 端登录用 RSA 公钥(PKCS#1 v1.5,与 JSEncrypt 一致), Web 端登录用 RSA 公钥(PKCS#1 v1.5,与 JSEncrypt 一致), web_login_rsa_public_key()
|
||||
|
||||
### Community 154 - "Community 154"
|
||||
Cohesion: 0.29
|
||||
Nodes (10): datetime, int, Session, str, BizNoKind, allocate_next_biz_no(), _format_biz_no(), _prefix_for() (+2 more)
|
||||
|
||||
### Community 155 - "Community 155"
|
||||
Cohesion: 0.40
|
||||
Nodes (4): _auto_fill_create_dept_id(), ORM before_insert 钩子:当 create_dept_id 为空时,自动从请求上下文填充当前用户部门 ID, ORM before_insert 钩子:当 create_dept_id 为空时,自动从请求上下文填充当前用户部门 ID, ORM before_insert 钩子:当 create_dept_id 为空时,自动从请求上下文填充当前用户部门 ID
|
||||
Nodes (5): get_web_auth_menu_codes(), 获取当前用户菜单权限标识(sys_menu.perms,经角色继承)。, 获取当前用户菜单权限标识(sys_menu.perms,经角色继承)。, 获取当前用户菜单权限标识(sys_menu.perms,经角色继承)。, 获取当前用户菜单权限标识(sys_menu.perms,经角色继承)。
|
||||
|
||||
### Community 156 - "Community 156"
|
||||
Cohesion: 0.40
|
||||
Nodes (4): ContactByCustomerBody, ContactDetailBody, ContactSetPrimaryBody, CRM 联系人 Pydantic Schemas
|
||||
Nodes (3): AdvanceApplyCreate, AdvanceApplyUpdate, _coerce_optional_id()
|
||||
|
||||
### Community 157 - "Community 157"
|
||||
Cohesion: 0.40
|
||||
Nodes (4): CreateLinkForPlanPayload, InboundOutboundBase, InboundOutboundCreate, InboundOutboundUpdate
|
||||
|
||||
### Community 158 - "Community 158"
|
||||
Cohesion: 0.50
|
||||
Nodes (3): Config, PageResult, ResponseModel
|
||||
|
||||
### Community 159 - "Community 159"
|
||||
Cohesion: 0.50
|
||||
Nodes (3): PurchasePlanBase, PurchasePlanCreate, PurchasePlanUpdate
|
||||
|
||||
### Community 160 - "Community 160"
|
||||
Cohesion: 0.50
|
||||
Nodes (3): WeighingSlipBase, WeighingSlipCreate, WeighingSlipUpdate
|
||||
Nodes (4): list_roles_for_select(), 返回运营端角色下拉选项(label=库中角色名,value=Web 角色码)。, 返回运营端角色下拉选项(label=库中角色名,value=Web 角色码)。, 返回运营端角色下拉选项(label=库中角色名,value=Web 角色码)。
|
||||
|
||||
### Community 162 - "Community 162"
|
||||
Cohesion: 0.50
|
||||
Nodes (3): Any, str, 与门户 JWT / apply_data_scope 所需的 user_info 字段对齐。
|
||||
|
||||
### Community 169 - "Community 169"
|
||||
Cohesion: 0.18
|
||||
Nodes (12): Session, Session, get_current_user(), get_current_user_by_token(), 根据 token 获取当前用户(包含 Redis 会话校验;会话滑动续期), 根据 token 获取当前用户(包含 Redis 会话校验;会话滑动续期), 根据 token 获取当前用户(包含 Redis 会话校验;会话滑动续期), 获取当前用户(sync 依赖,配合 sync 路由在线程池执行,避免阻塞事件循环)。 (+4 more)
|
||||
Cohesion: 0.12
|
||||
Nodes (17): Session, Session, get_current_user(), get_current_user_by_token(), 根据 token 获取当前用户(包含 Redis 会话校验;会话滑动续期), 根据 token 获取当前用户(包含 Redis 会话校验;会话滑动续期), 根据 token 获取当前用户(包含 Redis 会话校验;会话滑动续期), 获取当前用户(sync 依赖,配合 sync 路由在线程池执行,避免阻塞事件循环)。 (+9 more)
|
||||
|
||||
## Knowledge Gaps
|
||||
- **77 isolated node(s):** `Session`, `Request`, `bool`, `bool`, `Config` (+72 more)
|
||||
@@ -797,12 +787,12 @@ Nodes (12): Session, Session, get_current_user(), get_current_user_by_token(),
|
||||
## Suggested Questions
|
||||
_Questions this graph is uniquely positioned to answer:_
|
||||
|
||||
- **Why does `Session` connect `Community 112` to `Community 0`, `Community 1`, `Community 2`, `Community 4`, `Community 5`, `Community 6`, `Community 7`, `Community 8`, `Community 9`, `Community 10`, `Community 11`, `Community 12`, `Community 13`, `Community 14`, `Community 15`, `Community 16`, `Community 17`, `Community 18`, `Community 19`, `Community 21`, `Community 22`, `Community 23`, `Community 24`, `Community 25`, `Community 26`, `Community 27`, `Community 28`, `Community 29`, `Community 30`, `Community 31`, `Community 32`, `Community 33`, `Community 34`, `Community 35`, `Community 37`, `Community 38`, `Community 39`, `Community 42`, `Community 43`, `Community 44`, `Community 46`, `Community 47`, `Community 48`, `Community 51`, `Community 52`, `Community 53`, `Community 54`, `Community 58`, `Community 59`, `Community 60`, `Community 61`, `Community 62`, `Community 66`, `Community 68`, `Community 70`, `Community 71`, `Community 72`, `Community 73`, `Community 74`, `Community 76`, `Community 78`, `Community 79`, `Community 80`, `Community 81`, `Community 85`, `Community 86`, `Community 87`, `Community 89`, `Community 90`, `Community 91`, `Community 92`, `Community 93`, `Community 94`, `Community 95`, `Community 97`, `Community 98`, `Community 99`, `Community 101`, `Community 102`, `Community 104`, `Community 105`, `Community 107`, `Community 108`, `Community 109`, `Community 110`, `Community 111`, `Community 113`, `Community 116`, `Community 118`, `Community 119`, `Community 121`, `Community 125`, `Community 127`, `Community 129`, `Community 130`, `Community 132`, `Community 134`, `Community 135`, `Community 151`, `Community 154`, `Community 169`?**
|
||||
- **Why does `Session` connect `Community 12` to `Community 0`, `Community 1`, `Community 2`, `Community 4`, `Community 5`, `Community 7`, `Community 8`, `Community 9`, `Community 10`, `Community 11`, `Community 13`, `Community 15`, `Community 16`, `Community 17`, `Community 18`, `Community 19`, `Community 21`, `Community 22`, `Community 23`, `Community 24`, `Community 25`, `Community 26`, `Community 27`, `Community 28`, `Community 29`, `Community 30`, `Community 31`, `Community 32`, `Community 33`, `Community 35`, `Community 37`, `Community 38`, `Community 39`, `Community 42`, `Community 43`, `Community 44`, `Community 46`, `Community 47`, `Community 48`, `Community 49`, `Community 50`, `Community 51`, `Community 52`, `Community 53`, `Community 54`, `Community 56`, `Community 57`, `Community 58`, `Community 60`, `Community 61`, `Community 62`, `Community 64`, `Community 65`, `Community 66`, `Community 68`, `Community 70`, `Community 71`, `Community 72`, `Community 73`, `Community 74`, `Community 75`, `Community 76`, `Community 77`, `Community 78`, `Community 79`, `Community 80`, `Community 81`, `Community 85`, `Community 86`, `Community 87`, `Community 89`, `Community 90`, `Community 91`, `Community 92`, `Community 93`, `Community 94`, `Community 95`, `Community 97`, `Community 99`, `Community 102`, `Community 103`, `Community 104`, `Community 107`, `Community 109`, `Community 111`, `Community 112`, `Community 113`, `Community 116`, `Community 117`, `Community 118`, `Community 119`, `Community 121`, `Community 123`, `Community 125`, `Community 126`, `Community 127`, `Community 130`, `Community 134`, `Community 150`, `Community 151`, `Community 154`, `Community 157`, `Community 162`?**
|
||||
_High betweenness centrality (0.257) - this node is a cross-community bridge._
|
||||
- **Why does `BaseModel` connect `Community 3` to `Community 0`, `Community 1`, `Community 128`, `Community 4`, `Community 6`, `Community 7`, `Community 9`, `Community 10`, `Community 11`, `Community 14`, `Community 15`, `Community 18`, `Community 148`, `Community 149`, `Community 22`, `Community 23`, `Community 24`, `Community 152`, `Community 20`, `Community 156`, `Community 29`, `Community 158`, `Community 30`, `Community 157`, `Community 159`, `Community 160`, `Community 35`, `Community 32`, `Community 38`, `Community 39`, `Community 40`, `Community 42`, `Community 48`, `Community 61`, `Community 65`, `Community 76`, `Community 77`, `Community 104`, `Community 111`, `Community 112`, `Community 114`, `Community 126`, `Community 127`?**
|
||||
_High betweenness centrality (0.058) - this node is a cross-community bridge._
|
||||
- **Why does `int` connect `Community 55` to `Community 0`, `Community 1`, `Community 2`, `Community 4`, `Community 5`, `Community 7`, `Community 8`, `Community 9`, `Community 10`, `Community 11`, `Community 12`, `Community 14`, `Community 15`, `Community 17`, `Community 18`, `Community 21`, `Community 22`, `Community 23`, `Community 24`, `Community 25`, `Community 26`, `Community 29`, `Community 30`, `Community 32`, `Community 33`, `Community 35`, `Community 37`, `Community 38`, `Community 41`, `Community 42`, `Community 43`, `Community 44`, `Community 45`, `Community 46`, `Community 47`, `Community 48`, `Community 50`, `Community 51`, `Community 52`, `Community 53`, `Community 54`, `Community 58`, `Community 59`, `Community 66`, `Community 67`, `Community 68`, `Community 69`, `Community 70`, `Community 71`, `Community 72`, `Community 73`, `Community 74`, `Community 76`, `Community 78`, `Community 79`, `Community 80`, `Community 83`, `Community 86`, `Community 91`, `Community 92`, `Community 93`, `Community 94`, `Community 95`, `Community 96`, `Community 97`, `Community 98`, `Community 99`, `Community 101`, `Community 102`, `Community 104`, `Community 105`, `Community 108`, `Community 111`, `Community 112`, `Community 118`, `Community 121`, `Community 135`, `Community 154`?**
|
||||
_High betweenness centrality (0.040) - this node is a cross-community bridge._
|
||||
- **Why does `BaseModel` connect `Community 3` to `Community 0`, `Community 1`, `Community 132`, `Community 4`, `Community 6`, `Community 7`, `Community 9`, `Community 11`, `Community 15`, `Community 17`, `Community 18`, `Community 148`, `Community 149`, `Community 22`, `Community 23`, `Community 24`, `Community 152`, `Community 20`, `Community 155`, `Community 156`, `Community 30`, `Community 32`, `Community 35`, `Community 38`, `Community 39`, `Community 40`, `Community 48`, `Community 57`, `Community 58`, `Community 61`, `Community 64`, `Community 65`, `Community 66`, `Community 75`, `Community 76`, `Community 96`, `Community 111`, `Community 112`, `Community 114`, `Community 126`?**
|
||||
_High betweenness centrality (0.055) - this node is a cross-community bridge._
|
||||
- **Why does `int` connect `Community 38` to `Community 0`, `Community 1`, `Community 2`, `Community 4`, `Community 5`, `Community 7`, `Community 8`, `Community 9`, `Community 10`, `Community 11`, `Community 12`, `Community 14`, `Community 15`, `Community 16`, `Community 17`, `Community 18`, `Community 21`, `Community 22`, `Community 23`, `Community 24`, `Community 25`, `Community 26`, `Community 29`, `Community 30`, `Community 32`, `Community 33`, `Community 37`, `Community 41`, `Community 43`, `Community 44`, `Community 45`, `Community 47`, `Community 48`, `Community 49`, `Community 50`, `Community 51`, `Community 53`, `Community 54`, `Community 55`, `Community 56`, `Community 57`, `Community 62`, `Community 64`, `Community 65`, `Community 66`, `Community 67`, `Community 68`, `Community 69`, `Community 70`, `Community 71`, `Community 72`, `Community 73`, `Community 74`, `Community 76`, `Community 77`, `Community 78`, `Community 79`, `Community 80`, `Community 83`, `Community 85`, `Community 86`, `Community 90`, `Community 93`, `Community 95`, `Community 97`, `Community 99`, `Community 101`, `Community 102`, `Community 103`, `Community 105`, `Community 111`, `Community 112`, `Community 117`, `Community 119`, `Community 121`, `Community 123`?**
|
||||
_High betweenness centrality (0.044) - this node is a cross-community bridge._
|
||||
- **Are the 337 inferred relationships involving `str` (e.g. with `AdvanceApplyCreate` and `AdvanceApplyOut`) actually correct?**
|
||||
_`str` has 337 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Are the 333 inferred relationships involving `Session` (e.g. with `AdvanceApplyCreate` and `AdvanceApplyOut`) actually correct?**
|
||||
@@ -810,4 +800,4 @@ _Questions this graph is uniquely positioned to answer:_
|
||||
- **Are the 222 inferred relationships involving `int` (e.g. with `AdvanceApplyCreate` and `AdvanceApplyOut`) actually correct?**
|
||||
_`int` has 222 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **What connects `定期 ping MySQL,避免远程库/NAT 回收空闲 TCP 后首请求长时间卡住。`, `数据库异常:返回 JSON 并保留 CORS 头,避免浏览器只报跨域错误`, `未捕获异常:统一 JSON 500,确保跨域场景下仍有 Access-Control-Allow-Origin` to the rest of the system?**
|
||||
_1493 weakly-connected nodes found - possible documentation gaps or missing edges._
|
||||
_1525 weakly-connected nodes found - possible documentation gaps or missing edges._
|
||||
backend/graphify-out/cache/ast/02201f8488ad81c2add94b41c44b09828da457b96ed9bf4307c2b257e5f1f833.json
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_api_pl_base_data_py", "label": "base_data.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L1"}, {"id": "pl_base_data_list_carriers", "label": "list_carriers()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L74"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L74"}, {"id": "int", "label": "int", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L74"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L74"}, {"id": "dbusercontext", "label": "DbUserContext", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L74"}, {"id": "carrierlistresponse", "label": "CarrierListResponse", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L74"}, {"id": "pl_base_data_create_carrier", "label": "create_carrier()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L119"}, {"id": "carriercreate", "label": "CarrierCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L119"}, {"id": "carrierread", "label": "CarrierRead", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L119"}, {"id": "pl_base_data_get_carrier", "label": "get_carrier()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L149"}, {"id": "pl_base_data_update_carrier", "label": "update_carrier()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L160"}, {"id": "carrierupdate", "label": "CarrierUpdate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L160"}, {"id": "pl_base_data_delete_carrier", "label": "delete_carrier()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L194"}, {"id": "pl_base_data_approve_carrier", "label": "approve_carrier()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L208"}, {"id": "pl_base_data_reject_carrier", "label": "reject_carrier()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L232"}, {"id": "carrierauditreject", "label": "CarrierAuditReject", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L232"}, {"id": "plcarrier", "label": "PlCarrier", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L254"}, {"id": "pl_base_data_vehicle_to_read", "label": "_vehicle_to_read()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L275"}, {"id": "plvehicle", "label": "PlVehicle", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L275"}, {"id": "vehicleread", "label": "VehicleRead", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L275"}, {"id": "pl_base_data_vehicle_to_detail", "label": "_vehicle_to_detail()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L288"}, {"id": "vehicledetailread", "label": "VehicleDetailRead", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L288"}, {"id": "pl_base_data_list_vehicles", "label": "list_vehicles()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L306"}, {"id": "bool", "label": "bool", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L306"}, {"id": "vehiclelistresponse", "label": "VehicleListResponse", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L306"}, {"id": "pl_base_data_create_vehicle", "laLine truncated
|
||||
backend/graphify-out/cache/ast/123d08b4548931e30b486ed9a24dc299ed075435b9bb3919e79200c7c2055333.json
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_services_pl_cert_required_service_py", "label": "pl_cert_required_service.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L1"}, {"id": "services_pl_cert_required_service_match_required_cert_name", "label": "match_required_cert_name()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L25"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L25"}, {"id": "bool", "label": "bool", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L25"}, {"id": "services_pl_cert_required_service_cert_row_complete", "label": "_cert_row_complete()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L38"}, {"id": "certrow", "label": "CertRow", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L38"}, {"id": "services_pl_cert_required_service_find_cert_for_required", "label": "_find_cert_for_required()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L61"}, {"id": "services_pl_cert_required_service_missing_field_labels", "label": "_missing_field_labels()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L81"}, {"id": "services_pl_cert_required_service_collect_required_cert_errors", "label": "collect_required_cert_errors()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L107"}, {"id": "services_pl_cert_required_service_get_required_personnel_cert_names", "label": "get_required_personnel_cert_names()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L132"}, {"id": "services_pl_cert_required_service_validate_personnel_type_role_compat", "label": "validate_personnel_type_role_compat()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L141"}, {"id": "services_pl_cert_required_service_validate_personnel_required_certs", "label": "validate_personnel_required_certs()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L152"}, {"id": "services_pl_cert_required_service_validate_self_vehicle_required_certs", "label": "validate_self_vehicle_required_certs()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L171"}, {"id": "services_pl_cert_required_service_validate_self_vehicle_personnel_links", "label": "validate_self_vehicle_personnel_links()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L195"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L195"}, {"id": "vehiclepersonnelrow", "label": "VehiclePersonnelRow", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L195"}, {"id": "services_pl_cert_required_service_validate_vehicle_save_certs", "label": "validate_vehicle_save_certs()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L234"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_pl_cert_required_service_py", "target": "typing", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L4", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_pl_cert_required_service_py", "target": "fastapi", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L6", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_pl_cert_required_service_py", "target": "sqlalchemy_orm", "Line truncated
|
||||
backend/graphify-out/cache/ast/34f3d1d723890e294aac765a66d7e86ca87f4288f42cec674bb9151de091fe22.json
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_services_pl_personnel_service_py", "label": "pl_personnel_service.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L1"}, {"id": "services_pl_personnel_service_upsert_personnel", "label": "upsert_personnel()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L14"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L14"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L14"}, {"id": "vehiclepersonnelrow", "label": "VehiclePersonnelRow", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L14"}, {"id": "services_pl_personnel_service_save_vehicle_personnel", "label": "save_vehicle_personnel()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L90"}, {"id": "services_pl_personnel_service_build_personnel_summary", "label": "build_personnel_summary()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L140"}, {"id": "int", "label": "int", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L140"}, {"id": "services_pl_personnel_service_load_vehicle_personnel_rows", "label": "load_vehicle_personnel_rows()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L157"}, {"id": "services_pl_personnel_service_load_vehicle_personnel_summaries", "label": "load_vehicle_personnel_summaries()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L190"}, {"id": "services_pl_personnel_service_rationale_1", "label": "\u8fd0\u8f93\u8ba1\u5212 - \u4eba\u5458 upsert \u4e0e\u8f66\u8f86-\u4eba\u5458\u5173\u8054\u4fdd\u5b58", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L1"}, {"id": "services_pl_personnel_service_rationale_23", "label": "\u6309 personnelId \u2192 phone \u2192 name \u5f31\u5339\u914d upsert\uff0c\u8fd4\u56de personnel_id\u3002", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L23"}, {"id": "services_pl_personnel_service_rationale_143", "label": "links: [(personnel_name, role, is_default)] \u2192 (summary, default_driver_name).", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L143"}, {"id": "services_pl_personnel_service_rationale_193", "label": "\u6279\u91cf\u52a0\u8f7d personnelSummary / defaultDriverName\u3002", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L193"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_pl_personnel_service_py", "target": "typing", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L4", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_pl_personnel_service_py", "target": "fastapi", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L6", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_pl_personnel_service_py", "target": "sqlalchemy_orm", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L7", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_pl_personnel_service_py", "target": "app_core_utils", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L9", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_pl_personnel_service_py", "target": "app_models_pl_models", "relation": "imports_from"Line truncated
|
||||
backend/graphify-out/cache/ast/3be4253f6a131c632536d32b1f087a8838675bbb4b282e96cec1e2fb5063e9fd.json
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_services_pl_personnel_service_py", "label": "pl_personnel_service.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L1"}, {"id": "services_pl_personnel_service_upsert_personnel", "label": "upsert_personnel()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L14"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L14"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L14"}, {"id": "vehiclepersonnelrow", "label": "VehiclePersonnelRow", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L14"}, {"id": "services_pl_personnel_service_save_vehicle_personnel", "label": "save_vehicle_personnel()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L84"}, {"id": "services_pl_personnel_service_build_personnel_summary", "label": "build_personnel_summary()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L128"}, {"id": "int", "label": "int", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L128"}, {"id": "services_pl_personnel_service_load_vehicle_personnel_rows", "label": "load_vehicle_personnel_rows()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L145"}, {"id": "services_pl_personnel_service_load_vehicle_personnel_summaries", "label": "load_vehicle_personnel_summaries()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L178"}, {"id": "services_pl_personnel_service_rationale_1", "label": "\u8fd0\u8f93\u8ba1\u5212 - \u4eba\u5458 upsert \u4e0e\u8f66\u8f86-\u4eba\u5458\u5173\u8054\u4fdd\u5b58", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L1"}, {"id": "services_pl_personnel_service_rationale_22", "label": "\u6309 personnelId \u2192 phone \u2192 name \u5f31\u5339\u914d upsert\uff0c\u8fd4\u56de personnel_id\u3002", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L22"}, {"id": "services_pl_personnel_service_rationale_131", "label": "links: [(personnel_name, role, is_default)] \u2192 (summary, default_driver_name).", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L131"}, {"id": "services_pl_personnel_service_rationale_181", "label": "\u6279\u91cf\u52a0\u8f7d personnelSummary / defaultDriverName\u3002", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L181"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_pl_personnel_service_py", "target": "typing", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L4", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_pl_personnel_service_py", "target": "fastapi", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L6", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_pl_personnel_service_py", "target": "sqlalchemy_orm", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L7", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_pl_personnel_service_py", "target": "app_core_utils", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py", "source_location": "L9", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_pl_personnel_service_py", "target": "app_models_pl_models", "relation": "imports_from"Line truncated
|
||||
backend/graphify-out/cache/ast/45e8c537286f7ff6da75a37d0add654f0f6ae5e92165d5e42a764ecafba3c830.json
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_models_pl_models_py", "label": "pl_models.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L1"}, {"id": "models_pl_models_plcarrier", "label": "PlCarrier", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L28"}, {"id": "base", "label": "Base", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "models_pl_models_plvehicle", "label": "PlVehicle", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L57"}, {"id": "models_pl_models_plpersonnel", "label": "PlPersonnel", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L83"}, {"id": "models_pl_models_pltransportaddress", "label": "PlTransportAddress", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L111"}, {"id": "models_pl_models_plcert", "label": "PlCert", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L135"}, {"id": "models_pl_models_pldriver", "label": "PlDriver", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L161"}, {"id": "models_pl_models_plvehiclepersonnel", "label": "PlVehiclePersonnel", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L187"}, {"id": "models_pl_models_plvehicledriver", "label": "PlVehicleDriver", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L208"}, {"id": "models_pl_models_plplan", "label": "PlPlan", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L233"}, {"id": "models_pl_models_plplanproduct", "label": "PlPlanProduct", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L332"}, {"id": "models_pl_models_plplanchange", "label": "PlPlanChange", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L388"}, {"id": "models_pl_models_pltransport", "label": "PlTransport", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L415"}, {"id": "models_pl_models_pltransportdblink", "label": "PlTransportDbLink", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L477"}, {"id": "models_pl_models_plplantransport", "label": "PlPlanTransport", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L500"}, {"id": "models_pl_models_pltransportsitelink", "label": "PlTransportSiteLink", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L519"}, {"id": "models_pl_models_pltransportapproval", "label": "PlTransportApproval", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L552"}, {"id": "models_pl_models_plfreightsettlement", "label": "PlFreightSettlement", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L581"}, {"id": "models_pl_models_plfreightsettlementapproval", "label": "PlFreightSettlementApproval", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L613"}, {"id": "models_pl_models_plfreightsettlementtransport", "label": "PlFreightSettlementTransport", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L640"}, {"id": "models_pl_models_plremittancevoucher", "label": "PlRemittanceVoucher", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L660"}, {"id": "models_pl_models_plinventory", "label": "PlInventory", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L687"}, {"id": "models_pl_models_plinventorytransaction", "label": "PlInventoryTransaction", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L719"}, {"id": "models_pl_models_plinventorywarning", "label": "PlInventoryWarning", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0Line truncated
|
||||
backend/graphify-out/cache/ast/620cced501c465d38c97ff3e3fd29da4d25b940e8a1c816a2b74a9547d278db2.json
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_api_pl_plan_py", "label": "plan.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/plan.py", "source_location": "L1"}, {"id": "pl_plan_generate_plan_no", "label": "_generate_plan_no()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/plan.py", "source_location": "L84"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/plan.py", "source_location": "L84"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/plan.py", "source_location": "L84"}, {"id": "pl_plan_format_subpack_slip_no", "label": "_format_subpack_slip_no()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/plan.py", "source_location": "L116"}, {"id": "int", "label": "int", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/plan.py", "source_location": "L116"}, {"id": "pl_plan_normalize_subpack_slip_no", "label": "_normalize_subpack_slip_no()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/plan.py", "source_location": "L120"}, {"id": "pl_plan_subpack_slip_no_seq", "label": "_subpack_slip_no_seq()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/plan.py", "source_location": "L145"}, {"id": "pl_plan_ensure_subpack_slip_no", "label": "_ensure_subpack_slip_no()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/plan.py", "source_location": "L153"}, {"id": "plplan", "label": "PlPlan", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/plan.py", "source_location": "L153"}, {"id": "pl_plan_determine_plan_type", "label": "_determine_plan_type()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/plan.py", "source_location": "L185"}, {"id": "pl_plan_get_plan_type_label", "label": "_get_plan_type_label()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/plan.py", "source_location": "L190"}, {"id": "pl_plan_generate_notify_message", "label": "_generate_notify_message()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/plan.py", "source_location": "L199"}, {"id": "pl_plan_list_plans", "label": "list_plans()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/plan.py", "source_location": "L220"}, {"id": "date", "label": "date", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/plan.py", "source_location": "L220"}, {"id": "dbusercontext", "label": "DbUserContext", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/plan.py", "source_location": "L220"}, {"id": "planlistresponse", "label": "PlanListResponse", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/plan.py", "source_location": "L220"}, {"id": "pl_plan_count_plans_by_contract", "label": "count_plans_by_contract()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/plan.py", "source_location": "L332"}, {"id": "pl_plan_create_plan", "label": "create_plan()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/plan.py", "source_location": "L360"}, {"id": "plancreate", "label": "PlanCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/plan.py", "source_location": "L360"}, {"id": "planread", "label": "PlanRead", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/plan.py", "source_location": "L360"}, {"id": "pl_plan_create_plan_full", "label": "create_plan_full()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/plan.py", "source_location": "L443"}, {"id": "plancreatefull", "label": "PlanCreateFull", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/plan.py", "source_location": "L443"}, {"id": "planwithproducts", "label": "PlanWithProducts", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/plan.py", "source_location": "L443"}, {"id": "pl_plan_create_from_contract", "label": "create_from_contract()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/plan.py", "source_location": "L550"}, {"id": "createfromcontractrequest", "label": "CreateFromContractRequest", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/plan.py", "source_location": "L550"}, {"id": "pl_plan_get_plan", "label": "get_plan()", "file_type": "code",Line truncated
|
||||
backend/graphify-out/cache/ast/6b079cc6949a526f96edf0bcff31c43793c5c7a9570f18259693148c215a2cff.json
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_services_pl_cert_required_service_py", "label": "pl_cert_required_service.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L1"}, {"id": "services_pl_cert_required_service_match_required_cert_name", "label": "match_required_cert_name()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L29"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L29"}, {"id": "bool", "label": "bool", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L29"}, {"id": "services_pl_cert_required_service_cert_row_complete", "label": "_cert_row_complete()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L42"}, {"id": "certrow", "label": "CertRow", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L42"}, {"id": "services_pl_cert_required_service_find_cert_for_required", "label": "_find_cert_for_required()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L65"}, {"id": "services_pl_cert_required_service_missing_field_labels", "label": "_missing_field_labels()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L85"}, {"id": "services_pl_cert_required_service_collect_required_cert_errors", "label": "collect_required_cert_errors()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L111"}, {"id": "services_pl_cert_required_service_get_required_personnel_cert_names", "label": "get_required_personnel_cert_names()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L136"}, {"id": "services_pl_cert_required_service_validate_personnel_type_role_compat", "label": "validate_personnel_type_role_compat()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L145"}, {"id": "services_pl_cert_required_service_validate_personnel_required_certs", "label": "validate_personnel_required_certs()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L156"}, {"id": "services_pl_cert_required_service_validate_self_vehicle_required_certs", "label": "validate_self_vehicle_required_certs()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L175"}, {"id": "services_pl_cert_required_service_validate_carrier_vehicle_required_certs", "label": "validate_carrier_vehicle_required_certs()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L199"}, {"id": "services_pl_cert_required_service_validate_vehicle_personnel_affiliation", "label": "validate_vehicle_personnel_affiliation()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L223"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L223"}, {"id": "vehiclepersonnelrow", "label": "VehiclePersonnelRow", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L223"}, {"id": "services_pl_cert_required_service_validate_vehicle_personnel_cert_links", "label": "validate_vehicle_personnel_cert_links()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L257"}, {"id": "services_pl_cert_required_service_validate_vehicle_save_certs", "label": "validate_vehicle_save_certs()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py", "source_location": "L296"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_services_pl_cert_required_service_py", "target": "typing", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/woLine truncated
|
||||
backend/graphify-out/cache/ast/95969972c0773528b91d9728d303feae5ca7187d89a49495d5129dcedffc19dc.json
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_api_crm_contract_routes_py", "label": "crm_contract_routes.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/crm_contract_routes.py", "source_location": "L1"}, {"id": "api_crm_contract_routes_register_contract_routes", "label": "register_contract_routes()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/crm_contract_routes.py", "source_location": "L75"}, {"id": "api_crm_contract_routes_rationale_1", "label": "CRM \u5408\u540c API \u8def\u7531 \u2014 GET/PUT/DELETE/POST \u5bf9\u9f50\u539f Java CRM", "file_type": "rationale", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/crm_contract_routes.py", "source_location": "L1"}], "edges": [{"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_crm_contract_routes_py", "target": "datetime", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/crm_contract_routes.py", "source_location": "L4", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_crm_contract_routes_py", "target": "typing", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/crm_contract_routes.py", "source_location": "L5", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_crm_contract_routes_py", "target": "fastapi", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/crm_contract_routes.py", "source_location": "L7", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_crm_contract_routes_py", "target": "sqlalchemy_orm", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/crm_contract_routes.py", "source_location": "L8", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_crm_contract_routes_py", "target": "app_core_database", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/crm_contract_routes.py", "source_location": "L10", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_crm_contract_routes_py", "target": "app_core_menu_guard", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/crm_contract_routes.py", "source_location": "L11", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_crm_contract_routes_py", "target": "app_core_oper_log_decorator", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/crm_contract_routes.py", "source_location": "L12", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_crm_contract_routes_py", "target": "app_core_response", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/crm_contract_routes.py", "source_location": "L13", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_crm_contract_routes_py", "target": "app_core_tenant_context", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/crm_contract_routes.py", "source_location": "L14", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_crm_contract_routes_py", "target": "app_crud_crm_data_scope_helpers", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/crm_contract_routes.py", "source_location": "L15", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_crm_contract_routes_py", "target": "app_crud_crm_contract", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/crm_contract_routes.py", "source_location": "L16", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_crm_contract_routes_py", "target": "app_crud_crm_contract_approval", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/crm_contract_routes.py", "source_location": "L47", "weight": 1.0}, {"source": "users_zlwu_work_00code_shanxia2_0_backend_app_api_crm_contract_routes_py", "target": "app_schemas_crm_contract", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/zlwu/work/00code/shLine truncated
|
||||
backend/graphify-out/cache/ast/ce2b9ccf1faa70c6e084cc298aebe1b112f6515b0618f88fa29f3674c2f9649d.json
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_pl_base_data_py", "label": "base_data.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L1"}, {"id": "pl_base_data_carrierbase", "label": "CarrierBase", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L19"}, {"id": "basemodel", "label": "BaseModel", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "pl_base_data_carrierqualificationbase", "label": "CarrierQualificationBase", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L27"}, {"id": "pl_base_data_carriercreate", "label": "CarrierCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L45"}, {"id": "pl_base_data_carriercreate_validate_qualification", "label": ".validate_qualification()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L56"}, {"id": "pl_base_data_carrierupdate", "label": "CarrierUpdate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L66"}, {"id": "pl_base_data_carrierupdate_validate_qualification_patch", "label": ".validate_qualification_patch()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L88"}, {"id": "pl_base_data_carrierauditreject", "label": "CarrierAuditReject", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L109"}, {"id": "pl_base_data_carrierread", "label": "CarrierRead", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L115"}, {"id": "pl_base_data_carrier_to_read", "label": "carrier_to_read()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L134"}, {"id": "date", "label": "date", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L134"}, {"id": "pl_base_data_vehiclepersonnelrow", "label": "VehiclePersonnelRow", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L170"}, {"id": "pl_base_data_certrow", "label": "CertRow", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L181"}, {"id": "pl_base_data_certread", "label": "CertRead", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L196"}, {"id": "pl_base_data_vehiclebase", "label": "VehicleBase", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L212"}, {"id": "pl_base_data_vehiclecreate", "label": "VehicleCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L225"}, {"id": "pl_base_data_vehicleupdate", "label": "VehicleUpdate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L231"}, {"id": "pl_base_data_vehicleread", "label": "VehicleRead", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L245"}, {"id": "pl_base_data_vehicledetailread", "label": "VehicleDetailRead", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L259"}, {"id": "pl_base_data_personnelbase", "label": "PersonnelBase", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L270"}, {"id": "pl_base_data_personnelcreate", "label": "PersonnelCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L285"}, {"id": "pl_base_data_personnelupdate", "label": "PersonnelUpdate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L290"}, {"id": "pl_base_data_personnelread", "label": "PersonnelRead", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L302"}, {"id": "pl_base_data_personneldetailread", "label": "PersonnelDetailRead", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0Line truncated
|
||||
backend/graphify-out/cache/ast/ecd3d8e5dbdb55a8b25c434ad9935bacdf275e23af051e45fea79be017d1c633.json
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_schemas_pl_base_data_py", "label": "base_data.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L1"}, {"id": "pl_base_data_carrierbase", "label": "CarrierBase", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L19"}, {"id": "basemodel", "label": "BaseModel", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "pl_base_data_carrierqualificationbase", "label": "CarrierQualificationBase", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L27"}, {"id": "pl_base_data_carriercreate", "label": "CarrierCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L45"}, {"id": "pl_base_data_carriercreate_validate_qualification", "label": ".validate_qualification()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L56"}, {"id": "pl_base_data_carrierupdate", "label": "CarrierUpdate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L66"}, {"id": "pl_base_data_carrierupdate_validate_qualification_patch", "label": ".validate_qualification_patch()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L88"}, {"id": "pl_base_data_carrierauditreject", "label": "CarrierAuditReject", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L109"}, {"id": "pl_base_data_carrierread", "label": "CarrierRead", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L115"}, {"id": "pl_base_data_carrier_to_read", "label": "carrier_to_read()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L134"}, {"id": "date", "label": "date", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L134"}, {"id": "pl_base_data_vehiclepersonnelrow", "label": "VehiclePersonnelRow", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L170"}, {"id": "pl_base_data_certrow", "label": "CertRow", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L181"}, {"id": "pl_base_data_certread", "label": "CertRead", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L196"}, {"id": "pl_base_data_vehiclebase", "label": "VehicleBase", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L212"}, {"id": "pl_base_data_vehiclecreate", "label": "VehicleCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L225"}, {"id": "pl_base_data_vehicleupdate", "label": "VehicleUpdate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L231"}, {"id": "pl_base_data_vehicleread", "label": "VehicleRead", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L245"}, {"id": "pl_base_data_vehicledetailread", "label": "VehicleDetailRead", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L259"}, {"id": "pl_base_data_personnelbase", "label": "PersonnelBase", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L270"}, {"id": "pl_base_data_personnelcreate", "label": "PersonnelCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L291"}, {"id": "pl_base_data_personnelupdate", "label": "PersonnelUpdate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L296"}, {"id": "pl_base_data_personnelread", "label": "PersonnelRead", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py", "source_location": "L311"}, {"id": "pl_base_data_personneldetailread", "label": "PersonnelDetailRead", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0Line truncated
|
||||
backend/graphify-out/cache/ast/f644a9595a6c96924390a655ed92dee806b0dfc8cfb2001061d469a0d80caf91.json
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_models_pl_models_py", "label": "pl_models.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L1"}, {"id": "models_pl_models_plcarrier", "label": "PlCarrier", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L28"}, {"id": "base", "label": "Base", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "models_pl_models_plvehicle", "label": "PlVehicle", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L57"}, {"id": "models_pl_models_plpersonnel", "label": "PlPersonnel", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L83"}, {"id": "models_pl_models_pltransportaddress", "label": "PlTransportAddress", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L117"}, {"id": "models_pl_models_plcert", "label": "PlCert", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L141"}, {"id": "models_pl_models_pldriver", "label": "PlDriver", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L167"}, {"id": "models_pl_models_plvehiclepersonnel", "label": "PlVehiclePersonnel", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L193"}, {"id": "models_pl_models_plvehicledriver", "label": "PlVehicleDriver", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L214"}, {"id": "models_pl_models_plplan", "label": "PlPlan", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L239"}, {"id": "models_pl_models_plplanproduct", "label": "PlPlanProduct", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L338"}, {"id": "models_pl_models_plplanchange", "label": "PlPlanChange", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L394"}, {"id": "models_pl_models_pltransport", "label": "PlTransport", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L421"}, {"id": "models_pl_models_pltransportdblink", "label": "PlTransportDbLink", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L483"}, {"id": "models_pl_models_plplantransport", "label": "PlPlanTransport", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L506"}, {"id": "models_pl_models_pltransportsitelink", "label": "PlTransportSiteLink", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L525"}, {"id": "models_pl_models_pltransportapproval", "label": "PlTransportApproval", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L558"}, {"id": "models_pl_models_plfreightsettlement", "label": "PlFreightSettlement", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L587"}, {"id": "models_pl_models_plfreightsettlementapproval", "label": "PlFreightSettlementApproval", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L619"}, {"id": "models_pl_models_plfreightsettlementtransport", "label": "PlFreightSettlementTransport", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L646"}, {"id": "models_pl_models_plremittancevoucher", "label": "PlRemittanceVoucher", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L666"}, {"id": "models_pl_models_plinventory", "label": "PlInventory", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L693"}, {"id": "models_pl_models_plinventorytransaction", "label": "PlInventoryTransaction", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py", "source_location": "L725"}, {"id": "models_pl_models_plinventorywarning", "label": "PlInventoryWarning", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0Line truncated
|
||||
backend/graphify-out/cache/ast/fca395a2abc3bbf141be8b4ebd59a4c368f12368eb4fc28e0da254fcd35b7a50.json
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_zlwu_work_00code_shanxia2_0_backend_app_api_pl_base_data_py", "label": "base_data.py", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L1"}, {"id": "pl_base_data_list_carriers", "label": "list_carriers()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L74"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L74"}, {"id": "int", "label": "int", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L74"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L74"}, {"id": "dbusercontext", "label": "DbUserContext", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L74"}, {"id": "carrierlistresponse", "label": "CarrierListResponse", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L74"}, {"id": "pl_base_data_create_carrier", "label": "create_carrier()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L119"}, {"id": "carriercreate", "label": "CarrierCreate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L119"}, {"id": "carrierread", "label": "CarrierRead", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L119"}, {"id": "pl_base_data_get_carrier", "label": "get_carrier()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L149"}, {"id": "pl_base_data_update_carrier", "label": "update_carrier()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L160"}, {"id": "carrierupdate", "label": "CarrierUpdate", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L160"}, {"id": "pl_base_data_delete_carrier", "label": "delete_carrier()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L194"}, {"id": "pl_base_data_approve_carrier", "label": "approve_carrier()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L208"}, {"id": "pl_base_data_reject_carrier", "label": "reject_carrier()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L232"}, {"id": "carrierauditreject", "label": "CarrierAuditReject", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L232"}, {"id": "plcarrier", "label": "PlCarrier", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L254"}, {"id": "pl_base_data_vehicle_to_read", "label": "_vehicle_to_read()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L275"}, {"id": "plvehicle", "label": "PlVehicle", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L275"}, {"id": "vehicleread", "label": "VehicleRead", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L275"}, {"id": "pl_base_data_vehicle_to_detail", "label": "_vehicle_to_detail()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L288"}, {"id": "vehicledetailread", "label": "VehicleDetailRead", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L288"}, {"id": "pl_base_data_list_vehicles", "label": "list_vehicles()", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L306"}, {"id": "bool", "label": "bool", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L306"}, {"id": "vehiclelistresponse", "label": "VehicleListResponse", "file_type": "code", "source_file": "/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py", "source_location": "L306"}, {"id": "pl_base_data_create_vehicle", "laLine truncated
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"/Users/zlwu/work/00code/shanxia2.0/backend/start-prod.sh":{"size":7357,"mtime_ns":1782639355476060453,"hash":"ca89869446be74a2bf2367b3b28f448580220859a39a84ad9613ac5fe35a8433"},"/Users/zlwu/work/00code/shanxia2.0/backend/start.sh":{"size":9548,"mtime_ns":1780114661446983244,"hash":"4d632863e8a0cd81194b491d83a0218a4157435ae812dec76a6f6197b4d48e57"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/__init__.py":{"size":21,"mtime_ns":1780113982281973984,"hash":"60a04ff08fd97084e83ea809fd13feda6d2848be20ebf309e31fc1dad7523230"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/main.py":{"size":6280,"mtime_ns":1782790852770448643,"hash":"8f9da799977bee7134d87a26ff8434ac5ef8eabb0b7368dd537eee49ad7fd00b"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/tasks/__init__.py":{"size":2207,"mtime_ns":1783208067881286091,"hash":"313e6da0e91d743ce891dd283458c79b50b24c4d909dc96ca7b3fd9d2bfa5013"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/auth.py":{"size":7457,"mtime_ns":1782790841500516545,"hash":"34bac56e1c2cb6215f4b5fa2dc32d77d4b4abca51428819eebf4afd269cc51cf"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/api_crypto.py":{"size":1870,"mtime_ns":1780108930724444498,"hash":"c04b748aea86890a5971f749523c7b17dbad1ddd6f9f7f07a5679d159c984e33"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/login_crypto.py":{"size":3858,"mtime_ns":1780108930725155710,"hash":"b82d51afabf2293a9cb1fb2e5f994c383948e7f47ea95fe674686b4f3f3342b1"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/config.py":{"size":6716,"mtime_ns":1783761682571576393,"hash":"bbe64081c9790941eb2fa8f9c18ee39daf151068b3503f1fdb74c2cb9f7b5b45"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/wechat_pay.py":{"size":23585,"mtime_ns":1780108930725768130,"hash":"3d52265ce65d21e7103f94afa1e8547aee0823b5ea6d65c50317436ac81fb4fc"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/api_request_log_middleware.py":{"size":11971,"mtime_ns":1782701669198208175,"hash":"0b84c32828939e6102bb3647691fa8f04b1f68e0e0e9f28127374cc44e19a11c"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/database.py":{"size":1510,"mtime_ns":1782790847265601586,"hash":"8bc9c8ae62e2be2c6920036582ba448dfcd1711c41cebb62274ff6bc6ee14b0b"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/security.py":{"size":728,"mtime_ns":1780108930725470004,"hash":"df0f01115064f7b8de1c81dbe9a6db39360c236610b68c33b3234a51d7f0025b"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/request_payload.py":{"size":2842,"mtime_ns":1780108930725350836,"hash":"e9937151530ab1417ef297aef59111977725cf90be2a8fa76c00a0618789aba0"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/__init__.py":{"size":61,"mtime_ns":1780108930724312414,"hash":"3dd1842ee59b0c804176b03b4b8a323bd4a252b7feb1bbc32db64f3a8928570e"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/response.py":{"size":1824,"mtime_ns":1780108930725412462,"hash":"f1739352d65ea8287d76e282a59553aa8d63c97e35764c64ac378aa5bd9c347e"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/menu_guard.py":{"size":1611,"mtime_ns":1780108930725223002,"hash":"3e26077288c3620a4780038b891a326206b45bffe00aca545bdae30172487235"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/db_collation.py":{"size":548,"mtime_ns":1780108930725025043,"hash":"0c680e25a18244ca839a7de6ed1f4313b33bc8da118d89c463aee1b6af00c6f9"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/context.py":{"size":421,"mtime_ns":1780108930724824458,"hash":"83a10aee67162374993c10a30bf8bb8bd9c4b35cf48f42c832415ccebd91660f"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/utils.py":{"size":1498,"mtime_ns":1780108930725607296,"hash":"09323693661c1d4560458b86a954128ccff82bbaf6522da0b44f24da99ddf9ea"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/storage.py":{"size":14141,"mtime_ns":1782855136810943216,"hash":"3ae2c413c10898a8adb3d6773870bafbfbc18c739c10e0e8b4f765b4b5ca6c08"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/order_status_compat.py":{"size":2828,"mtime_ns":1780108930725284669,"hash":"5349e5a0745ee6734c4a995337779307adefc46f453906171988a77de2081540"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/exceptions.py":{"size":4946,"mtime_ns":1780108930725084335,"hash":"79c997d964bca1a4f12f2b92fb1f181fdb9e2b3cf72eae39f4cc164d23794fd7"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/data_scope.py":{"size":8043,"mtime_ns":1782648925694296304,"hash":"626dff86937e5cacec634ecbc61cd53d3763d760223a1d4ec7cd11f64e59a220"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/admin_request_json.py":{"size":2669,"mtime_ns":1780108930724378914,"hash":"4f68b3cf84291e7bddad6ecf4bb4d29ca5e1ec246a9f7f9c4cf5909d0ec1174a"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/api_crypto_middleware.py":{"size":10876,"mtime_ns":1782790001926627098,"hash":"b39f128b6612589ea6ac5babd30752e9585b3af07b82bb1ad53c9e3c0d90816f"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/wechat_message.py":{"size":11194,"mtime_ns":1780108930725684255,"hash":"5086070deb14d99d1a69e419b2299ec4Line truncated
|
||||
{"/Users/zlwu/work/00code/shanxia2.0/backend/start-prod.sh":{"size":7357,"mtime_ns":1782639355476060453,"hash":"ca89869446be74a2bf2367b3b28f448580220859a39a84ad9613ac5fe35a8433"},"/Users/zlwu/work/00code/shanxia2.0/backend/start.sh":{"size":9548,"mtime_ns":1780114661446983244,"hash":"4d632863e8a0cd81194b491d83a0218a4157435ae812dec76a6f6197b4d48e57"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/__init__.py":{"size":21,"mtime_ns":1780113982281973984,"hash":"60a04ff08fd97084e83ea809fd13feda6d2848be20ebf309e31fc1dad7523230"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/main.py":{"size":6280,"mtime_ns":1782790852770448643,"hash":"8f9da799977bee7134d87a26ff8434ac5ef8eabb0b7368dd537eee49ad7fd00b"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/tasks/__init__.py":{"size":2207,"mtime_ns":1783208067881286091,"hash":"313e6da0e91d743ce891dd283458c79b50b24c4d909dc96ca7b3fd9d2bfa5013"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/auth.py":{"size":7457,"mtime_ns":1782790841500516545,"hash":"34bac56e1c2cb6215f4b5fa2dc32d77d4b4abca51428819eebf4afd269cc51cf"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/api_crypto.py":{"size":1870,"mtime_ns":1780108930724444498,"hash":"c04b748aea86890a5971f749523c7b17dbad1ddd6f9f7f07a5679d159c984e33"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/login_crypto.py":{"size":3858,"mtime_ns":1780108930725155710,"hash":"b82d51afabf2293a9cb1fb2e5f994c383948e7f47ea95fe674686b4f3f3342b1"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/config.py":{"size":6716,"mtime_ns":1783761682571576393,"hash":"bbe64081c9790941eb2fa8f9c18ee39daf151068b3503f1fdb74c2cb9f7b5b45"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/wechat_pay.py":{"size":23585,"mtime_ns":1780108930725768130,"hash":"3d52265ce65d21e7103f94afa1e8547aee0823b5ea6d65c50317436ac81fb4fc"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/api_request_log_middleware.py":{"size":11971,"mtime_ns":1782701669198208175,"hash":"0b84c32828939e6102bb3647691fa8f04b1f68e0e0e9f28127374cc44e19a11c"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/database.py":{"size":1510,"mtime_ns":1782790847265601586,"hash":"8bc9c8ae62e2be2c6920036582ba448dfcd1711c41cebb62274ff6bc6ee14b0b"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/security.py":{"size":728,"mtime_ns":1780108930725470004,"hash":"df0f01115064f7b8de1c81dbe9a6db39360c236610b68c33b3234a51d7f0025b"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/request_payload.py":{"size":2842,"mtime_ns":1780108930725350836,"hash":"e9937151530ab1417ef297aef59111977725cf90be2a8fa76c00a0618789aba0"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/__init__.py":{"size":61,"mtime_ns":1780108930724312414,"hash":"3dd1842ee59b0c804176b03b4b8a323bd4a252b7feb1bbc32db64f3a8928570e"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/response.py":{"size":1824,"mtime_ns":1780108930725412462,"hash":"f1739352d65ea8287d76e282a59553aa8d63c97e35764c64ac378aa5bd9c347e"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/menu_guard.py":{"size":1611,"mtime_ns":1780108930725223002,"hash":"3e26077288c3620a4780038b891a326206b45bffe00aca545bdae30172487235"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/db_collation.py":{"size":548,"mtime_ns":1780108930725025043,"hash":"0c680e25a18244ca839a7de6ed1f4313b33bc8da118d89c463aee1b6af00c6f9"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/context.py":{"size":421,"mtime_ns":1780108930724824458,"hash":"83a10aee67162374993c10a30bf8bb8bd9c4b35cf48f42c832415ccebd91660f"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/utils.py":{"size":1498,"mtime_ns":1780108930725607296,"hash":"09323693661c1d4560458b86a954128ccff82bbaf6522da0b44f24da99ddf9ea"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/storage.py":{"size":14141,"mtime_ns":1782855136810943216,"hash":"3ae2c413c10898a8adb3d6773870bafbfbc18c739c10e0e8b4f765b4b5ca6c08"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/order_status_compat.py":{"size":2828,"mtime_ns":1780108930725284669,"hash":"5349e5a0745ee6734c4a995337779307adefc46f453906171988a77de2081540"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/exceptions.py":{"size":4946,"mtime_ns":1780108930725084335,"hash":"79c997d964bca1a4f12f2b92fb1f181fdb9e2b3cf72eae39f4cc164d23794fd7"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/data_scope.py":{"size":8043,"mtime_ns":1782648925694296304,"hash":"626dff86937e5cacec634ecbc61cd53d3763d760223a1d4ec7cd11f64e59a220"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/admin_request_json.py":{"size":2669,"mtime_ns":1780108930724378914,"hash":"4f68b3cf84291e7bddad6ecf4bb4d29ca5e1ec246a9f7f9c4cf5909d0ec1174a"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/api_crypto_middleware.py":{"size":10876,"mtime_ns":1782790001926627098,"hash":"b39f128b6612589ea6ac5babd30752e9585b3af07b82bb1ad53c9e3c0d90816f"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/wechat_message.py":{"size":11194,"mtime_ns":1780108930725684255,"hash":"5086070deb14d99d1a69e419b2299ec4Line truncated
|
||||
+4346
-1989
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,24 @@
|
||||
-- pl_personnel 增加人员归属:self-山峡 carrier-非山峡
|
||||
SET @col_exists := (
|
||||
SELECT COUNT(*)
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'pl_personnel'
|
||||
AND COLUMN_NAME = 'personnel_affiliation'
|
||||
);
|
||||
|
||||
SET @ddl := IF(
|
||||
@col_exists = 0,
|
||||
'ALTER TABLE `pl_personnel`
|
||||
ADD COLUMN `personnel_affiliation` varchar(20) NOT NULL DEFAULT ''self''
|
||||
COMMENT ''人员归属 self-山峡 carrier-非山峡''
|
||||
AFTER `personnel_type`',
|
||||
'SELECT ''personnel_affiliation already exists'' AS msg'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
UPDATE `pl_personnel`
|
||||
SET `personnel_affiliation` = 'self'
|
||||
WHERE `personnel_affiliation` IS NULL OR `personnel_affiliation` = '';
|
||||
@@ -0,0 +1,52 @@
|
||||
-- pl_personnel 增加人员类型;历史数据按车辆关联推断
|
||||
-- 可重复执行(列已存在则跳过加列,UPDATE 可重复跑)
|
||||
|
||||
SET @col_exists := (
|
||||
SELECT COUNT(*)
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'pl_personnel'
|
||||
AND COLUMN_NAME = 'personnel_type'
|
||||
);
|
||||
|
||||
SET @ddl := IF(
|
||||
@col_exists = 0,
|
||||
'ALTER TABLE `pl_personnel`
|
||||
ADD COLUMN `personnel_type` varchar(20) NOT NULL DEFAULT ''both''
|
||||
COMMENT ''人员类型 driver-驾驶员 escort-押运员 both-驾驶员兼押运员''
|
||||
AFTER `id_card_no`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- 仅有 driver 关联
|
||||
UPDATE pl_personnel p
|
||||
SET p.personnel_type = 'driver'
|
||||
WHERE p.del_flag = 0
|
||||
AND (p.personnel_type IS NULL OR p.personnel_type = '' OR p.personnel_type = 'both')
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM pl_vehicle_personnel vp
|
||||
WHERE vp.personnel_id = p.id AND vp.del_flag = 0 AND vp.role = 'driver'
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM pl_vehicle_personnel vp
|
||||
WHERE vp.personnel_id = p.id AND vp.del_flag = 0 AND vp.role = 'escort'
|
||||
);
|
||||
|
||||
-- 仅有 escort 关联
|
||||
UPDATE pl_personnel p
|
||||
SET p.personnel_type = 'escort'
|
||||
WHERE p.del_flag = 0
|
||||
AND (p.personnel_type IS NULL OR p.personnel_type = '' OR p.personnel_type = 'both')
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM pl_vehicle_personnel vp
|
||||
WHERE vp.personnel_id = p.id AND vp.del_flag = 0 AND vp.role = 'escort'
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM pl_vehicle_personnel vp
|
||||
WHERE vp.personnel_id = p.id AND vp.del_flag = 0 AND vp.role = 'driver'
|
||||
);
|
||||
|
||||
-- 同时有 driver + escort 或无关联保持 both(默认)
|
||||
@@ -0,0 +1,24 @@
|
||||
-- pl_personnel 增加人员归属:self-山峡 carrier-非山峡
|
||||
SET @col_exists := (
|
||||
SELECT COUNT(*)
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'pl_personnel'
|
||||
AND COLUMN_NAME = 'personnel_affiliation'
|
||||
);
|
||||
|
||||
SET @ddl := IF(
|
||||
@col_exists = 0,
|
||||
'ALTER TABLE `pl_personnel`
|
||||
ADD COLUMN `personnel_affiliation` varchar(20) NOT NULL DEFAULT ''self''
|
||||
COMMENT ''人员归属 self-山峡 carrier-非山峡''
|
||||
AFTER `personnel_type`',
|
||||
'SELECT ''personnel_affiliation already exists'' AS msg'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
UPDATE `pl_personnel`
|
||||
SET `personnel_affiliation` = 'self'
|
||||
WHERE `personnel_affiliation` IS NULL OR `personnel_affiliation` = '';
|
||||
@@ -0,0 +1,52 @@
|
||||
-- pl_personnel 增加人员类型;历史数据按车辆关联推断
|
||||
-- 可重复执行(列已存在则跳过加列,UPDATE 可重复跑)
|
||||
|
||||
SET @col_exists := (
|
||||
SELECT COUNT(*)
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'pl_personnel'
|
||||
AND COLUMN_NAME = 'personnel_type'
|
||||
);
|
||||
|
||||
SET @ddl := IF(
|
||||
@col_exists = 0,
|
||||
'ALTER TABLE `pl_personnel`
|
||||
ADD COLUMN `personnel_type` varchar(20) NOT NULL DEFAULT ''both''
|
||||
COMMENT ''人员类型 driver-驾驶员 escort-押运员 both-驾驶员兼押运员''
|
||||
AFTER `id_card_no`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- 仅有 driver 关联
|
||||
UPDATE pl_personnel p
|
||||
SET p.personnel_type = 'driver'
|
||||
WHERE p.del_flag = 0
|
||||
AND (p.personnel_type IS NULL OR p.personnel_type = '' OR p.personnel_type = 'both')
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM pl_vehicle_personnel vp
|
||||
WHERE vp.personnel_id = p.id AND vp.del_flag = 0 AND vp.role = 'driver'
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM pl_vehicle_personnel vp
|
||||
WHERE vp.personnel_id = p.id AND vp.del_flag = 0 AND vp.role = 'escort'
|
||||
);
|
||||
|
||||
-- 仅有 escort 关联
|
||||
UPDATE pl_personnel p
|
||||
SET p.personnel_type = 'escort'
|
||||
WHERE p.del_flag = 0
|
||||
AND (p.personnel_type IS NULL OR p.personnel_type = '' OR p.personnel_type = 'both')
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM pl_vehicle_personnel vp
|
||||
WHERE vp.personnel_id = p.id AND vp.del_flag = 0 AND vp.role = 'escort'
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM pl_vehicle_personnel vp
|
||||
WHERE vp.personnel_id = p.id AND vp.del_flag = 0 AND vp.role = 'driver'
|
||||
);
|
||||
|
||||
-- 同时有 driver + escort 或无关联保持 both(默认)
|
||||
@@ -81,6 +81,8 @@ CREATE TABLE `pl_personnel` (
|
||||
`phone_no` varchar(32) DEFAULT NULL COMMENT '手机号(非必填)',
|
||||
`age` int(11) DEFAULT NULL COMMENT '年龄',
|
||||
`id_card_no` varchar(32) DEFAULT NULL COMMENT '身份证号',
|
||||
`personnel_type` varchar(20) NOT NULL DEFAULT 'both' COMMENT '人员类型 driver-驾驶员 escort-押运员 both-驾驶员兼押运员',
|
||||
`personnel_affiliation` varchar(20) NOT NULL DEFAULT 'self' COMMENT '人员归属 self-山峡 carrier-非山峡',
|
||||
`status` char(1) NOT NULL DEFAULT '1' COMMENT '状态 1-在职 0-停用',
|
||||
`tenant_no` varchar(64) NOT NULL DEFAULT 'shanxia2.0' COMMENT '租户编号',
|
||||
`order_num` int(11) DEFAULT NULL COMMENT '排序号',
|
||||
|
||||
@@ -2752,6 +2752,7 @@ CREATE TABLE `pl_personnel` (
|
||||
`phone_no` varchar(32) DEFAULT NULL COMMENT '手机号(非必填)',
|
||||
`age` int(11) DEFAULT NULL COMMENT '年龄',
|
||||
`id_card_no` varchar(32) DEFAULT NULL COMMENT '身份证号',
|
||||
`personnel_type` varchar(20) NOT NULL DEFAULT 'both' COMMENT '人员类型 driver-驾驶员 escort-押运员 both-驾驶员兼押运员',
|
||||
`status` char(1) NOT NULL DEFAULT '1' COMMENT '状态 1-在职 0-停用',
|
||||
`tenant_no` varchar(64) NOT NULL DEFAULT 'shanxia2.0' COMMENT '租户编号',
|
||||
`order_num` int(11) DEFAULT NULL COMMENT '排序号',
|
||||
@@ -2772,10 +2773,10 @@ CREATE TABLE `pl_personnel` (
|
||||
-- Records of pl_personnel
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
INSERT INTO `pl_personnel` (`id`, `personnel_name`, `phone_no`, `age`, `id_card_no`, `status`, `tenant_no`, `order_num`, `remark`, `create_by`, `create_dept_id`, `create_time`, `update_by`, `update_time`, `del_flag`) VALUES ('3C13B995C2B149E8AA50222609D030A1', '里斯', '13099922123', 32, '123123', '1', 'shanxia2.0', NULL, '123123', 'd3000000000000000000000000000001', 'b13c2be2-4e71-11f1-9f47-00163e3bdd96', '2026-06-20 07:45:10', 'd3000000000000000000000000000001', '2026-06-20 07:55:22', 0);
|
||||
INSERT INTO `pl_personnel` (`id`, `personnel_name`, `phone_no`, `age`, `id_card_no`, `status`, `tenant_no`, `order_num`, `remark`, `create_by`, `create_dept_id`, `create_time`, `update_by`, `update_time`, `del_flag`) VALUES ('4B753C6E5CE446AB9208C449A76BEA0C', '张三', '13900022112', 35, '340829181823123123123', '1', 'shanxia2.0', NULL, '备注', 'd3000000000000000000000000000001', 'b13c2be2-4e71-11f1-9f47-00163e3bdd96', '2026-06-19 22:56:38', 'd3000000000000000000000000000001', '2026-06-20 07:44:42', 0);
|
||||
INSERT INTO `pl_personnel` (`id`, `personnel_name`, `phone_no`, `age`, `id_card_no`, `status`, `tenant_no`, `order_num`, `remark`, `create_by`, `create_dept_id`, `create_time`, `update_by`, `update_time`, `del_flag`) VALUES ('9AB6B5474343428C80C24503EB7771DC', '刘道银', '130023312', 32, '34082219912312', '1', 'shanxia2.0', NULL, '司机', 'BFB4CBEAB7054F48BDE82B55A985798C', 'b13c2be2-4e71-11f1-9f47-00163e3bdd96', '2026-06-28 14:57:39', 'd3000000000000000000000000000001', '2026-06-28 16:11:58', 0);
|
||||
INSERT INTO `pl_personnel` (`id`, `personnel_name`, `phone_no`, `age`, `id_card_no`, `status`, `tenant_no`, `order_num`, `remark`, `create_by`, `create_dept_id`, `create_time`, `update_by`, `update_time`, `del_flag`) VALUES ('BD11DEEA3C1E4C8EA75CDF18A6EED21F', '吴春生', '1390001231', 32, '340822121323', '1', 'shanxia2.0', NULL, '押运员', 'BFB4CBEAB7054F48BDE82B55A985798C', 'b13c2be2-4e71-11f1-9f47-00163e3bdd96', '2026-06-28 14:56:57', 'd3000000000000000000000000000001', '2026-06-28 16:11:58', 0);
|
||||
INSERT INTO `pl_personnel` (`id`, `personnel_name`, `phone_no`, `age`, `id_card_no`, `personnel_type`, `status`, `tenant_no`, `order_num`, `remark`, `create_by`, `create_dept_id`, `create_time`, `update_by`, `update_time`, `del_flag`) VALUES ('3C13B995C2B149E8AA50222609D030A1', '里斯', '13099922123', 32, '123123', 'both', '1', 'shanxia2.0', NULL, '123123', 'd3000000000000000000000000000001', 'b13c2be2-4e71-11f1-9f47-00163e3bdd96', '2026-06-20 07:45:10', 'd3000000000000000000000000000001', '2026-06-20 07:55:22', 0);
|
||||
INSERT INTO `pl_personnel` (`id`, `personnel_name`, `phone_no`, `age`, `id_card_no`, `personnel_type`, `status`, `tenant_no`, `order_num`, `remark`, `create_by`, `create_dept_id`, `create_time`, `update_by`, `update_time`, `del_flag`) VALUES ('4B753C6E5CE446AB9208C449A76BEA0C', '张三', '13900022112', 35, '340829181823123123123', 'both', '1', 'shanxia2.0', NULL, '备注', 'd3000000000000000000000000000001', 'b13c2be2-4e71-11f1-9f47-00163e3bdd96', '2026-06-19 22:56:38', 'd3000000000000000000000000000001', '2026-06-20 07:44:42', 0);
|
||||
INSERT INTO `pl_personnel` (`id`, `personnel_name`, `phone_no`, `age`, `id_card_no`, `personnel_type`, `status`, `tenant_no`, `order_num`, `remark`, `create_by`, `create_dept_id`, `create_time`, `update_by`, `update_time`, `del_flag`) VALUES ('9AB6B5474343428C80C24503EB7771DC', '刘道银', '130023312', 32, '34082219912312', 'driver', '1', 'shanxia2.0', NULL, '司机', 'BFB4CBEAB7054F48BDE82B55A985798C', 'b13c2be2-4e71-11f1-9f47-00163e3bdd96', '2026-06-28 14:57:39', 'd3000000000000000000000000000001', '2026-06-28 16:11:58', 0);
|
||||
INSERT INTO `pl_personnel` (`id`, `personnel_name`, `phone_no`, `age`, `id_card_no`, `personnel_type`, `status`, `tenant_no`, `order_num`, `remark`, `create_by`, `create_dept_id`, `create_time`, `update_by`, `update_time`, `del_flag`) VALUES ('BD11DEEA3C1E4C8EA75CDF18A6EED21F', '吴春生', '1390001231', 32, '340822121323', 'escort', '1', 'shanxia2.0', NULL, '押运员', 'BFB4CBEAB7054F48BDE82B55A985798C', 'b13c2be2-4e71-11f1-9f47-00163e3bdd96', '2026-06-28 14:56:57', 'd3000000000000000000000000000001', '2026-06-28 16:11:58', 0);
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
|
||||
@@ -0,0 +1,340 @@
|
||||
# 运输管理 · 固定必备证书槽位 — 设计文档
|
||||
|
||||
> **日期**: 2026-07-12
|
||||
> **状态**: 已实现(2026-07-12)
|
||||
> **前置**: `docs/superpowers/specs/2026-06-19-pl-vehicle-personnel-cert-design.md`(动态多证、首期不配置必检)
|
||||
> **界面模式**: P01 标准列表 + 弹窗表单(`/#/pl/vehicle`、`/#/pl/self-vehicle`、`/#/pl/personnel`)
|
||||
|
||||
## 背景与问题
|
||||
|
||||
### 现状(2026-06-19 上线后)
|
||||
|
||||
| 环节 | 行为 |
|
||||
|------|------|
|
||||
| 证书录入 | 用户点击「添加证书」→ 手选/手输证书名称 → 填写证号、有效期 → 上传附件 |
|
||||
| 前端校验 | 仅有证书行时「证书名称不能为空」;**不**校验必备证种齐全 |
|
||||
| 必备常数 | `pl-vehicle-cert.ts` / `pl-personnel-cert.ts` 已定义必备名称,但 `validateVehicleRequiredCerts` 已 `@deprecated` |
|
||||
| 缺失提示 | 查看态标题可显示「(证书缺失)」;**保存不阻断** |
|
||||
| 人员类型 | `pl_personnel` **无**人员类型字段;司机/押运员角色仅在车辆关联子表 `pl_vehicle_personnel.role` |
|
||||
| 承运商车辆 | 「全部车辆信息」含自有 + 承运商;必备缺失检查仅对 `vehicleType=self` |
|
||||
|
||||
### 业务诉求(2026-07-12 确认)
|
||||
|
||||
1. **固定必备槽位**:打开编辑弹窗时,系统预置必备证书行,名称锁定不可改、不可删。
|
||||
2. **必备三项必填**:每个必备槽位的 **证号、有效期、附件** 均必填。
|
||||
3. **可增补其他证件**:除必备行外,仍可「添加其他证书」(名称可选/可输,可删)。
|
||||
4. **山峡自有车强制**:仅 `vehicleType=self` 的车辆强制主车/挂车必备;**承运商等非山峡车辆不强制**。
|
||||
5. **人员类型**:人员主数据新增类型——驾驶员、押运员、驾驶员兼押运员;按类型预置必备证书。
|
||||
6. **历史数据**:**一律阻断**——只要保存(含仅改备注),必备证书未补齐则拒绝。
|
||||
|
||||
### 与 2026-06-19 设计的关系
|
||||
|
||||
2026-06-19 明确「首期不配置必检规则、安排运输不硬性拦截证书」。本次变更:
|
||||
|
||||
- **升级** 车辆/人员主数据保存时的证书完整性要求(仅山峡自有车 + 全部人员主数据)。
|
||||
- **不变更** 运输计划安排运输时对 `hazard_cert_ready` 的既有逻辑(危化品计划级证书另案)。
|
||||
- **沿用** `pl_cert` 动态多证表结构,不新增证书实例表。
|
||||
|
||||
---
|
||||
|
||||
## 已确认设计
|
||||
|
||||
| # | 决策 | 说明 |
|
||||
|---|------|------|
|
||||
| D1 | **必备槽位预置 + 锁定** | 必备行 `certName` 只读、不可删除;额外行可自由增删 |
|
||||
| D2 | **必备字段:证号 + 有效期 + 附件** | 提醒天数维持默认 30,不作为必备条件 |
|
||||
| D3 | **仅山峡自有车强制车辆证** | `vehicleType=self` 时校验主车 3 证、有挂车号时挂车 4 证;`carrier` 不校验 |
|
||||
| D4 | **人员类型三选一** | `driver` 驾驶员 / `escort` 押运员 / `both` 驾驶员兼押运员;人员主数据必填 |
|
||||
| D5 | **人员必备按类型** | driver、both → 身份证+驾驶证+从业资格证;escort → 身份证+从业资格证 |
|
||||
| D6 | **从业资格证别名** | 「危化品从业资格证」等等价匹配「从业资格证」(沿用 `matchRequiredCertName`) |
|
||||
| D7 | **保存一律阻断** | 历史数据缺必备证时,任何字段保存均须先补齐 |
|
||||
| D8 | **前后端双校验** | 前端 UX + 后端 API 400,防绕过 |
|
||||
| D9 | **关联人员角色兼容** | 车辆子表 `role` 保留;保存山峡自有车时校验人员类型与 role 可匹配(见 §三) |
|
||||
| D10 | **挂车动态** | 无 `trailerNo` 不要求挂车槽位;填写挂车号后立即要求 4 项必备 |
|
||||
|
||||
---
|
||||
|
||||
## 一、必备证书清单
|
||||
|
||||
### 1.1 山峡自有车辆(`vehicleType=self`)
|
||||
|
||||
| 部位 | 条件 | 必备证书(各需证号+有效期+附件) |
|
||||
|------|------|----------------------------------|
|
||||
| 主车 | 始终 | 行驶证、营运证、保险单 |
|
||||
| 挂车 | `trailerNo` 非空 | 行驶证、营运证、保险单、罐检报告 |
|
||||
|
||||
常数来源:`PL_SELF_VEHICLE_HEAD_REQUIRED_CERT_NAMES`、`PL_SELF_VEHICLE_TRAILER_REQUIRED_CERT_NAMES`(`web-admin/src/utils/pl-vehicle-cert.ts`)。
|
||||
|
||||
### 1.2 承运商 / 非自有车辆(`vehicleType=carrier`)
|
||||
|
||||
- 不预置必备行(或预置但不阻断,**推荐:不预置、不校验**,与现网自由录入一致)。
|
||||
- 用户仍可手动添加任意证书。
|
||||
|
||||
### 1.3 人员(`pl_personnel.personnel_type`)
|
||||
|
||||
| personnel_type | 展示文案 | 必备证书 |
|
||||
|----------------|---------|---------|
|
||||
| `driver` | 驾驶员 | 身份证、驾驶证、从业资格证 |
|
||||
| `escort` | 押运员 | 身份证、从业资格证 |
|
||||
| `both` | 驾驶员兼押运员 | 身份证、驾驶证、从业资格证 |
|
||||
|
||||
常数来源:`PL_PERSONNEL_DRIVER_REQUIRED_CERT_NAMES`、`PL_PERSONNEL_ESCORT_REQUIRED_CERT_NAMES`。
|
||||
|
||||
### 1.4 「证书已填」判定
|
||||
|
||||
必备槽位 **同时满足**:
|
||||
|
||||
```text
|
||||
certName 匹配目标名称(从业资格证含别名规则)
|
||||
certNo 非空 trim
|
||||
expireDate 非空
|
||||
attachmentUrls 非空 trim(至少一个 URL)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 二、数据模型
|
||||
|
||||
### 2.1 `pl_personnel` 新增字段
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `personnel_type` | varchar(20) | ✅(新数据;迁移见 §2.3) | `driver` / `escort` / `both` |
|
||||
|
||||
DDL 示例:
|
||||
|
||||
```sql
|
||||
ALTER TABLE `pl_personnel`
|
||||
ADD COLUMN `personnel_type` varchar(20) NOT NULL DEFAULT 'both'
|
||||
COMMENT '人员类型 driver-驾驶员 escort-押运员 both-驾驶员兼押运员'
|
||||
AFTER `id_card_no`;
|
||||
```
|
||||
|
||||
同步:`docs/sql/pl_tables.sql`、`docs/sql/shanxia2.0.sql`;增量脚本 `backend/scripts/migration_20260712_pl_personnel_type.sql`。
|
||||
|
||||
### 2.2 字典(可选增强)
|
||||
|
||||
| dict_type | 值 |
|
||||
|-----------|-----|
|
||||
| `pl_personnel_type` | driver-驾驶员, escort-押运员, both-驾驶员兼押运员 |
|
||||
|
||||
列表 Tag、表单下拉可与字典对齐;**必备规则仍硬编码于前后端 util**,不依赖运行时字典配置(YAGNI)。
|
||||
|
||||
### 2.3 历史数据迁移 · 人员类型
|
||||
|
||||
按优先级推断 `personnel_type`:
|
||||
|
||||
1. 若 `pl_vehicle_personnel` 中该人仅有 `role=driver` → `driver`
|
||||
2. 若仅有 `role=escort` → `escort`
|
||||
3. 若同时存在 driver 与 escort 关联 → `both`
|
||||
4. 若无关联记录 → 默认 `both`(必备要求最宽,避免误标 escort 导致缺驾驶证仍可通过)
|
||||
|
||||
迁移后运营需对人员主数据做一次补录/核对(D7 保存阻断会驱动补齐)。
|
||||
|
||||
### 2.4 证书表
|
||||
|
||||
`pl_cert` **无结构变更**。必备/额外均存同一表;必备行通过 `cert_name` +(车辆)`vehicle_part` 识别。
|
||||
|
||||
可选:必备行写入时设 `order_num` 靠前,额外行在后。
|
||||
|
||||
---
|
||||
|
||||
## 三、业务规则
|
||||
|
||||
### 3.1 车辆保存(POST/PUT `/transport/vehicle`)
|
||||
|
||||
```
|
||||
if vehicle_type == 'self':
|
||||
校验主车必备 3 证齐全(证号+有效期+附件)
|
||||
if trailer_no 非空:
|
||||
校验挂车必备 4 证齐全
|
||||
校验关联人员(见 3.2)
|
||||
else:
|
||||
跳过车辆必备校验(仅校验 certName 非空等基础项,若有证书行)
|
||||
```
|
||||
|
||||
「山峡自有车辆信息」页固定 `vehicleType=self`,与「全部车辆信息」中自有行逻辑一致。
|
||||
|
||||
### 3.2 山峡自有车 · 关联人员
|
||||
|
||||
保存时除人员主数据证书外,额外校验:
|
||||
|
||||
| 检查项 | 规则 |
|
||||
|--------|------|
|
||||
| 类型与 role 匹配 | `escort` 类型不可关联为 `driver`;`driver` 类型不可关联为 `escort`;`both` 均可 |
|
||||
| 角色必备证书 | 关联行的 `role=driver` 时,该人员证书须满足 driver 必备;`role=escort` 时满足 escort 必备 |
|
||||
|
||||
说明:人员主数据按 `personnel_type` 校验一次;若 `both` 且仅关联为 escort,主数据仍须驾驶证(因类型为 both)。若未来需放宽,可另开变更。
|
||||
|
||||
### 3.3 人员保存(POST/PUT `/transport/personnel`)
|
||||
|
||||
- `personnel_type` 必填。
|
||||
- 按类型校验必备证书(D5)。
|
||||
- **任意**字段更新(含仅 `remark`)均执行完整必备校验(D7)。
|
||||
|
||||
### 3.4 挂车号动态
|
||||
|
||||
- 编辑中 `trailerNo` 从空变为非空:前端立即展示挂车 4 个必备空槽;保存时校验。
|
||||
- 从非空变为空:挂车必备校验跳过;已有挂车 `vehicle_part=trailer` 证书行保存策略——**保留数据但不阻断**(挂车号清空后不再校验挂车必备;证书行仍随 `certList` 提交,后端按现有 upsert 逻辑处理)。
|
||||
|
||||
---
|
||||
|
||||
## 四、前端设计
|
||||
|
||||
### 4.1 组件策略
|
||||
|
||||
**方案**:扩展或替换 `PlCertFormList` → `PlCertSlotFormList`(推荐新组件,原组件供承运商等非强制场景复用)。
|
||||
|
||||
| 模式 | props | 行为 |
|
||||
|------|-------|------|
|
||||
| `required` | `requiredCertNames`, `vehiclePart?`, `strictFields=true` | 预置必备行、锁定名称、校验三字段 |
|
||||
| `free` | 无 required | 等同现有 `PlCertFormList` |
|
||||
|
||||
`PlVehicleCertPartTabs`:
|
||||
|
||||
- `vehicleType=self` → 各 part 传入对应 `requiredCertNames`
|
||||
- `vehicleType=carrier` → 子组件 `mode=free`
|
||||
|
||||
### 4.2 页面改动
|
||||
|
||||
| 页面 | 变更 |
|
||||
|------|------|
|
||||
| `pl/self-vehicle/index.vue` | 证书区 `required`;保存前 `validateSelfVehicleCerts` |
|
||||
| `pl/vehicle/index.vue` | 依 `formData.vehicleType` 切换 required/free;查看态缺失 suffix 逻辑保持 |
|
||||
| `pl/personnel/index.vue` | 表单加「人员类型」;证书区按类型 `required`;列表加类型列 Tag |
|
||||
|
||||
### 4.3 表单 · 人员类型
|
||||
|
||||
```text
|
||||
人员类型* [ el-select ]
|
||||
- 驾驶员 (driver)
|
||||
- 押运员 (escort)
|
||||
- 驾驶员兼押运员 (both)
|
||||
```
|
||||
|
||||
- 新增默认:`both` 或空(须必选,无默认值更安全 → **无默认,必选**)。
|
||||
- 修改类型:重新计算必备槽位;已有同名校验数据保留,新增必备行补空槽。
|
||||
|
||||
### 4.4 UI 分区
|
||||
|
||||
```
|
||||
┌─ 人员证书 ──────────────────────────────────────┐
|
||||
│ ▎必备证书 │
|
||||
│ 身份证 证号* 有效期* 附件* 🔒 │
|
||||
│ 驾驶证 ... │
|
||||
│ ▎其他证书 │
|
||||
│ [+ 添加其他证书] │
|
||||
└─────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
必备区与额外区可用分组标题或表格分段区分;必备行删除按钮隐藏。
|
||||
|
||||
### 4.5 校验与错误提示
|
||||
|
||||
- 保存失败:`ElMessage.warning` + 可选 `ElMessageBox.alert` 列出缺失项。
|
||||
- 文案示例:`主车「行驶证」缺少:证号、附件`。
|
||||
- 长表单:`scroll-to-error` 滚动至证书区首个缺失槽。
|
||||
|
||||
---
|
||||
|
||||
## 五、后端设计
|
||||
|
||||
### 5.1 新增服务 · `pl_cert_required_service.py`(或并入 `pl_cert_service.py`)
|
||||
|
||||
```python
|
||||
def get_required_vehicle_cert_names(vehicle_type, trailer_no, part) -> list[str]
|
||||
def get_required_personnel_cert_names(personnel_type) -> list[str]
|
||||
def validate_required_certs(cert_rows, required_names, *, part_label=None) -> None # raises HTTPException 400
|
||||
def validate_personnel_type_role_compat(personnel_type, role) -> None
|
||||
```
|
||||
|
||||
### 5.2 API 接入点
|
||||
|
||||
| 路由 | 校验时机 |
|
||||
|------|---------|
|
||||
| `POST/PUT .../vehicle` | `vehicle_type=self` 时校验 cert_list + 关联人员 |
|
||||
| `POST/PUT .../personnel` | 始终校验 personnel_type + cert_list |
|
||||
|
||||
错误响应示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": "必备证书未齐全:主车「保险单」缺少有效期、附件;人员「张三」类型为驾驶员,缺少驾驶证"
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 Schema
|
||||
|
||||
- `PersonnelBase` / `PersonnelCreate` / `PersonnelUpdate` / `PersonnelRead` 增加 `personnel_type`(alias `personnelType`)。
|
||||
- `VehicleCreate/Update` 无需新字段;沿用 `vehicle_type`。
|
||||
|
||||
---
|
||||
|
||||
## 六、OpenSpec / 工程规范
|
||||
|
||||
建议在 `openspec/changes/transport-plan-management/` 或新 change 中增补 Scenario:
|
||||
|
||||
- 山峡自有车保存 SHALL 阻断缺失必备车辆证。
|
||||
- 承运商车辆保存 SHALL NOT 因缺失必备车辆证阻断。
|
||||
- 人员保存 SHALL 按 personnel_type 阻断缺失必备人员证。
|
||||
|
||||
交叉核对:`openspec/changes/zkxw-engineering-standards/specs/ui-conventions/spec.md`(Tag 展示人员类型、按钮图标等)。
|
||||
|
||||
---
|
||||
|
||||
## 七、验收标准
|
||||
|
||||
1. 山峡自有车新增:打开弹窗即见主车 3 个必备空槽;填挂车号后出现挂车 4 槽。
|
||||
2. 必备槽位:名称不可编辑、不可删除;证号/有效期/附件任一缺失则无法保存。
|
||||
3. 可添加「道路运输证」等额外证书并正常保存。
|
||||
4. 承运商车辆在「全部车辆信息」中无必备槽位(或有不阻断),缺证仍可保存。
|
||||
5. 人员页必选类型;三类人员必备槽位正确;改类型后槽位同步。
|
||||
6. 历史自有车/人员:仅改备注时若缺必备证,保存返回明确错误。
|
||||
7. 山峡自有车:驾驶员类型不可选为押运员 role(及反向);both 均可。
|
||||
8. 后端 duplicate 校验:直接调 API 缺必备证返回 400。
|
||||
9. `python scripts/check_field_consistency.py` 通过(personnel_type 字段)。
|
||||
10. 迁移脚本可重复执行;现有 `pl_cert` 数据不丢失。
|
||||
|
||||
---
|
||||
|
||||
## 八、风险与缓解
|
||||
|
||||
| 风险 | 缓解 |
|
||||
|------|------|
|
||||
| 历史大量缺证导致无法保存 | D7 为业务明确要求;上线前导出缺证清单,运营批量补录 |
|
||||
| 人员类型迁移不准 | 迁移规则 §2.3 + 列表展示类型供人工修正 |
|
||||
| 组件复杂度上升 | 新组件 `PlCertSlotFormList`,承运商继续用旧组件 |
|
||||
| 车辆页切换类型 self↔carrier | 切换为 carrier 时隐藏必备槽位且不校验;切回 self 时重新注入必备槽并校验 |
|
||||
| both 类型关联 escort 仍要驾驶证 | 设计已说明;若业务异议可后续改为「按 role 动态必备」 |
|
||||
|
||||
---
|
||||
|
||||
## 九、工作量估算
|
||||
|
||||
| 模块 | 预估 |
|
||||
|------|------|
|
||||
| DB + 迁移 + Schema | 0.5 天 |
|
||||
| 后端必备校验服务 + API | 0.5 天 |
|
||||
| `PlCertSlotFormList` + 车辆 tabs 接入 | 1 天 |
|
||||
| 人员页类型字段 + 证书接入 | 0.5 天 |
|
||||
| 三页联调 + 历史场景自测 | 0.5~1 天 |
|
||||
| **合计** | **约 3~3.5 人天** |
|
||||
|
||||
---
|
||||
|
||||
## 十、实施任务清单(概要)
|
||||
|
||||
1. SQL:`personnel_type` 列 + 迁移推断脚本
|
||||
2. Backend:Model / Schema / `_row_to_dict` / 校验服务 / vehicle & personnel API
|
||||
3. Frontend utils:统一 `validateRequiredCertSlots`、`buildRequiredCertRows`
|
||||
4. Component:`PlCertSlotFormList`
|
||||
5. Pages:`self-vehicle`、`vehicle`、`personnel`
|
||||
6. 查看态:人员类型 Tag;缺失 suffix 与必备校验口径一致
|
||||
7. 自测 + `check_field_consistency.py`
|
||||
8. OpenSpec scenario 增补(可选同 PR)
|
||||
|
||||
---
|
||||
|
||||
## 附录:与现有常数对照
|
||||
|
||||
需求与 `pl-vehicle-cert.ts` / `pl-personnel-cert.ts` **完全一致**,无需调整必备名称列表,仅需 **启用** 校验并改造 UI 为固定槽位。
|
||||
+77
-57
@@ -210,8 +210,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py": {
|
||||
"mtime": 1783777743.341288,
|
||||
"ast_hash": "c922ece8f7295870b9b64f61ee6ddae9",
|
||||
"mtime": 1783845022.041298,
|
||||
"ast_hash": "1e988545bba72069f18f5deaf74915e7",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/backend/app/models/models.py": {
|
||||
@@ -320,8 +320,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py": {
|
||||
"mtime": 1783176696.4542158,
|
||||
"ast_hash": "6ef5cf534eb50f012ed9bda34df152d9",
|
||||
"mtime": 1783845022.3061872,
|
||||
"ast_hash": "378d20f7fbaca5f1c88ac1d322535994",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/vehicle_urea.py": {
|
||||
@@ -445,7 +445,7 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/crm/contract.py": {
|
||||
"mtime": 1783823649.766528,
|
||||
"mtime": 1783845455.3648205,
|
||||
"ast_hash": "6553f54ca1fd19086c170095f402a628",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
@@ -750,8 +750,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/crm_contract_routes.py": {
|
||||
"mtime": 1783763319.0708206,
|
||||
"ast_hash": "44223920a99b6b0796a49232e8c93a8e",
|
||||
"mtime": 1783825526.6141698,
|
||||
"ast_hash": "46a845a2d7ce98f2486ad45bde2d9612",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/workflow.py": {
|
||||
@@ -765,8 +765,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/plan.py": {
|
||||
"mtime": 1783760540.4064815,
|
||||
"ast_hash": "e5c5c37075fe5e8adb90b6f1859071b7",
|
||||
"mtime": 1783845558.7014904,
|
||||
"ast_hash": "853850cff4f1ebd6ff197681e21e81f6",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/transport.py": {
|
||||
@@ -800,8 +800,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/base_data.py": {
|
||||
"mtime": 1782831664.104135,
|
||||
"ast_hash": "705af7431e010950bd742926a8767600",
|
||||
"mtime": 1783845063.8368685,
|
||||
"ast_hash": "8529164d201cbaed199ea77d3a33fb71",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/vehicle_urea.py": {
|
||||
@@ -965,8 +965,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_personnel_service.py": {
|
||||
"mtime": 1783239403.8478649,
|
||||
"ast_hash": "4d0591f2ccd5eed7b4949a11f838d95c",
|
||||
"mtime": 1783845044.1152287,
|
||||
"ast_hash": "94f2517f259023b46dfef7f0f74fede3",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/backend/app/services/crm_quote_pdf.py": {
|
||||
@@ -2530,8 +2530,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/utils/pl-personnel-cert.ts": {
|
||||
"mtime": 1783142758.6120825,
|
||||
"ast_hash": "103281f7dcc60fb29211c534ed6e6744",
|
||||
"mtime": 1783845064.4713657,
|
||||
"ast_hash": "0e5d989ac54b364a5983a5f9af9fd6d6",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/utils/patch-el-select-filterable.ts": {
|
||||
@@ -2590,8 +2590,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/utils/pl-display.ts": {
|
||||
"mtime": 1783242794.3651183,
|
||||
"ast_hash": "02e465faedad1f9a9ad7b42bdb4a60cd",
|
||||
"mtime": 1783846376.254574,
|
||||
"ast_hash": "e0e2f44cd091369aed67791e684b3c98",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/utils/view-dialog-refresh.ts": {
|
||||
@@ -2685,8 +2685,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/utils/pl-vehicle-cert.ts": {
|
||||
"mtime": 1783142762.1595736,
|
||||
"ast_hash": "63cdbcaab03c15b70d8045462e56c93d",
|
||||
"mtime": 1783844738.0471256,
|
||||
"ast_hash": "873ca5c7a0f7afbfa44925292f3f68ff",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/utils/protocol-payment-installment.ts": {
|
||||
@@ -3575,8 +3575,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/business/pl-vehicle-cert-personnel-tabs/index.vue": {
|
||||
"mtime": 1783239419.8721654,
|
||||
"ast_hash": "975163ea136bc1a966b166dc399f16b5",
|
||||
"mtime": 1783844806.526282,
|
||||
"ast_hash": "4661518a5a0ae9a2ad4384cfbeee5a42",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/business/pl-plan-list-cert-cell/index.vue": {
|
||||
@@ -3585,8 +3585,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/business/pl-cert-form-list/index.vue": {
|
||||
"mtime": 1783204483.7404983,
|
||||
"ast_hash": "ba0c4c8ae84a295116d6b6d48ed853cc",
|
||||
"mtime": 1783844774.5544193,
|
||||
"ast_hash": "675188fc7671fde3487a942e3b9af0d3",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/business/product-view-dialog/index.vue": {
|
||||
@@ -3625,8 +3625,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/business/pl-vehicle-cert-part-tabs/index.vue": {
|
||||
"mtime": 1783203118.0167286,
|
||||
"ast_hash": "ae5b4d29397ae52e65acd8308084ea92",
|
||||
"mtime": 1783844759.0212061,
|
||||
"ast_hash": "a8237cabea3fef3788d758e43fb360ec",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/business/pl-product-name-hazard-cell/index.vue": {
|
||||
@@ -3640,8 +3640,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/business/pl-plan-archive-dialog/index.vue": {
|
||||
"mtime": 1783754641.7341132,
|
||||
"ast_hash": "4ffb80920f360ffd3be7c5031656e6b7",
|
||||
"mtime": 1783846283.221872,
|
||||
"ast_hash": "a81c97a14c6f9aad011061c50194b296",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/business/pl-plan-view-tabs/index.vue": {
|
||||
@@ -3715,8 +3715,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/business/pl-vehicle-cert-part-view/index.vue": {
|
||||
"mtime": 1783176286.6399972,
|
||||
"ast_hash": "82094acfee492822cf747e537ef0e586",
|
||||
"mtime": 1783844812.829093,
|
||||
"ast_hash": "4cb51d89cbfacbc635f405599710ffa2",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/business/pl-plan-arrange-dialog/index.vue": {
|
||||
@@ -3735,8 +3735,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/business/pl-vehicle-view-dialog/index.vue": {
|
||||
"mtime": 1783239462.7506347,
|
||||
"ast_hash": "f4ba884a00eaba522145a7595a8568dc",
|
||||
"mtime": 1783844817.3452597,
|
||||
"ast_hash": "379405598f0f3c19e06d65bc1cbaed6b",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/business/pl-inbound-slip-print-dialog/index.vue": {
|
||||
@@ -3765,13 +3765,13 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/business/system-view-dialog/ViewKvTableCell.vue": {
|
||||
"mtime": 1783203672.1663585,
|
||||
"ast_hash": "40d984c0fe2ba989f54a3dd8a1b62ee0",
|
||||
"mtime": 1783826496.3181424,
|
||||
"ast_hash": "81ea08367bbe5878972f438eb3ec4b77",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/business/system-view-dialog/types.ts": {
|
||||
"mtime": 1783203671.4708154,
|
||||
"ast_hash": "6ebd9545f517277abc2e913e6b986a6c",
|
||||
"mtime": 1783826501.0256994,
|
||||
"ast_hash": "f9a7e100b725653c28513dd7e325c301",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/business/system-view-dialog/ViewHighlights.vue": {
|
||||
@@ -3800,8 +3800,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/business/pl-vehicle-personnel-view-list/PersonnelViewPanel.vue": {
|
||||
"mtime": 1783753612.7487419,
|
||||
"ast_hash": "eca43ee259a74b2b83da5f037efe6c3d",
|
||||
"mtime": 1783844813.70631,
|
||||
"ast_hash": "7a27cdd0fc0db3ab1de01a16174729e9",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/ListPagination/index.vue": {
|
||||
@@ -3980,8 +3980,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/api/pl/index.ts": {
|
||||
"mtime": 1783754611.920795,
|
||||
"ast_hash": "8ca7f86a901ee6834fd39adeb9bcc473",
|
||||
"mtime": 1783845066.0273972,
|
||||
"ast_hash": "fa2ffcb5318c1ed1a6a06855422880d9",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/api/pl/public-transport.ts": {
|
||||
@@ -4165,8 +4165,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/pl/vehicle/index.vue": {
|
||||
"mtime": 1783252180.3044844,
|
||||
"ast_hash": "1c9691b756903ec22f8b302fc81bbe5d",
|
||||
"mtime": 1783845088.2792692,
|
||||
"ast_hash": "23f9cedc7e3b87b4d670e429cf736a55",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/pl/vehicle-recharge-record/index.vue": {
|
||||
@@ -4180,8 +4180,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/pl/plan/index.vue": {
|
||||
"mtime": 1783750515.7251935,
|
||||
"ast_hash": "503f7ef7167e32d3bb0ebc78489bf973",
|
||||
"mtime": 1783841453.747368,
|
||||
"ast_hash": "9ae88b869dc2c937b027f7fdc6ac60aa",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/pl/plan/arrange.vue": {
|
||||
@@ -4285,23 +4285,23 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/pl/plan-archive/index.vue": {
|
||||
"mtime": 1783750518.4763997,
|
||||
"ast_hash": "ba57b39a05f387fabae440c908c46985",
|
||||
"mtime": 1783846246.026981,
|
||||
"ast_hash": "8e2207eb52be4b0dca7811323f61aabf",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/pl/self-vehicle/index.vue": {
|
||||
"mtime": 1783252181.1993465,
|
||||
"ast_hash": "ccda242ae74e22346703bcefaf58f903",
|
||||
"mtime": 1783845096.14697,
|
||||
"ast_hash": "25357bf2f887716989f8197285b5964e",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/pl/carrier/index.vue": {
|
||||
"mtime": 1783821190.6414018,
|
||||
"ast_hash": "047b4cc96dc747d17f802d892ba4f424",
|
||||
"mtime": 1783841744.362471,
|
||||
"ast_hash": "1fcfd92cfd0d2b344d2110d7164b4b2a",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/pl/personnel/index.vue": {
|
||||
"mtime": 1783203297.0313385,
|
||||
"ast_hash": "2ccaa7d6c84a1d2b660b75412ef816a3",
|
||||
"mtime": 1783845137.1568706,
|
||||
"ast_hash": "f12fbbc1e2a8e2e7cc1d3c6df3ceff6f",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/pl/vehicle-maintenance-process/index.vue": {
|
||||
@@ -4485,8 +4485,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/crm/contract/index.vue": {
|
||||
"mtime": 1783749577.7739928,
|
||||
"ast_hash": "ce91084329a7737179e85701ec10f737",
|
||||
"mtime": 1783826680.883232,
|
||||
"ast_hash": "d072970a9ba6eb25f2a738cb3f5f653b",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/crm/contract/detail.vue": {
|
||||
@@ -4520,8 +4520,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/crm/contract/components/ContractDetailDialog.vue": {
|
||||
"mtime": 1783753625.445439,
|
||||
"ast_hash": "ea172441d0463ff69fcbb57cc998a1e5",
|
||||
"mtime": 1783826496.7785032,
|
||||
"ast_hash": "24d49c1edca30ad1243380953b540b2f",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/crm/contract/components/ContractAttachmentPanel.vue": {
|
||||
@@ -4540,8 +4540,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/crm/contract/components/ApprovalDialog.vue": {
|
||||
"mtime": 1783753615.3856215,
|
||||
"ast_hash": "0722dd2ab21a746ddd7e2ecdbc5d0e4f",
|
||||
"mtime": 1783826784.9388635,
|
||||
"ast_hash": "c2a695de1bbd282d616715c5bde1d414",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/crm/contract/components/RenewDialog.vue": {
|
||||
@@ -7398,5 +7398,25 @@
|
||||
"mtime": 1783777759.8294604,
|
||||
"ast_hash": "73869416708f10036beec5769af334bb",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_cert_required_service.py": {
|
||||
"mtime": 1783845035.534594,
|
||||
"ast_hash": "279f299a4969244bad7f866ae9ee97fb",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/backend/scripts/migration_20260712_pl_personnel_type.sql": {
|
||||
"mtime": 1783843576.7474706,
|
||||
"ast_hash": "82dcd1f60a2b486cc94f34bfb2228bd6",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/utils/pl-cert-slots.ts": {
|
||||
"mtime": 1783844749.9400146,
|
||||
"ast_hash": "33ea084eaaba3ec98c170d8651ca83f9",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/backend/scripts/migration_20260712_pl_personnel_affiliation.sql": {
|
||||
"mtime": 1783845019.9129975,
|
||||
"ast_hash": "f97c57b5fac34dc5d47491e9de4c1769",
|
||||
"semantic_hash": ""
|
||||
}
|
||||
}
|
||||
@@ -267,14 +267,12 @@
|
||||
"265": "Community 265",
|
||||
"266": "Community 266",
|
||||
"267": "Community 267",
|
||||
"268": "Community 268",
|
||||
"269": "Community 269",
|
||||
"270": "Community 270",
|
||||
"271": "Community 271",
|
||||
"272": "Community 272",
|
||||
"273": "Community 273",
|
||||
"274": "Community 274",
|
||||
"275": "Community 275",
|
||||
"276": "Community 276",
|
||||
"277": "Community 277",
|
||||
"278": "Community 278",
|
||||
@@ -618,7 +616,6 @@
|
||||
"616": "Community 616",
|
||||
"617": "Community 617",
|
||||
"618": "Community 618",
|
||||
"619": "Community 619",
|
||||
"620": "Community 620",
|
||||
"621": "Community 621",
|
||||
"622": "Community 622",
|
||||
@@ -682,13 +679,13 @@
|
||||
"680": "Community 680",
|
||||
"681": "Community 681",
|
||||
"682": "Community 682",
|
||||
"683": "Community 683",
|
||||
"684": "Community 684",
|
||||
"685": "Community 685",
|
||||
"686": "Community 686",
|
||||
"687": "Community 687",
|
||||
"688": "Community 688",
|
||||
"689": "Community 689",
|
||||
"690": "Community 690",
|
||||
"691": "Community 691",
|
||||
"692": "Community 692",
|
||||
"693": "Community 693",
|
||||
@@ -713,28 +710,25 @@
|
||||
"713": "Community 713",
|
||||
"714": "Community 714",
|
||||
"715": "Community 715",
|
||||
"716": "Community 716",
|
||||
"717": "Community 717",
|
||||
"718": "Community 718",
|
||||
"719": "Community 719",
|
||||
"720": "Community 720",
|
||||
"721": "Community 721",
|
||||
"722": "Community 722",
|
||||
"723": "Community 723",
|
||||
"724": "Community 724",
|
||||
"725": "Community 725",
|
||||
"726": "Community 726",
|
||||
"727": "Community 727",
|
||||
"728": "Community 728",
|
||||
"729": "Community 729",
|
||||
"730": "Community 730",
|
||||
"731": "Community 731",
|
||||
"732": "Community 732",
|
||||
"733": "Community 733",
|
||||
"734": "Community 734",
|
||||
"735": "Community 735",
|
||||
"736": "Community 736",
|
||||
"737": "Community 737",
|
||||
"738": "Community 738",
|
||||
"739": "Community 739",
|
||||
"740": "Community 740",
|
||||
"741": "Community 741",
|
||||
"743": "Community 743"
|
||||
"742": "Community 742"
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
+4754
-3370
File diff suppressed because it is too large.
Load diff
@@ -147,6 +147,8 @@ export interface Personnel {
|
||||
phoneNo?: string
|
||||
age?: number
|
||||
idCardNo?: string
|
||||
personnelType: string
|
||||
personnelAffiliation: string
|
||||
status: string
|
||||
remark?: string
|
||||
nearestExpireDate?: string
|
||||
@@ -162,6 +164,8 @@ export interface PersonnelPayload {
|
||||
phoneNo?: string
|
||||
age?: number | null
|
||||
idCardNo?: string
|
||||
personnelType: string
|
||||
personnelAffiliation: string
|
||||
status: string
|
||||
remark?: string
|
||||
certList?: CertRow[]
|
||||
@@ -170,6 +174,7 @@ export interface PersonnelPayload {
|
||||
export interface PersonnelQuery {
|
||||
q?: string
|
||||
status?: string
|
||||
personnelAffiliation?: string
|
||||
page?: number
|
||||
pageSize?: number
|
||||
}
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
<div class="cert-form-header">
|
||||
<span class="cert-form-title">{{ title }}</span>
|
||||
<el-button type="primary" size="small" @click="handleAddCert">
|
||||
<el-icon><Plus /></el-icon>添加证书
|
||||
<el-icon><Plus /></el-icon>{{ addButtonLabel }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<div v-if="!certList.length" class="cert-empty">暂无证书,点击「添加证书」</div>
|
||||
<div v-if="!certList.length" class="cert-empty">{{ emptyHint }}</div>
|
||||
|
||||
<el-table v-else-if="layout === 'table'" :data="certList" stripe class="list-table cert-form-table">
|
||||
<el-table-column type="index" label="序号" min-width="68" align="center" header-align="center" />
|
||||
@@ -20,7 +20,7 @@
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-select
|
||||
v-if="hasTrailerPlateOption"
|
||||
v-if="hasTrailerPlateOption && !isRowRequired(row)"
|
||||
:model-value="normalizeVehicleCertPart(row.vehiclePart)"
|
||||
placeholder="请选择车牌号"
|
||||
style="width: 100%"
|
||||
@@ -33,12 +33,17 @@
|
||||
:value="option.value"
|
||||
/>
|
||||
</el-select>
|
||||
<span v-else>{{ headPlateLabel }}</span>
|
||||
<span v-else>{{ resolvePlateLabel(row) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="证书名称" min-width="160">
|
||||
<template #default="{ row }">
|
||||
<span v-if="isRowRequired(row)" class="cert-name-locked">
|
||||
{{ row.certName }}
|
||||
<el-tag type="info" size="small">必备</el-tag>
|
||||
</span>
|
||||
<el-select
|
||||
v-else
|
||||
v-model="row.certName"
|
||||
placeholder="请选择或输入"
|
||||
filterable
|
||||
@@ -87,8 +92,8 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" min-width="60" align="center" header-align="center">
|
||||
<template #default="{ $index }">
|
||||
<el-button link type="danger" @click="handleRemoveCert($index)">
|
||||
<template #default="{ row, $index }">
|
||||
<el-button v-if="!isRowRequired(row)" link type="danger" @click="handleRemoveCert($index)">
|
||||
<el-icon><Delete /></el-icon>
|
||||
</el-button>
|
||||
</template>
|
||||
@@ -98,15 +103,19 @@
|
||||
<div v-else class="cert-card-list">
|
||||
<div v-for="(cert, index) in certList" :key="cert._key" class="cert-card">
|
||||
<div class="cert-card-head">
|
||||
<span class="cert-card-index">证书 {{ index + 1 }}</span>
|
||||
<el-button link type="danger" @click="handleRemoveCert(index)">
|
||||
<span class="cert-card-index">
|
||||
{{ isRowRequired(cert) ? `必备 · ${cert.certName}` : `证书 ${index + 1}` }}
|
||||
</span>
|
||||
<el-button v-if="!isRowRequired(cert)" link type="danger" @click="handleRemoveCert(index)">
|
||||
<el-icon><Delete /></el-icon>删除
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<div class="form-grid-4 cert-card-grid">
|
||||
<el-form-item label="证书名称" required>
|
||||
<span v-if="isRowRequired(cert)" class="cert-name-locked">{{ cert.certName }}</span>
|
||||
<el-select
|
||||
v-else
|
||||
v-model="cert.certName"
|
||||
placeholder="请选择或输入证书名称"
|
||||
filterable
|
||||
@@ -155,12 +164,23 @@ import { Delete, Plus } from '@element-plus/icons-vue'
|
||||
import AttachmentUploadField from '@/components/business/attachment-upload-field/index.vue'
|
||||
import type { CertRow } from '@/api/pl'
|
||||
import {
|
||||
ensureCarrierVehicleCertSlots,
|
||||
ensureSelfVehicleCertSlots,
|
||||
mergeCertListWithRequiredSlots,
|
||||
type CertRowWithMeta
|
||||
} from '@/utils/pl-cert-slots'
|
||||
import { matchRequiredCertName } from '@/utils/pl-personnel-cert'
|
||||
import {
|
||||
PL_CARRIER_VEHICLE_HEAD_REQUIRED_CERT_NAMES,
|
||||
PL_CARRIER_VEHICLE_TRAILER_REQUIRED_CERT_NAMES,
|
||||
PL_SELF_VEHICLE_HEAD_REQUIRED_CERT_NAMES,
|
||||
PL_SELF_VEHICLE_TRAILER_REQUIRED_CERT_NAMES,
|
||||
PL_VEHICLE_CERT_PART_HEAD,
|
||||
PL_VEHICLE_CERT_PART_TRAILER,
|
||||
normalizeVehicleCertPart
|
||||
} from '@/utils/pl-vehicle-cert'
|
||||
|
||||
type CertRowWithKey = CertRow & { _key: string }
|
||||
type CertRowWithKey = CertRowWithMeta & { _key: string }
|
||||
type CertUploadRef = InstanceType<typeof AttachmentUploadField>
|
||||
|
||||
const props = withDefaults(
|
||||
@@ -169,10 +189,16 @@ const props = withDefaults(
|
||||
uploadFolder: string
|
||||
certNameOptions?: string[]
|
||||
layout?: 'card' | 'table'
|
||||
/** 列表模式:展示车牌号列,可从主车/挂车车牌中选择 */
|
||||
showVehiclePartColumn?: boolean
|
||||
licensePlate?: string
|
||||
trailerNo?: string
|
||||
/** 人员/单段车辆:固定必备证书名称 */
|
||||
requiredCertNames?: readonly string[]
|
||||
requiredVehiclePart?: string
|
||||
/** 山峡自有车统一表格:按主车/挂车自动注入必备槽位 */
|
||||
enforceSelfVehicleRequired?: boolean
|
||||
/** 承运商车辆:按主车/挂车注入必备槽位(行驶证、营运证) */
|
||||
enforceCarrierVehicleRequired?: boolean
|
||||
}>(),
|
||||
{
|
||||
title: '证书',
|
||||
@@ -180,10 +206,27 @@ const props = withDefaults(
|
||||
layout: 'card',
|
||||
showVehiclePartColumn: false,
|
||||
licensePlate: '',
|
||||
trailerNo: ''
|
||||
trailerNo: '',
|
||||
requiredCertNames: () => [],
|
||||
requiredVehiclePart: PL_VEHICLE_CERT_PART_HEAD,
|
||||
enforceSelfVehicleRequired: false,
|
||||
enforceCarrierVehicleRequired: false
|
||||
}
|
||||
)
|
||||
|
||||
const hasRequiredMode = computed(
|
||||
() =>
|
||||
props.enforceSelfVehicleRequired ||
|
||||
props.enforceCarrierVehicleRequired ||
|
||||
(props.requiredCertNames?.length ?? 0) > 0
|
||||
)
|
||||
|
||||
const addButtonLabel = computed(() => (hasRequiredMode.value ? '添加其他证书' : '添加证书'))
|
||||
|
||||
const emptyHint = computed(() =>
|
||||
hasRequiredMode.value ? '暂无证书' : '暂无证书,点击「添加证书」'
|
||||
)
|
||||
|
||||
const headPlateLabel = computed(() => props.licensePlate?.trim() || '—')
|
||||
const hasTrailerPlateOption = computed(() => Boolean(props.trailerNo?.trim()))
|
||||
|
||||
@@ -203,23 +246,88 @@ const vehiclePlateOptions = computed(() => {
|
||||
return options
|
||||
})
|
||||
|
||||
function resolvePlateLabel(row: CertRowWithKey) {
|
||||
const part = normalizeVehicleCertPart(row.vehiclePart)
|
||||
if (part === PL_VEHICLE_CERT_PART_TRAILER) {
|
||||
return props.trailerNo?.trim() || '挂车'
|
||||
}
|
||||
return headPlateLabel.value
|
||||
}
|
||||
|
||||
function requiredNamesForRow(row: CertRowWithKey): readonly string[] {
|
||||
if (props.requiredCertNames?.length) {
|
||||
return props.requiredCertNames
|
||||
}
|
||||
if (!props.enforceSelfVehicleRequired && !props.enforceCarrierVehicleRequired) return []
|
||||
const part = normalizeVehicleCertPart(row.vehiclePart)
|
||||
if (props.enforceCarrierVehicleRequired) {
|
||||
return part === PL_VEHICLE_CERT_PART_TRAILER
|
||||
? PL_CARRIER_VEHICLE_TRAILER_REQUIRED_CERT_NAMES
|
||||
: PL_CARRIER_VEHICLE_HEAD_REQUIRED_CERT_NAMES
|
||||
}
|
||||
return part === PL_VEHICLE_CERT_PART_TRAILER
|
||||
? PL_SELF_VEHICLE_TRAILER_REQUIRED_CERT_NAMES
|
||||
: PL_SELF_VEHICLE_HEAD_REQUIRED_CERT_NAMES
|
||||
}
|
||||
|
||||
function isRowRequired(row: CertRowWithKey) {
|
||||
if (row._required) return true
|
||||
const requiredNames = requiredNamesForRow(row)
|
||||
if (!requiredNames.length) return false
|
||||
return requiredNames.some(name => matchRequiredCertName(row.certName, name))
|
||||
}
|
||||
|
||||
function syncRequiredSlots() {
|
||||
if (props.enforceSelfVehicleRequired) {
|
||||
certList.value = ensureSelfVehicleCertSlots(certList.value, props.trailerNo)
|
||||
return
|
||||
}
|
||||
if (props.enforceCarrierVehicleRequired) {
|
||||
certList.value = ensureCarrierVehicleCertSlots(certList.value, props.trailerNo)
|
||||
return
|
||||
}
|
||||
if (props.requiredCertNames?.length) {
|
||||
certList.value = mergeCertListWithRequiredSlots(certList.value, props.requiredCertNames, {
|
||||
vehiclePart: props.requiredVehiclePart
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function handleVehiclePartChange(row: CertRowWithKey, value: string) {
|
||||
if (isRowRequired(row)) return
|
||||
row.vehiclePart = normalizeVehicleCertPart(value)
|
||||
}
|
||||
|
||||
const certList = defineModel<CertRowWithKey[]>('modelValue', { default: () => [] })
|
||||
|
||||
watch(
|
||||
() =>
|
||||
[
|
||||
props.enforceSelfVehicleRequired,
|
||||
props.enforceCarrierVehicleRequired,
|
||||
props.requiredCertNames,
|
||||
props.trailerNo
|
||||
] as const,
|
||||
() => {
|
||||
if (hasRequiredMode.value) {
|
||||
syncRequiredSlots()
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
const certUploadRefs = ref<Record<number, CertUploadRef | null>>({})
|
||||
|
||||
function createEmptyCert(): CertRowWithKey {
|
||||
return {
|
||||
_key: `cert-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
||||
vehiclePart: PL_VEHICLE_CERT_PART_HEAD,
|
||||
vehiclePart: props.requiredVehiclePart || PL_VEHICLE_CERT_PART_HEAD,
|
||||
certName: '',
|
||||
certNo: '',
|
||||
expireDate: '',
|
||||
remindDays: 30,
|
||||
attachmentUrls: ''
|
||||
attachmentUrls: '',
|
||||
_required: false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,6 +336,8 @@ function handleAddCert() {
|
||||
}
|
||||
|
||||
function handleRemoveCert(index: number) {
|
||||
const row = certList.value[index]
|
||||
if (row && isRowRequired(row)) return
|
||||
const next = [...certList.value]
|
||||
next.splice(index, 1)
|
||||
certList.value = next
|
||||
@@ -250,7 +360,11 @@ function clearUploadRefs() {
|
||||
certUploadRefs.value = {}
|
||||
}
|
||||
|
||||
defineExpose({ hasUploading, clearUploadRefs })
|
||||
function refreshRequiredSlots() {
|
||||
syncRequiredSlots()
|
||||
}
|
||||
|
||||
defineExpose({ hasUploading, clearUploadRefs, refreshRequiredSlots })
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@@ -282,6 +396,14 @@ defineExpose({ hasUploading, clearUploadRefs })
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.cert-name-locked {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-weight: 500;
|
||||
color: var(--text, #1e293b);
|
||||
}
|
||||
|
||||
.cert-form-table {
|
||||
border-top: 1px solid var(--mint-border, #d9e8e2);
|
||||
|
||||
|
||||
@@ -26,61 +26,67 @@
|
||||
</section>
|
||||
|
||||
<section class="pl-plan-form-section pl-plan-freight-section">
|
||||
<h4 class="pl-plan-subtitle">运费信息</h4>
|
||||
<div class="pl-plan-freight-section-head">
|
||||
<h4 class="pl-plan-subtitle">运费信息</h4>
|
||||
<label class="pl-plan-freight-switch">
|
||||
<span class="pl-plan-freight-switch-label">是否有运输费用</span>
|
||||
<el-switch v-model="formData.hasFreight" :active-value="1" :inactive-value="0" />
|
||||
</label>
|
||||
</div>
|
||||
<el-form
|
||||
v-if="formData.hasFreight === 1"
|
||||
:model="formData"
|
||||
label-width="140px"
|
||||
label-position="right"
|
||||
class="pl-plan-freight-form"
|
||||
>
|
||||
<div class="form-grid-4">
|
||||
<el-form-item label="是否有运输费用">
|
||||
<el-switch v-model="formData.hasFreight" :active-value="1" :inactive-value="0" />
|
||||
<div class="form-grid-4 pl-plan-archive-freight-grid">
|
||||
<el-form-item label="运费计算方式">
|
||||
<el-select v-model="formData.freightType">
|
||||
<el-option label="按重量" value="by_weight" />
|
||||
<el-option label="按趟" value="by_trip" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="运输公里数">
|
||||
<el-input-number
|
||||
v-model="formData.distanceKm"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="formData.freightType === 'by_weight'"
|
||||
:label="`运输量(${freightUnitLabel})`"
|
||||
>
|
||||
<el-input-number
|
||||
v-model="freightCalc.transportQty.value"
|
||||
:min="0"
|
||||
:precision="3"
|
||||
@change="freightCalc.markTransportQtyTouched()"
|
||||
/>
|
||||
</el-form-item>
|
||||
<div v-else class="pl-plan-archive-freight-grid-cell" aria-hidden="true" />
|
||||
|
||||
<div class="pl-plan-archive-freight-grid-cell" aria-hidden="true" />
|
||||
<el-form-item
|
||||
v-if="formData.freightType === 'by_weight'"
|
||||
:label="`运输单价(元/${freightUnitLabel})`"
|
||||
>
|
||||
<el-input-number
|
||||
v-model="freightCalc.freightPrice.value"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
/>
|
||||
</el-form-item>
|
||||
<div v-else class="pl-plan-archive-freight-grid-cell" aria-hidden="true" />
|
||||
<el-form-item label="运费金额(元)">
|
||||
<el-input-number
|
||||
v-model="freightCalc.freightAmount.value"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
@change="freightCalc.markFreightAmountTouched()"
|
||||
/>
|
||||
</el-form-item>
|
||||
<template v-if="formData.hasFreight === 1">
|
||||
<el-form-item label="运输公里数">
|
||||
<el-input-number
|
||||
v-model="formData.distanceKm"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="运费计算方式">
|
||||
<el-select v-model="formData.freightType">
|
||||
<el-option label="按重量" value="by_weight" />
|
||||
<el-option label="按趟" value="by_trip" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="formData.freightType === 'by_weight'"
|
||||
:label="`运输量(${freightUnitLabel})`"
|
||||
>
|
||||
<el-input-number
|
||||
v-model="freightCalc.transportQty.value"
|
||||
:min="0"
|
||||
:precision="3"
|
||||
@change="freightCalc.markTransportQtyTouched()"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="formData.freightType === 'by_weight'"
|
||||
:label="`运输单价(元/${freightUnitLabel})`"
|
||||
>
|
||||
<el-input-number
|
||||
v-model="freightCalc.freightPrice.value"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="运费金额(元)">
|
||||
<el-input-number
|
||||
v-model="freightCalc.freightAmount.value"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
@change="freightCalc.markFreightAmountTouched()"
|
||||
/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</div>
|
||||
</el-form>
|
||||
</section>
|
||||
@@ -119,7 +125,10 @@ import {
|
||||
import PlPlanFormProductTable from '@/components/business/pl-plan-form-dialog/PlPlanFormProductTable.vue'
|
||||
import PlPlanViewTabs from '@/components/business/pl-plan-view-tabs/index.vue'
|
||||
import type { PlanFormProductRow } from '@/composables/usePlContractToPlan'
|
||||
import { enrichPlanProductsHazardFromCrm } from '@/composables/usePlToxicProductCertCheck'
|
||||
import {
|
||||
buildMissingHazardCertReadyMessage,
|
||||
enrichPlanProductsHazardFromCrm
|
||||
} from '@/composables/usePlToxicProductCertCheck'
|
||||
import { usePlFreightCalc, resolveFreightUnitLabel } from '@/composables/usePlFreightCalc'
|
||||
import { useDictStore } from '@/store/modules/dict'
|
||||
import { pickBxData } from '@/utils/http/bx'
|
||||
@@ -302,6 +311,16 @@ async function handleSubmit() {
|
||||
return
|
||||
}
|
||||
|
||||
if (!isAdjustMode.value) {
|
||||
const certMessage = buildMissingHazardCertReadyMessage(plan.value, productRows.value)
|
||||
if (certMessage) {
|
||||
await ElMessageBox.alert(`${certMessage},请至【危化品证上传】页面操作。`, '无法归档', {
|
||||
type: 'warning'
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
submitLoading.value = true
|
||||
try {
|
||||
const body: PlanArchiveBody = {
|
||||
@@ -392,6 +411,7 @@ async function handleSubmit() {
|
||||
.pl-plan-freight-section {
|
||||
.pl-plan-subtitle {
|
||||
margin-top: 4px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
:deep(.el-form-item__label) {
|
||||
@@ -400,6 +420,28 @@ async function handleSubmit() {
|
||||
}
|
||||
}
|
||||
|
||||
.pl-plan-freight-section-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 16px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.pl-plan-freight-switch {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-shrink: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pl-plan-freight-switch-label {
|
||||
font-size: 13px;
|
||||
color: var(--mint-text-muted, #64748b);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.pl-plan-freight-form {
|
||||
:deep(.el-form-item) {
|
||||
margin-bottom: 0;
|
||||
@@ -418,8 +460,13 @@ async function handleSubmit() {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.form-grid-4 {
|
||||
.pl-plan-archive-freight-grid {
|
||||
align-items: center;
|
||||
row-gap: 12px;
|
||||
}
|
||||
|
||||
.pl-plan-archive-freight-grid-cell {
|
||||
min-height: 1px;
|
||||
}
|
||||
|
||||
:deep(.el-select),
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
show-vehicle-part-column
|
||||
:license-plate="licensePlate"
|
||||
:trailer-no="trailerNo"
|
||||
:enforce-self-vehicle-required="enforceSelfVehicleRequired"
|
||||
:enforce-carrier-vehicle-required="enforceCarrierVehicleRequired"
|
||||
/>
|
||||
|
||||
<el-tabs
|
||||
@@ -73,13 +75,19 @@ const props = withDefaults(
|
||||
certNameOptions?: string[]
|
||||
title?: string
|
||||
layout?: 'card' | 'table'
|
||||
/** 山峡自有车:注入必备证书槽位并校验 */
|
||||
enforceSelfVehicleRequired?: boolean
|
||||
/** 承运商车辆:注入必备证书槽位并校验 */
|
||||
enforceCarrierVehicleRequired?: boolean
|
||||
}>(),
|
||||
{
|
||||
licensePlate: '',
|
||||
trailerNo: '',
|
||||
certNameOptions: () => [],
|
||||
title: '车辆证书',
|
||||
layout: 'card'
|
||||
layout: 'card',
|
||||
enforceSelfVehicleRequired: false,
|
||||
enforceCarrierVehicleRequired: false
|
||||
}
|
||||
)
|
||||
|
||||
@@ -149,9 +157,16 @@ function clearUploadRefs() {
|
||||
trailerCertFormRef.value?.clearUploadRefs()
|
||||
}
|
||||
|
||||
function refreshRequiredSlots() {
|
||||
unifiedCertFormRef.value?.refreshRequiredSlots?.()
|
||||
headCertFormRef.value?.refreshRequiredSlots?.()
|
||||
trailerCertFormRef.value?.refreshRequiredSlots?.()
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
hasUploading,
|
||||
clearUploadRefs
|
||||
clearUploadRefs,
|
||||
refreshRequiredSlots
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -46,7 +46,8 @@ import type { ViewTableMode } from '@/components/business/system-view-dialog/typ
|
||||
import {
|
||||
PL_VEHICLE_CERT_PART_HEAD,
|
||||
PL_VEHICLE_CERT_PART_TRAILER,
|
||||
getSelfVehiclePartCertMissingSuffix,
|
||||
getVehiclePartCertMissingSuffix,
|
||||
type PlVehicleCertCheckType,
|
||||
splitVehicleCertsByPart
|
||||
} from '@/utils/pl-vehicle-cert'
|
||||
|
||||
@@ -56,6 +57,8 @@ const props = withDefaults(
|
||||
licensePlate?: string
|
||||
trailerNo?: string
|
||||
vehicleId?: string
|
||||
vehicleCertCheckType?: PlVehicleCertCheckType
|
||||
/** @deprecated 使用 vehicleCertCheckType */
|
||||
checkSelfVehicleCerts?: boolean
|
||||
tableMode?: ViewTableMode
|
||||
certLayout?: 'card' | 'table'
|
||||
@@ -65,6 +68,7 @@ const props = withDefaults(
|
||||
licensePlate: '',
|
||||
trailerNo: '',
|
||||
vehicleId: '',
|
||||
vehicleCertCheckType: undefined,
|
||||
checkSelfVehicleCerts: false,
|
||||
tableMode: 'triple',
|
||||
certLayout: 'card'
|
||||
@@ -98,16 +102,26 @@ const scopedCertList = computed(() => {
|
||||
const headCertList = computed(() => splitVehicleCertsByPart(scopedCertList.value).headCerts)
|
||||
const trailerCertList = computed(() => splitVehicleCertsByPart(scopedCertList.value).trailerCerts)
|
||||
|
||||
const resolvedVehicleCertCheckType = computed<PlVehicleCertCheckType | undefined>(() => {
|
||||
if (props.vehicleCertCheckType) return props.vehicleCertCheckType
|
||||
return props.checkSelfVehicleCerts ? 'self' : undefined
|
||||
})
|
||||
|
||||
const headTabLabelWithHint = computed(() => {
|
||||
if (!props.checkSelfVehicleCerts) return headTabLabel.value
|
||||
return headTabLabel.value + getSelfVehiclePartCertMissingSuffix(headCertList.value, PL_VEHICLE_CERT_PART_HEAD)
|
||||
const checkType = resolvedVehicleCertCheckType.value
|
||||
if (!checkType) return headTabLabel.value
|
||||
return (
|
||||
headTabLabel.value +
|
||||
getVehiclePartCertMissingSuffix(headCertList.value, PL_VEHICLE_CERT_PART_HEAD, checkType)
|
||||
)
|
||||
})
|
||||
|
||||
const trailerTabLabelWithHint = computed(() => {
|
||||
if (!props.checkSelfVehicleCerts) return trailerTabLabel.value
|
||||
const checkType = resolvedVehicleCertCheckType.value
|
||||
if (!checkType) return trailerTabLabel.value
|
||||
return (
|
||||
trailerTabLabel.value +
|
||||
getSelfVehiclePartCertMissingSuffix(trailerCertList.value, PL_VEHICLE_CERT_PART_TRAILER)
|
||||
getVehiclePartCertMissingSuffix(trailerCertList.value, PL_VEHICLE_CERT_PART_TRAILER, checkType)
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
:license-plate="licensePlate"
|
||||
:trailer-no="trailerNo"
|
||||
:vehicle-id="vehicleId"
|
||||
:check-self-vehicle-certs="checkSelfVehicleCerts"
|
||||
:vehicle-cert-check-type="vehicleCertCheckType"
|
||||
:table-mode="tableMode"
|
||||
:cert-layout="certLayout"
|
||||
/>
|
||||
@@ -20,7 +20,7 @@
|
||||
>
|
||||
<PersonnelViewPanel
|
||||
:person="person"
|
||||
:check-self-vehicle-certs="checkSelfVehicleCerts"
|
||||
:check-linked-personnel-certs="showLinkedPersonnelCertCheck"
|
||||
:table-mode="tableMode"
|
||||
:cert-layout="certLayout"
|
||||
/>
|
||||
@@ -34,7 +34,7 @@ import PersonnelViewPanel from '@/components/business/pl-vehicle-personnel-view-
|
||||
import type { CertRead } from '@/api/pl'
|
||||
import type { VehiclePersonnelViewRow } from '@/components/business/pl-vehicle-personnel-view-list/index.vue'
|
||||
import type { ViewTableMode } from '@/components/business/system-view-dialog/types'
|
||||
import { getSelfVehicleCertMissingSuffix } from '@/utils/pl-vehicle-cert'
|
||||
import { getVehicleCertMissingSuffix, type PlVehicleCertCheckType } from '@/utils/pl-vehicle-cert'
|
||||
import { getPersonnelCertMissingSuffix } from '@/utils/pl-personnel-cert'
|
||||
import { formatPlPersonnelNameWithRole } from '@/utils/pl-display'
|
||||
|
||||
@@ -45,6 +45,10 @@ const props = withDefaults(
|
||||
trailerNo?: string
|
||||
vehicleId?: string
|
||||
personnelList?: VehiclePersonnelViewRow[]
|
||||
vehicleCertCheckType?: PlVehicleCertCheckType
|
||||
/** 山峡自有车:关联人员证书缺失提示 */
|
||||
checkLinkedPersonnelCerts?: boolean
|
||||
/** @deprecated 使用 vehicleCertCheckType + checkLinkedPersonnelCerts */
|
||||
checkSelfVehicleCerts?: boolean
|
||||
tableMode?: ViewTableMode
|
||||
certLayout?: 'card' | 'table'
|
||||
@@ -55,6 +59,8 @@ const props = withDefaults(
|
||||
trailerNo: '',
|
||||
vehicleId: '',
|
||||
personnelList: () => [],
|
||||
vehicleCertCheckType: undefined,
|
||||
checkLinkedPersonnelCerts: false,
|
||||
checkSelfVehicleCerts: false,
|
||||
tableMode: 'triple',
|
||||
certLayout: 'card'
|
||||
@@ -73,23 +79,29 @@ const tabsInstanceKey = computed(
|
||||
|
||||
const vehicleCertTabLabel = computed(() => {
|
||||
const label = '车辆证书'
|
||||
if (!props.checkSelfVehicleCerts) return label
|
||||
const checkType = props.vehicleCertCheckType
|
||||
if (!checkType) return label
|
||||
return (
|
||||
label +
|
||||
getSelfVehicleCertMissingSuffix({
|
||||
getVehicleCertMissingSuffix({
|
||||
vehicleType: checkType,
|
||||
certList: props.certList,
|
||||
trailerNo: props.trailerNo
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
const showLinkedPersonnelCertCheck = computed(
|
||||
() => props.checkLinkedPersonnelCerts || props.checkSelfVehicleCerts
|
||||
)
|
||||
|
||||
function getPersonKey(person: VehiclePersonnelViewRow, index: number) {
|
||||
return `person-${props.vehicleId || props.licensePlate || 'vehicle'}-${person.personnelId || `${person.personnelName}-${index}`}`
|
||||
}
|
||||
|
||||
function getPersonTabLabel(person: VehiclePersonnelViewRow) {
|
||||
const suffix =
|
||||
props.checkSelfVehicleCerts && person.role
|
||||
showLinkedPersonnelCertCheck.value && person.role
|
||||
? getPersonnelCertMissingSuffix(person.certList || [], person.role as 'driver' | 'escort')
|
||||
: ''
|
||||
return `${formatPlPersonnelNameWithRole(person.personnelName, person.role)}${suffix}`
|
||||
|
||||
+8
-1
@@ -38,19 +38,26 @@ const props = withDefaults(
|
||||
person: VehiclePersonnelViewRow
|
||||
showTitle?: boolean
|
||||
checkSelfVehicleCerts?: boolean
|
||||
/** @deprecated 使用 checkLinkedPersonnelCerts */
|
||||
checkLinkedPersonnelCerts?: boolean
|
||||
tableMode?: ViewTableMode
|
||||
certLayout?: 'card' | 'table'
|
||||
}>(),
|
||||
{
|
||||
showTitle: false,
|
||||
checkSelfVehicleCerts: false,
|
||||
checkLinkedPersonnelCerts: false,
|
||||
tableMode: 'triple',
|
||||
certLayout: 'card'
|
||||
}
|
||||
)
|
||||
|
||||
const showPersonnelCertCheck = computed(
|
||||
() => props.checkLinkedPersonnelCerts || props.checkSelfVehicleCerts
|
||||
)
|
||||
|
||||
const personnelCertMissingSuffix = computed(() => {
|
||||
if (!props.checkSelfVehicleCerts || !props.person.role) return ''
|
||||
if (!showPersonnelCertCheck.value || !props.person.role) return ''
|
||||
return getPersonnelCertMissingSuffix(
|
||||
props.person.certList || [],
|
||||
props.person.role as 'driver' | 'escort'
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
:trailer-no="detail?.trailerNo"
|
||||
:vehicle-id="detail?.id"
|
||||
:personnel-list="personnelList"
|
||||
:check-self-vehicle-certs="detail?.vehicleType === 'self'"
|
||||
:vehicle-cert-check-type="detail?.vehicleType === 'self' || detail?.vehicleType === 'carrier' ? detail.vehicleType : undefined"
|
||||
:check-linked-personnel-certs="detail?.vehicleType === 'self'"
|
||||
cert-layout="table"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,241 @@
|
||||
import type { CertRow } from '@/api/pl'
|
||||
import { matchRequiredCertName } from '@/utils/pl-personnel-cert'
|
||||
import {
|
||||
PL_CARRIER_VEHICLE_HEAD_REQUIRED_CERT_NAMES,
|
||||
PL_CARRIER_VEHICLE_TRAILER_REQUIRED_CERT_NAMES,
|
||||
PL_SELF_VEHICLE_HEAD_REQUIRED_CERT_NAMES,
|
||||
PL_SELF_VEHICLE_TRAILER_REQUIRED_CERT_NAMES,
|
||||
PL_VEHICLE_CERT_PART_HEAD,
|
||||
PL_VEHICLE_CERT_PART_TRAILER,
|
||||
type PlVehicleCertCheckType,
|
||||
getVehicleHeadRequiredCertNames,
|
||||
getVehicleTrailerRequiredCertNames,
|
||||
normalizeVehicleCertPart
|
||||
} from '@/utils/pl-vehicle-cert'
|
||||
|
||||
export type CertRowWithMeta = CertRow & { _key?: string; _required?: boolean }
|
||||
|
||||
export function isRequiredCertSlotComplete(
|
||||
cert: CertRow | undefined,
|
||||
requiredName: string
|
||||
): boolean {
|
||||
if (!cert) return false
|
||||
if (!matchRequiredCertName(cert.certName, requiredName)) return false
|
||||
if (!cert.certNo?.trim()) return false
|
||||
if (!cert.expireDate) return false
|
||||
if (!cert.attachmentUrls?.trim()) return false
|
||||
return true
|
||||
}
|
||||
|
||||
export function getMissingRequiredCertFieldLabels(
|
||||
cert: CertRow | undefined,
|
||||
requiredName: string
|
||||
): string[] {
|
||||
if (!cert || !matchRequiredCertName(cert.certName, requiredName)) {
|
||||
return ['证号', '有效期', '附件']
|
||||
}
|
||||
const missing: string[] = []
|
||||
if (!cert.certNo?.trim()) missing.push('证号')
|
||||
if (!cert.expireDate) missing.push('有效期')
|
||||
if (!cert.attachmentUrls?.trim()) missing.push('附件')
|
||||
return missing
|
||||
}
|
||||
|
||||
export function collectRequiredCertSlotErrors(
|
||||
certList: CertRow[],
|
||||
requiredNames: readonly string[],
|
||||
partLabel = ''
|
||||
): string[] {
|
||||
const errors: string[] = []
|
||||
for (const requiredName of requiredNames) {
|
||||
const matched = certList.find(c => matchRequiredCertName(c.certName, requiredName))
|
||||
const missing = getMissingRequiredCertFieldLabels(matched, requiredName)
|
||||
if (missing.length) {
|
||||
const prefix = partLabel ? `${partLabel}「${requiredName}」` : `「${requiredName}」`
|
||||
errors.push(`${prefix}缺少:${missing.join('、')}`)
|
||||
}
|
||||
}
|
||||
return errors
|
||||
}
|
||||
|
||||
export function collectSelfVehicleRequiredCertErrors(
|
||||
certList: CertRow[],
|
||||
trailerNo?: string | null
|
||||
): string[] {
|
||||
return collectVehicleRequiredCertErrors(certList, trailerNo, 'self')
|
||||
}
|
||||
|
||||
export function collectCarrierVehicleRequiredCertErrors(
|
||||
certList: CertRow[],
|
||||
trailerNo?: string | null
|
||||
): string[] {
|
||||
return collectVehicleRequiredCertErrors(certList, trailerNo, 'carrier')
|
||||
}
|
||||
|
||||
export function collectVehicleRequiredCertErrors(
|
||||
certList: CertRow[],
|
||||
trailerNo: string | null | undefined,
|
||||
vehicleType: PlVehicleCertCheckType
|
||||
): string[] {
|
||||
const headCerts = certList.filter(
|
||||
c => normalizeVehicleCertPart(c.vehiclePart) === PL_VEHICLE_CERT_PART_HEAD
|
||||
)
|
||||
const trailerCerts = certList.filter(
|
||||
c => normalizeVehicleCertPart(c.vehiclePart) === PL_VEHICLE_CERT_PART_TRAILER
|
||||
)
|
||||
const errors = [
|
||||
...collectRequiredCertSlotErrors(
|
||||
headCerts,
|
||||
getVehicleHeadRequiredCertNames(vehicleType),
|
||||
'主车'
|
||||
),
|
||||
...(trailerNo?.trim()
|
||||
? collectRequiredCertSlotErrors(
|
||||
trailerCerts,
|
||||
getVehicleTrailerRequiredCertNames(vehicleType),
|
||||
'挂车'
|
||||
)
|
||||
: [])
|
||||
]
|
||||
return errors
|
||||
}
|
||||
|
||||
function createEmptyCertRow(
|
||||
certName: string,
|
||||
vehiclePart?: string,
|
||||
required = false
|
||||
): CertRowWithMeta {
|
||||
return {
|
||||
_key: `cert-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
||||
vehiclePart,
|
||||
certName,
|
||||
certNo: '',
|
||||
expireDate: '',
|
||||
remindDays: 30,
|
||||
attachmentUrls: '',
|
||||
_required: required
|
||||
}
|
||||
}
|
||||
|
||||
function findCertIndexForRequired(
|
||||
certs: CertRowWithMeta[],
|
||||
requiredName: string,
|
||||
vehiclePart?: string
|
||||
): number {
|
||||
return certs.findIndex(cert => {
|
||||
if (vehiclePart && normalizeVehicleCertPart(cert.vehiclePart) !== vehiclePart) {
|
||||
return false
|
||||
}
|
||||
return matchRequiredCertName(cert.certName, requiredName)
|
||||
})
|
||||
}
|
||||
|
||||
export function mergeCertListWithRequiredSlots(
|
||||
existing: CertRowWithMeta[],
|
||||
requiredNames: readonly string[],
|
||||
options?: { vehiclePart?: string }
|
||||
): CertRowWithMeta[] {
|
||||
const vehiclePart = options?.vehiclePart
|
||||
const next = existing.map(cert => ({ ...cert }))
|
||||
const normalizedPart = vehiclePart ? normalizeVehicleCertPart(vehiclePart) : null
|
||||
|
||||
const extras = next.filter(cert => {
|
||||
if (normalizedPart && normalizeVehicleCertPart(cert.vehiclePart) !== normalizedPart) {
|
||||
return false
|
||||
}
|
||||
return !requiredNames.some(name => matchRequiredCertName(cert.certName, name))
|
||||
})
|
||||
|
||||
const requiredRows: CertRowWithMeta[] = requiredNames.map(requiredName => {
|
||||
const idx = findCertIndexForRequired(next, requiredName, normalizedPart || undefined)
|
||||
if (idx >= 0) {
|
||||
return {
|
||||
...next[idx],
|
||||
certName: requiredName,
|
||||
vehiclePart: normalizedPart || next[idx].vehiclePart || PL_VEHICLE_CERT_PART_HEAD,
|
||||
_required: true
|
||||
}
|
||||
}
|
||||
return createEmptyCertRow(
|
||||
requiredName,
|
||||
normalizedPart || PL_VEHICLE_CERT_PART_HEAD,
|
||||
true
|
||||
)
|
||||
})
|
||||
|
||||
const otherPartRows = normalizedPart
|
||||
? next.filter(cert => normalizeVehicleCertPart(cert.vehiclePart) !== normalizedPart)
|
||||
: []
|
||||
|
||||
return [...otherPartRows, ...requiredRows, ...extras.map(c => ({ ...c, _required: false }))]
|
||||
}
|
||||
|
||||
function ensureVehicleCertSlotsByNames(
|
||||
certList: CertRowWithMeta[],
|
||||
trailerNo: string | null | undefined,
|
||||
headRequired: readonly string[],
|
||||
trailerRequired: readonly string[]
|
||||
): CertRowWithMeta[] {
|
||||
let merged = mergeCertListWithRequiredSlots(certList, headRequired, {
|
||||
vehiclePart: PL_VEHICLE_CERT_PART_HEAD
|
||||
})
|
||||
if (trailerNo?.trim()) {
|
||||
merged = mergeCertListWithRequiredSlots(merged, trailerRequired, {
|
||||
vehiclePart: PL_VEHICLE_CERT_PART_TRAILER
|
||||
})
|
||||
} else {
|
||||
merged = stripTrailerRequiredSlots(merged, trailerRequired)
|
||||
}
|
||||
return merged
|
||||
}
|
||||
|
||||
export function ensureSelfVehicleCertSlots(
|
||||
certList: CertRowWithMeta[],
|
||||
trailerNo?: string | null
|
||||
): CertRowWithMeta[] {
|
||||
return ensureVehicleCertSlotsByNames(
|
||||
certList,
|
||||
trailerNo,
|
||||
PL_SELF_VEHICLE_HEAD_REQUIRED_CERT_NAMES,
|
||||
PL_SELF_VEHICLE_TRAILER_REQUIRED_CERT_NAMES
|
||||
)
|
||||
}
|
||||
|
||||
export function ensureCarrierVehicleCertSlots(
|
||||
certList: CertRowWithMeta[],
|
||||
trailerNo?: string | null
|
||||
): CertRowWithMeta[] {
|
||||
return ensureVehicleCertSlotsByNames(
|
||||
certList,
|
||||
trailerNo,
|
||||
PL_CARRIER_VEHICLE_HEAD_REQUIRED_CERT_NAMES,
|
||||
PL_CARRIER_VEHICLE_TRAILER_REQUIRED_CERT_NAMES
|
||||
)
|
||||
}
|
||||
|
||||
export function ensureVehicleCertSlots(
|
||||
certList: CertRowWithMeta[],
|
||||
trailerNo: string | null | undefined,
|
||||
vehicleType: PlVehicleCertCheckType
|
||||
): CertRowWithMeta[] {
|
||||
return vehicleType === 'carrier'
|
||||
? ensureCarrierVehicleCertSlots(certList, trailerNo)
|
||||
: ensureSelfVehicleCertSlots(certList, trailerNo)
|
||||
}
|
||||
|
||||
export function stripTrailerRequiredSlots(
|
||||
certList: CertRowWithMeta[],
|
||||
trailerRequiredNames: readonly string[] = PL_SELF_VEHICLE_TRAILER_REQUIRED_CERT_NAMES
|
||||
): CertRowWithMeta[] {
|
||||
return certList.filter(cert => {
|
||||
if (normalizeVehicleCertPart(cert.vehiclePart) !== PL_VEHICLE_CERT_PART_TRAILER) {
|
||||
return true
|
||||
}
|
||||
return !trailerRequiredNames.some(name => matchRequiredCertName(cert.certName, name))
|
||||
})
|
||||
}
|
||||
|
||||
export function formatRequiredCertErrors(errors: string[]): string {
|
||||
if (!errors.length) return ''
|
||||
return `必备证书未齐全:${errors.join(';')}`
|
||||
}
|
||||
@@ -225,7 +225,7 @@ export function getPlPlanFollowStatusLabel(status?: string) {
|
||||
}
|
||||
|
||||
export function getPlPlanArchiveStatusTagType(status?: string) {
|
||||
return status === 'archived' ? 'info' : 'success'
|
||||
return status === 'archived' ? 'info' : 'danger'
|
||||
}
|
||||
|
||||
export function getPlPlanArchiveStatusLabel(status?: string) {
|
||||
|
||||
@@ -11,6 +11,95 @@ export const PL_PERSONNEL_DRIVER_REQUIRED_CERT_NAMES = [
|
||||
export const PL_PERSONNEL_ESCORT_REQUIRED_CERT_NAMES = ['身份证', '从业资格证'] as const
|
||||
|
||||
export type PersonnelCertRole = 'driver' | 'escort'
|
||||
export type PersonnelType = 'driver' | 'escort' | 'both'
|
||||
|
||||
export const PL_PERSONNEL_TYPE_OPTIONS = [
|
||||
{ value: 'driver', label: '驾驶员' },
|
||||
{ value: 'escort', label: '押运员' },
|
||||
{ value: 'both', label: '驾驶员兼押运员' }
|
||||
] as const
|
||||
|
||||
export type PersonnelAffiliation = 'self' | 'carrier'
|
||||
|
||||
export const PL_PERSONNEL_AFFILIATION_OPTIONS = [
|
||||
{ value: 'self', label: '山峡' },
|
||||
{ value: 'carrier', label: '非山峡' }
|
||||
] as const
|
||||
|
||||
export function getPersonnelAffiliationLabel(affiliation?: string | null): string {
|
||||
const found = PL_PERSONNEL_AFFILIATION_OPTIONS.find(item => item.value === affiliation)
|
||||
return found?.label || affiliation || '—'
|
||||
}
|
||||
|
||||
export function getPersonnelAffiliationTagType(
|
||||
affiliation?: string | null
|
||||
): 'success' | 'warning' | 'info' {
|
||||
if (affiliation === 'self') return 'success'
|
||||
if (affiliation === 'carrier') return 'warning'
|
||||
return 'info'
|
||||
}
|
||||
|
||||
export function validatePersonnelAffiliationVehicleCompat(
|
||||
personnelAffiliation: string | undefined,
|
||||
vehicleType: string | undefined,
|
||||
onError: (message: string) => void
|
||||
): boolean {
|
||||
if (!personnelAffiliation || !vehicleType) return true
|
||||
if (personnelAffiliation === vehicleType) return true
|
||||
const vehicleLabel = vehicleType === 'self' ? '山峡' : '非山峡'
|
||||
onError(`${vehicleLabel}车辆仅可关联${vehicleLabel}人员`)
|
||||
return false
|
||||
}
|
||||
|
||||
export function getPersonnelTypeLabel(type?: string | null): string {
|
||||
const found = PL_PERSONNEL_TYPE_OPTIONS.find(item => item.value === type)
|
||||
return found?.label || type || '—'
|
||||
}
|
||||
|
||||
export function getPersonnelTypeTagType(type?: string | null): 'success' | 'warning' | 'primary' | 'info' {
|
||||
if (type === 'driver') return 'primary'
|
||||
if (type === 'escort') return 'warning'
|
||||
if (type === 'both') return 'success'
|
||||
return 'info'
|
||||
}
|
||||
|
||||
export function getRequiredPersonnelCertNamesByType(
|
||||
personnelType?: string | null
|
||||
): readonly string[] {
|
||||
if (personnelType === 'escort') {
|
||||
return PL_PERSONNEL_ESCORT_REQUIRED_CERT_NAMES
|
||||
}
|
||||
return PL_PERSONNEL_DRIVER_REQUIRED_CERT_NAMES
|
||||
}
|
||||
|
||||
export function validatePersonnelTypeRoleCompat(
|
||||
personnelType: string | undefined,
|
||||
role: string | undefined,
|
||||
onError: (message: string) => void
|
||||
): boolean {
|
||||
if (!personnelType || !role) return true
|
||||
if (personnelType === 'driver' && role === 'escort') {
|
||||
onError('驾驶员类型人员不可关联为押运员')
|
||||
return false
|
||||
}
|
||||
if (personnelType === 'escort' && role === 'driver') {
|
||||
onError('押运员类型人员不可关联为驾驶员')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
export function isPersonnelCertStrictComplete(
|
||||
cert: Pick<CertRow, 'certName' | 'certNo' | 'expireDate' | 'attachmentUrls'> | undefined,
|
||||
requiredName: string
|
||||
): boolean {
|
||||
if (!cert?.certName?.trim()) return false
|
||||
if (!matchRequiredCertName(cert.certName, requiredName)) return false
|
||||
if (!cert.certNo?.trim()) return false
|
||||
if (!cert.expireDate) return false
|
||||
if (!cert.attachmentUrls?.trim()) return false
|
||||
return true
|
||||
}
|
||||
|
||||
export function matchRequiredCertName(
|
||||
actualName: string | undefined,
|
||||
@@ -28,8 +117,13 @@ export function matchRequiredCertName(
|
||||
return name === requiredName
|
||||
}
|
||||
|
||||
export function isPersonnelCertUploaded(cert?: Pick<CertRow, 'certName' | 'attachmentUrls'>) {
|
||||
return Boolean(cert?.certName?.trim() && cert?.attachmentUrls?.trim())
|
||||
export function isPersonnelCertUploaded(cert?: Pick<CertRow, 'certName' | 'certNo' | 'expireDate' | 'attachmentUrls'>) {
|
||||
return Boolean(
|
||||
cert?.certName?.trim() &&
|
||||
cert?.certNo?.trim() &&
|
||||
cert?.expireDate &&
|
||||
cert?.attachmentUrls?.trim()
|
||||
)
|
||||
}
|
||||
|
||||
export function getRequiredPersonnelCertNames(role: PersonnelCertRole): readonly string[] {
|
||||
@@ -55,15 +149,28 @@ export function hasPersonnelCertMissing(
|
||||
return getMissingPersonnelCertNames(role, certList).length > 0
|
||||
}
|
||||
|
||||
/** 人员主数据页:至少满足押运员必备证书(身份证 + 从业资格证) */
|
||||
export function hasPersonnelMasterCertMissing(certList: CertRow[] = []): boolean {
|
||||
return hasPersonnelCertMissing('escort', certList)
|
||||
/** 人员主数据页:按人员类型检查必备证书 */
|
||||
export function hasPersonnelMasterCertMissing(
|
||||
certList: CertRow[] = [],
|
||||
personnelType?: string | null
|
||||
): boolean {
|
||||
const required = getRequiredPersonnelCertNamesByType(personnelType || 'both')
|
||||
return required.some(requiredName => {
|
||||
const matched = certList.find(cert => matchRequiredCertName(cert.certName, requiredName))
|
||||
return !isPersonnelCertStrictComplete(matched, requiredName)
|
||||
})
|
||||
}
|
||||
|
||||
export function getPersonnelCertMissingSuffix(
|
||||
certList: CertRow[] = [],
|
||||
role?: PersonnelCertRole
|
||||
role?: PersonnelCertRole,
|
||||
personnelType?: string | null
|
||||
): string {
|
||||
if (personnelType) {
|
||||
return hasPersonnelMasterCertMissing(certList, personnelType)
|
||||
? PL_PERSONNEL_CERT_MISSING_SUFFIX
|
||||
: ''
|
||||
}
|
||||
if (role) {
|
||||
return hasPersonnelCertMissing(role, certList) ? PL_PERSONNEL_CERT_MISSING_SUFFIX : ''
|
||||
}
|
||||
|
||||
@@ -21,6 +21,12 @@ export const PL_SELF_VEHICLE_TRAILER_REQUIRED_CERT_NAMES = [
|
||||
'罐检报告'
|
||||
] as const
|
||||
|
||||
export const PL_CARRIER_VEHICLE_HEAD_REQUIRED_CERT_NAMES = ['行驶证', '营运证'] as const
|
||||
|
||||
export const PL_CARRIER_VEHICLE_TRAILER_REQUIRED_CERT_NAMES = ['行驶证', '营运证'] as const
|
||||
|
||||
export type PlVehicleCertCheckType = 'self' | 'carrier'
|
||||
|
||||
export const PL_VEHICLE_CERT_NAME_OPTIONS = [
|
||||
'行驶证',
|
||||
'营运证',
|
||||
@@ -70,18 +76,40 @@ export function mergeVehicleCertsByPart<T extends CertRow>(
|
||||
return [...normalizedHead, ...normalizedTrailer]
|
||||
}
|
||||
|
||||
export function isVehicleCertUploaded(cert?: Pick<CertRow, 'certName' | 'attachmentUrls'>) {
|
||||
return Boolean(cert?.certName?.trim() && cert?.attachmentUrls?.trim())
|
||||
export function isVehicleCertUploaded(cert?: Pick<CertRow, 'certName' | 'certNo' | 'expireDate' | 'attachmentUrls'>) {
|
||||
return Boolean(
|
||||
cert?.certName?.trim() &&
|
||||
cert?.certNo?.trim() &&
|
||||
cert?.expireDate &&
|
||||
cert?.attachmentUrls?.trim()
|
||||
)
|
||||
}
|
||||
|
||||
export function getMissingSelfVehiclePartCertNames(
|
||||
export function getVehicleHeadRequiredCertNames(
|
||||
vehicleType: PlVehicleCertCheckType
|
||||
): readonly string[] {
|
||||
return vehicleType === 'carrier'
|
||||
? PL_CARRIER_VEHICLE_HEAD_REQUIRED_CERT_NAMES
|
||||
: PL_SELF_VEHICLE_HEAD_REQUIRED_CERT_NAMES
|
||||
}
|
||||
|
||||
export function getVehicleTrailerRequiredCertNames(
|
||||
vehicleType: PlVehicleCertCheckType
|
||||
): readonly string[] {
|
||||
return vehicleType === 'carrier'
|
||||
? PL_CARRIER_VEHICLE_TRAILER_REQUIRED_CERT_NAMES
|
||||
: PL_SELF_VEHICLE_TRAILER_REQUIRED_CERT_NAMES
|
||||
}
|
||||
|
||||
export function getMissingVehiclePartCertNames(
|
||||
certList: CertRow[] = [],
|
||||
part: VehicleCertPart
|
||||
part: VehicleCertPart,
|
||||
vehicleType: PlVehicleCertCheckType
|
||||
): string[] {
|
||||
const requiredNames =
|
||||
part === PL_VEHICLE_CERT_PART_TRAILER
|
||||
? PL_SELF_VEHICLE_TRAILER_REQUIRED_CERT_NAMES
|
||||
: PL_SELF_VEHICLE_HEAD_REQUIRED_CERT_NAMES
|
||||
? getVehicleTrailerRequiredCertNames(vehicleType)
|
||||
: getVehicleHeadRequiredCertNames(vehicleType)
|
||||
|
||||
return requiredNames.filter(requiredName => {
|
||||
const matched = certList.find(cert => cert.certName?.trim() === requiredName)
|
||||
@@ -89,39 +117,77 @@ export function getMissingSelfVehiclePartCertNames(
|
||||
})
|
||||
}
|
||||
|
||||
export function hasSelfVehicleCertMissing(options: {
|
||||
export function getMissingSelfVehiclePartCertNames(
|
||||
certList: CertRow[] = [],
|
||||
part: VehicleCertPart
|
||||
): string[] {
|
||||
return getMissingVehiclePartCertNames(certList, part, 'self')
|
||||
}
|
||||
|
||||
export function hasVehicleCertMissing(options: {
|
||||
vehicleType: PlVehicleCertCheckType
|
||||
certList?: CertRow[]
|
||||
trailerNo?: string | null
|
||||
}): boolean {
|
||||
const { certList = [], trailerNo } = options
|
||||
const { vehicleType, certList = [], trailerNo } = options
|
||||
const { headCerts, trailerCerts } = splitVehicleCertsByPart(certList)
|
||||
const hasTrailer = Boolean(trailerNo?.trim())
|
||||
|
||||
if (getMissingSelfVehiclePartCertNames(headCerts, PL_VEHICLE_CERT_PART_HEAD).length > 0) {
|
||||
if (
|
||||
getMissingVehiclePartCertNames(headCerts, PL_VEHICLE_CERT_PART_HEAD, vehicleType).length > 0
|
||||
) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (hasTrailer && getMissingSelfVehiclePartCertNames(trailerCerts, PL_VEHICLE_CERT_PART_TRAILER).length > 0) {
|
||||
if (
|
||||
hasTrailer &&
|
||||
getMissingVehiclePartCertNames(trailerCerts, PL_VEHICLE_CERT_PART_TRAILER, vehicleType)
|
||||
.length > 0
|
||||
) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
export function hasSelfVehicleCertMissing(options: {
|
||||
certList?: CertRow[]
|
||||
trailerNo?: string | null
|
||||
}): boolean {
|
||||
return hasVehicleCertMissing({ ...options, vehicleType: 'self' })
|
||||
}
|
||||
|
||||
export function getVehicleCertMissingSuffix(options: {
|
||||
vehicleType: PlVehicleCertCheckType
|
||||
certList?: CertRow[]
|
||||
trailerNo?: string | null
|
||||
}): string {
|
||||
return hasVehicleCertMissing(options) ? PL_VEHICLE_CERT_MISSING_SUFFIX : ''
|
||||
}
|
||||
|
||||
export function getSelfVehicleCertMissingSuffix(options: {
|
||||
certList?: CertRow[]
|
||||
trailerNo?: string | null
|
||||
}): string {
|
||||
return hasSelfVehicleCertMissing(options) ? PL_VEHICLE_CERT_MISSING_SUFFIX : ''
|
||||
return getVehicleCertMissingSuffix({ ...options, vehicleType: 'self' })
|
||||
}
|
||||
|
||||
export function getVehiclePartCertMissingSuffix(
|
||||
certList: CertRow[] = [],
|
||||
part: VehicleCertPart,
|
||||
vehicleType?: PlVehicleCertCheckType | null
|
||||
): string {
|
||||
if (!vehicleType) return ''
|
||||
return getMissingVehiclePartCertNames(certList, part, vehicleType).length > 0
|
||||
? PL_VEHICLE_CERT_MISSING_SUFFIX
|
||||
: ''
|
||||
}
|
||||
|
||||
export function getSelfVehiclePartCertMissingSuffix(
|
||||
certList: CertRow[] = [],
|
||||
part: VehicleCertPart
|
||||
): string {
|
||||
return getMissingSelfVehiclePartCertNames(certList, part).length > 0
|
||||
? PL_VEHICLE_CERT_MISSING_SUFFIX
|
||||
: ''
|
||||
return getVehiclePartCertMissingSuffix(certList, part, 'self')
|
||||
}
|
||||
|
||||
/** @deprecated 保存不再阻断证书完整性,仅校验证书名称非空等基础项 */
|
||||
|
||||
@@ -426,6 +426,7 @@ const formData = reactive({
|
||||
const createQualificationRules = {
|
||||
businessLicenseUrls: [
|
||||
{
|
||||
required: true,
|
||||
validator: (_rule: unknown, value: string, callback: (error?: Error) => void) => {
|
||||
if (!value || !String(value).trim()) {
|
||||
callback(new Error('请上传营业执照'))
|
||||
@@ -438,6 +439,7 @@ const createQualificationRules = {
|
||||
],
|
||||
roadTransportLicenseUrls: [
|
||||
{
|
||||
required: true,
|
||||
validator: (_rule: unknown, value: string, callback: (error?: Error) => void) => {
|
||||
if (!value || !String(value).trim()) {
|
||||
callback(new Error('请上传道路经营许可证'))
|
||||
@@ -450,6 +452,7 @@ const createQualificationRules = {
|
||||
],
|
||||
transportAgreementUrls: [
|
||||
{
|
||||
required: true,
|
||||
validator: (_rule: unknown, value: string, callback: (error?: Error) => void) => {
|
||||
if (!value || !String(value).trim()) {
|
||||
callback(new Error('请上传运输协议'))
|
||||
@@ -462,10 +465,12 @@ const createQualificationRules = {
|
||||
]
|
||||
}
|
||||
|
||||
const formRules = computed(() => ({
|
||||
const formRules = {
|
||||
carrierName: [{ required: true, message: '请输入承运商名称', trigger: 'blur' }],
|
||||
...(isEdit.value ? {} : createQualificationRules)
|
||||
}))
|
||||
contactName: [{ required: true, message: '请输入联系人', trigger: 'blur' }],
|
||||
contactPhone: [{ required: true, message: '请输入联系电话', trigger: 'blur' }],
|
||||
...createQualificationRules
|
||||
}
|
||||
|
||||
function handleCertFieldChange(
|
||||
field: 'businessLicenseUrls' | 'roadTransportLicenseUrls' | 'transportAgreementUrls'
|
||||
@@ -658,29 +663,12 @@ async function handleSubmit() {
|
||||
contactPhone: formData.contactPhone,
|
||||
status: formData.status,
|
||||
remark: formData.remark,
|
||||
...(isEdit.value
|
||||
? {
|
||||
...(formData.businessLicenseUrls
|
||||
? { businessLicenseUrls: formData.businessLicenseUrls }
|
||||
: {}),
|
||||
...(formData.roadTransportLicenseUrls
|
||||
? { roadTransportLicenseUrls: formData.roadTransportLicenseUrls }
|
||||
: {}),
|
||||
...(formData.transportAgreementUrls
|
||||
? { transportAgreementUrls: formData.transportAgreementUrls }
|
||||
: {}),
|
||||
...(formData.transportAgreementExpireDate
|
||||
? { transportAgreementExpireDate: formData.transportAgreementExpireDate }
|
||||
: {})
|
||||
}
|
||||
: {
|
||||
businessLicenseUrls: formData.businessLicenseUrls,
|
||||
roadTransportLicenseUrls: formData.roadTransportLicenseUrls,
|
||||
transportAgreementUrls: formData.transportAgreementUrls,
|
||||
...(formData.transportAgreementExpireDate
|
||||
? { transportAgreementExpireDate: formData.transportAgreementExpireDate }
|
||||
: {})
|
||||
})
|
||||
businessLicenseUrls: formData.businessLicenseUrls,
|
||||
roadTransportLicenseUrls: formData.roadTransportLicenseUrls,
|
||||
transportAgreementUrls: formData.transportAgreementUrls,
|
||||
...(formData.transportAgreementExpireDate
|
||||
? { transportAgreementExpireDate: formData.transportAgreementExpireDate }
|
||||
: {})
|
||||
}
|
||||
if (isEdit.value) {
|
||||
await carrierApi.update(formData.id, payload)
|
||||
|
||||
@@ -24,6 +24,16 @@
|
||||
<el-option label="停用" value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="人员归属">
|
||||
<el-select v-model="searchForm.personnelAffiliation" placeholder="全部" clearable>
|
||||
<el-option
|
||||
v-for="option in PL_PERSONNEL_AFFILIATION_OPTIONS"
|
||||
:key="option.value"
|
||||
:label="option.label"
|
||||
:value="option.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="filter-actions">
|
||||
<div class="filter-actions-end">
|
||||
@@ -112,6 +122,32 @@
|
||||
<el-link type="primary" @click="handleView(row)">{{ row.personnelName }}</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
v-if="isColumnVisible('personnelAffiliation')"
|
||||
label="人员归属"
|
||||
min-width="100"
|
||||
align="center"
|
||||
header-align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="getPersonnelAffiliationTagType(row.personnelAffiliation)" size="small">
|
||||
{{ getPersonnelAffiliationLabel(row.personnelAffiliation) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
v-if="isColumnVisible('personnelType')"
|
||||
label="人员类型"
|
||||
min-width="130"
|
||||
align="center"
|
||||
header-align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="getPersonnelTypeTagType(row.personnelType)" size="small">
|
||||
{{ getPersonnelTypeLabel(row.personnelType) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
v-if="isColumnVisible('phoneNo')"
|
||||
prop="phoneNo"
|
||||
@@ -224,6 +260,26 @@
|
||||
<el-form-item label="身份证号" prop="idCardNo">
|
||||
<el-input v-model="formData.idCardNo" placeholder="请输入身份证号" maxlength="32" />
|
||||
</el-form-item>
|
||||
<el-form-item label="人员归属" prop="personnelAffiliation">
|
||||
<el-select v-model="formData.personnelAffiliation" placeholder="请选择人员归属">
|
||||
<el-option
|
||||
v-for="option in PL_PERSONNEL_AFFILIATION_OPTIONS"
|
||||
:key="option.value"
|
||||
:label="option.label"
|
||||
:value="option.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="人员类型" prop="personnelType">
|
||||
<el-select v-model="formData.personnelType" placeholder="请选择人员类型">
|
||||
<el-option
|
||||
v-for="option in PL_PERSONNEL_TYPE_OPTIONS"
|
||||
:key="option.value"
|
||||
:label="option.label"
|
||||
:value="option.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="formData.status" placeholder="请选择">
|
||||
<el-option label="在职" value="1" />
|
||||
@@ -248,6 +304,7 @@
|
||||
title="人员证书"
|
||||
upload-folder="pl/personnel-cert"
|
||||
:cert-name-options="certNameOptions"
|
||||
:required-cert-names="requiredPersonnelCertNames"
|
||||
layout="table"
|
||||
/>
|
||||
</el-form>
|
||||
@@ -311,7 +368,21 @@ import { pickBxPageResult } from '@/utils/http/bx'
|
||||
import { DEFAULT_LIST_PAGE_SIZE } from '@/utils/table/tableConfig'
|
||||
import { formatDateTime } from '@/utils/format'
|
||||
import { canDeleteByCreator, filterDeletableByCreator, allRowsDeletableByCreator, CREATOR_DELETE_TIP } from '@/utils/creator-delete'
|
||||
import { getPersonnelCertMissingSuffix } from '@/utils/pl-personnel-cert'
|
||||
import {
|
||||
collectRequiredCertSlotErrors,
|
||||
formatRequiredCertErrors,
|
||||
mergeCertListWithRequiredSlots
|
||||
} from '@/utils/pl-cert-slots'
|
||||
import {
|
||||
getPersonnelCertMissingSuffix,
|
||||
getRequiredPersonnelCertNamesByType,
|
||||
getPersonnelTypeLabel,
|
||||
getPersonnelTypeTagType,
|
||||
getPersonnelAffiliationLabel,
|
||||
getPersonnelAffiliationTagType,
|
||||
PL_PERSONNEL_AFFILIATION_OPTIONS,
|
||||
PL_PERSONNEL_TYPE_OPTIONS
|
||||
} from '@/utils/pl-personnel-cert'
|
||||
|
||||
type CertListItem = CertRow & { _key?: string }
|
||||
type CertFormListRef = InstanceType<typeof PlCertFormList>
|
||||
@@ -319,6 +390,8 @@ type CertFormListRef = InstanceType<typeof PlCertFormList>
|
||||
const loading = ref(false)
|
||||
|
||||
const listColumnDefs = [
|
||||
{ prop: 'personnelAffiliation', label: '人员归属' },
|
||||
{ prop: 'personnelType', label: '人员类型' },
|
||||
{ prop: 'phoneNo', label: '手机号' },
|
||||
{ prop: 'age', label: '年龄' },
|
||||
{ prop: 'idCardNo', label: '身份证号' },
|
||||
@@ -359,7 +432,8 @@ const certNameOptions = [
|
||||
|
||||
const searchForm = reactive({
|
||||
q: '',
|
||||
status: ''
|
||||
status: '',
|
||||
personnelAffiliation: ''
|
||||
})
|
||||
|
||||
const pagination = reactive({
|
||||
@@ -374,12 +448,20 @@ const formData = reactive({
|
||||
phoneNo: '',
|
||||
age: null as number | null,
|
||||
idCardNo: '',
|
||||
personnelType: '',
|
||||
personnelAffiliation: 'self',
|
||||
status: '1',
|
||||
remark: ''
|
||||
})
|
||||
|
||||
const requiredPersonnelCertNames = computed(() =>
|
||||
getRequiredPersonnelCertNamesByType(formData.personnelType)
|
||||
)
|
||||
|
||||
const formRules = {
|
||||
personnelName: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
|
||||
personnelAffiliation: [{ required: true, message: '请选择人员归属', trigger: 'change' }],
|
||||
personnelType: [{ required: true, message: '请选择人员类型', trigger: 'change' }],
|
||||
status: [{ required: true, message: '请选择状态', trigger: 'change' }]
|
||||
}
|
||||
|
||||
@@ -410,7 +492,7 @@ const viewHeroMeta = computed(() => {
|
||||
const viewDialogTitle = computed(() => {
|
||||
const name = viewDetail.value?.personnelName || '人员详情'
|
||||
if (!viewDetail.value) return name
|
||||
return name + getPersonnelCertMissingSuffix(viewDetail.value.certList || [])
|
||||
return name + getPersonnelCertMissingSuffix(viewDetail.value.certList || [], undefined, viewDetail.value.personnelType)
|
||||
})
|
||||
|
||||
const viewSections = computed<ViewSection[]>(() => {
|
||||
@@ -423,6 +505,16 @@ const viewSections = computed<ViewSection[]>(() => {
|
||||
fields: [
|
||||
{ label: '姓名', value: d.personnelName },
|
||||
{ label: '手机号', value: d.phoneNo || '—' },
|
||||
{
|
||||
label: '人员归属',
|
||||
value: getPersonnelAffiliationLabel(d.personnelAffiliation),
|
||||
tagType: getPersonnelAffiliationTagType(d.personnelAffiliation)
|
||||
},
|
||||
{
|
||||
label: '人员类型',
|
||||
value: getPersonnelTypeLabel(d.personnelType),
|
||||
tagType: getPersonnelTypeTagType(d.personnelType)
|
||||
},
|
||||
{ label: '年龄', value: d.age ?? '—' },
|
||||
{ label: '身份证号', value: maskIdCard(d.idCardNo) },
|
||||
{
|
||||
@@ -443,7 +535,7 @@ const viewSections = computed<ViewSection[]>(() => {
|
||||
tableMode: 'triple'
|
||||
},
|
||||
{
|
||||
title: `人员证书${viewDetail.value ? getPersonnelCertMissingSuffix(viewDetail.value.certList || []) : ''}`,
|
||||
title: `人员证书${viewDetail.value ? getPersonnelCertMissingSuffix(viewDetail.value.certList || [], undefined, viewDetail.value.personnelType) : ''}`,
|
||||
fields: [],
|
||||
slot: 'certs'
|
||||
}
|
||||
@@ -451,15 +543,39 @@ const viewSections = computed<ViewSection[]>(() => {
|
||||
})
|
||||
|
||||
function validateCerts(): boolean {
|
||||
if (!formData.personnelType) {
|
||||
ElMessage.warning('请选择人员类型')
|
||||
return false
|
||||
}
|
||||
const errors = collectRequiredCertSlotErrors(
|
||||
certList.value,
|
||||
requiredPersonnelCertNames.value
|
||||
)
|
||||
if (errors.length) {
|
||||
ElMessage.warning(formatRequiredCertErrors(errors))
|
||||
return false
|
||||
}
|
||||
for (const cert of certList.value) {
|
||||
if (!cert.certName?.trim()) {
|
||||
ElMessage.warning('证书名称不能为空')
|
||||
if (!cert._required && !cert.certName?.trim()) {
|
||||
ElMessage.warning('其他证书名称不能为空')
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
watch(
|
||||
() => formData.personnelType,
|
||||
() => {
|
||||
if (!formData.personnelType) return
|
||||
certList.value = mergeCertListWithRequiredSlots(
|
||||
certList.value,
|
||||
getRequiredPersonnelCertNamesByType(formData.personnelType)
|
||||
)
|
||||
certFormListRef.value?.refreshRequiredSlots?.()
|
||||
}
|
||||
)
|
||||
|
||||
function resetCertList() {
|
||||
certList.value = []
|
||||
certFormListRef.value?.clearUploadRefs()
|
||||
@@ -471,6 +587,7 @@ async function fetchData() {
|
||||
const res = await personnelApi.list({
|
||||
q: searchForm.q || undefined,
|
||||
status: searchForm.status || undefined,
|
||||
personnelAffiliation: searchForm.personnelAffiliation || undefined,
|
||||
page: pagination.page,
|
||||
pageSize: pagination.pageSize
|
||||
})
|
||||
@@ -492,6 +609,7 @@ function handleSearch() {
|
||||
function handleReset() {
|
||||
searchForm.q = ''
|
||||
searchForm.status = ''
|
||||
searchForm.personnelAffiliation = ''
|
||||
selectedRows.value = []
|
||||
pagination.page = 1
|
||||
fetchData()
|
||||
@@ -548,6 +666,8 @@ function handleAdd() {
|
||||
phoneNo: '',
|
||||
age: null,
|
||||
idCardNo: '',
|
||||
personnelType: '',
|
||||
personnelAffiliation: 'self',
|
||||
status: '1',
|
||||
remark: ''
|
||||
})
|
||||
@@ -583,19 +703,24 @@ async function handleEdit(row: Personnel) {
|
||||
phoneNo: detail.phoneNo || '',
|
||||
age: detail.age ?? null,
|
||||
idCardNo: detail.idCardNo || '',
|
||||
personnelType: detail.personnelType || 'both',
|
||||
personnelAffiliation: detail.personnelAffiliation || 'self',
|
||||
status: detail.status,
|
||||
remark: detail.remark || ''
|
||||
})
|
||||
certList.value = (detail.certList || []).map(c => ({
|
||||
_key: c.id || c.certId || `cert-${Math.random().toString(36).slice(2, 10)}`,
|
||||
certId: c.id || c.certId,
|
||||
certName: c.certName,
|
||||
certNo: c.certNo,
|
||||
expireDate: c.expireDate,
|
||||
remindDays: c.remindDays ?? 30,
|
||||
attachmentUrls: c.attachmentUrls || ''
|
||||
}))
|
||||
certFormListRef.value?.clearUploadRefs()
|
||||
certList.value = mergeCertListWithRequiredSlots(
|
||||
(detail.certList || []).map(c => ({
|
||||
_key: c.id || c.certId || `cert-${Math.random().toString(36).slice(2, 10)}`,
|
||||
certId: c.id || c.certId,
|
||||
certName: c.certName,
|
||||
certNo: c.certNo,
|
||||
expireDate: c.expireDate,
|
||||
remindDays: c.remindDays ?? 30,
|
||||
attachmentUrls: c.attachmentUrls || ''
|
||||
})),
|
||||
getRequiredPersonnelCertNamesByType(detail.personnelType || 'both')
|
||||
)
|
||||
certFormListRef.value?.refreshRequiredSlots?.()
|
||||
} catch {
|
||||
ElMessage.error('加载人员详情失败')
|
||||
}
|
||||
@@ -616,6 +741,8 @@ async function handleSubmit() {
|
||||
phoneNo: formData.phoneNo || undefined,
|
||||
age: formData.age ?? undefined,
|
||||
idCardNo: formData.idCardNo || undefined,
|
||||
personnelType: formData.personnelType,
|
||||
personnelAffiliation: formData.personnelAffiliation,
|
||||
status: formData.status,
|
||||
remark: formData.remark || undefined,
|
||||
certList: certList.value.map(c => ({
|
||||
@@ -664,7 +791,8 @@ async function handleDelete(row: Personnel) {
|
||||
useDbInstantSearch(
|
||||
() => ({
|
||||
q: searchForm.q,
|
||||
status: searchForm.status
|
||||
status: searchForm.status,
|
||||
personnelAffiliation: searchForm.personnelAffiliation
|
||||
}),
|
||||
handleSearch
|
||||
)
|
||||
|
||||
@@ -148,36 +148,6 @@
|
||||
min-width="120"
|
||||
align="center"
|
||||
/>
|
||||
<el-table-column
|
||||
v-if="isColumnVisible('planType')"
|
||||
prop="planType"
|
||||
label="单据类型"
|
||||
min-width="100"
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="getPlanTypeTagType(row.planType)" size="small">
|
||||
{{ getPlanTypeLabel(row.planType) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
v-if="isColumnVisible('customerName')"
|
||||
prop="customerName"
|
||||
label="客户名称"
|
||||
min-width="180"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column v-if="isColumnVisible('products')" label="产品信息" min-width="240">
|
||||
<template #default="{ row }">
|
||||
<PlPlanListProductCell :products="row.products" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="isColumnVisible('planAttachments')" label="计划附件" min-width="200">
|
||||
<template #default="{ row }">
|
||||
<PlPlanListCertCell :products="row.products" compact hide-product />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
v-if="isColumnVisible('archiveStatus')"
|
||||
prop="archiveStatus"
|
||||
@@ -217,6 +187,36 @@
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
v-if="isColumnVisible('planType')"
|
||||
prop="planType"
|
||||
label="单据类型"
|
||||
min-width="100"
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="getPlanTypeTagType(row.planType)" size="small">
|
||||
{{ getPlanTypeLabel(row.planType) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
v-if="isColumnVisible('customerName')"
|
||||
prop="customerName"
|
||||
label="客户名称"
|
||||
min-width="180"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column v-if="isColumnVisible('products')" label="产品信息" min-width="240">
|
||||
<template #default="{ row }">
|
||||
<PlPlanListProductCell :products="row.products" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="isColumnVisible('planAttachments')" label="计划附件" min-width="200">
|
||||
<template #default="{ row }">
|
||||
<PlPlanListCertCell :products="row.products" compact hide-product />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
v-if="isColumnVisible('licensePlate')"
|
||||
prop="licensePlate"
|
||||
@@ -313,6 +313,11 @@ import {
|
||||
Search
|
||||
} from '@element-plus/icons-vue'
|
||||
import { planApi, type Plan } from '@/api/pl'
|
||||
import {
|
||||
buildMissingHazardCertReadyMessage,
|
||||
enrichPlanProductsHazardFromCrm
|
||||
} from '@/composables/usePlToxicProductCertCheck'
|
||||
import { pickBxData } from '@/utils/http/bx'
|
||||
import PlPlanArchiveDialog from '@/components/business/pl-plan-archive-dialog/index.vue'
|
||||
import PlPlanFormDialog from '@/components/business/pl-plan-form-dialog/index.vue'
|
||||
import PlPlanListCertCell from '@/components/business/pl-plan-list-cert-cell/index.vue'
|
||||
@@ -347,13 +352,13 @@ const planList = ref<Plan[]>([])
|
||||
|
||||
const listColumnDefs = [
|
||||
{ prop: 'planDate', label: '计划日期' },
|
||||
{ prop: 'archiveStatus', label: '归档状态' },
|
||||
{ prop: 'followStatus', label: '跟进状态' },
|
||||
{ prop: 'transportStatus', label: '运输状态' },
|
||||
{ prop: 'planType', label: '单据类型' },
|
||||
{ prop: 'customerName', label: '客户名称' },
|
||||
{ prop: 'products', label: '产品信息' },
|
||||
{ prop: 'planAttachments', label: '计划附件' },
|
||||
{ prop: 'archiveStatus', label: '归档状态' },
|
||||
{ prop: 'followStatus', label: '跟进状态' },
|
||||
{ prop: 'transportStatus', label: '运输状态' },
|
||||
{ prop: 'licensePlate', label: '车牌号' },
|
||||
{ prop: 'personnelSummary', label: '人员' },
|
||||
{ prop: 'followUserName', label: '跟进人', defaultVisible: false }
|
||||
@@ -530,7 +535,34 @@ function handleEdit(row: Plan) {
|
||||
formDialogVisible.value = true
|
||||
}
|
||||
|
||||
function handleArchive(row: Plan) {
|
||||
async function handleArchive(row: Plan) {
|
||||
if (row.archiveStatus === 'archived') {
|
||||
archivePlanId.value = row.id
|
||||
archiveDialogVisible.value = true
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await planApi.get(row.id)
|
||||
const data = pickBxData<Plan>(res)
|
||||
if (!data?.id) {
|
||||
throw new Error('运输计划数据无效')
|
||||
}
|
||||
const rawProducts = Array.isArray(data.products) ? data.products : []
|
||||
const enrichedProducts = await enrichPlanProductsHazardFromCrm(rawProducts)
|
||||
const certMessage = buildMissingHazardCertReadyMessage(data, enrichedProducts)
|
||||
if (certMessage) {
|
||||
await ElMessageBox.alert(`${certMessage},请至【危化品证上传】页面操作。`, '无法归档', {
|
||||
type: 'warning'
|
||||
})
|
||||
return
|
||||
}
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : '加载运输计划失败'
|
||||
ElMessage.error(message.includes('加载') ? message : `加载运输计划失败:${message}`)
|
||||
return
|
||||
}
|
||||
|
||||
archivePlanId.value = row.id
|
||||
archiveDialogVisible.value = true
|
||||
}
|
||||
|
||||
@@ -197,7 +197,7 @@
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.transportStatus === 'arranged' ? 'success' : 'info'" size="small">
|
||||
<el-tag :type="row.transportStatus === 'arranged' ? 'success' : 'danger'" size="small">
|
||||
{{ row.transportStatus === 'arranged' ? '已安排' : '待安排' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
|
||||
@@ -316,6 +316,7 @@
|
||||
upload-folder="pl/vehicle-cert"
|
||||
:cert-name-options="certNameOptions"
|
||||
layout="table"
|
||||
enforce-self-vehicle-required
|
||||
/>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
@@ -362,7 +363,8 @@
|
||||
:trailer-no="viewDetail?.trailerNo"
|
||||
:vehicle-id="viewDetail?.id"
|
||||
:personnel-list="viewPersonnelList"
|
||||
check-self-vehicle-certs
|
||||
vehicle-cert-check-type="self"
|
||||
check-linked-personnel-certs
|
||||
cert-layout="table"
|
||||
/>
|
||||
</template>
|
||||
@@ -403,12 +405,16 @@ import {
|
||||
getPlVehicleStatusTagType,
|
||||
normalizePlPersonnelSummaryDisplay
|
||||
} from '@/utils/pl-display'
|
||||
import {
|
||||
collectSelfVehicleRequiredCertErrors,
|
||||
ensureSelfVehicleCertSlots,
|
||||
formatRequiredCertErrors
|
||||
} from '@/utils/pl-cert-slots'
|
||||
import {
|
||||
PL_VEHICLE_CERT_NAME_OPTIONS,
|
||||
splitVehicleCertsByPart,
|
||||
getSelfVehicleCertMissingSuffix,
|
||||
validateVehicleCertNamesNotEmpty
|
||||
getSelfVehicleCertMissingSuffix
|
||||
} from '@/utils/pl-vehicle-cert'
|
||||
import { validatePersonnelTypeRoleCompat, validatePersonnelAffiliationVehicleCompat } from '@/utils/pl-personnel-cert'
|
||||
import { canDeleteByCreator, filterDeletableByCreator, allRowsDeletableByCreator, CREATOR_DELETE_TIP } from '@/utils/creator-delete'
|
||||
|
||||
const VEHICLE_TYPE_SELF = 'self'
|
||||
@@ -628,7 +634,7 @@ function resolvePersonnelRows(rows: VehiclePersonnelRow[]) {
|
||||
}
|
||||
|
||||
async function loadPersonnelOptions() {
|
||||
const res = await personnelApi.list({ status: '1', pageSize: 100 })
|
||||
const res = await personnelApi.list({ status: '1', pageSize: 100, personnelAffiliation: 'self' })
|
||||
personnelOptions.value = pickBxList<Personnel>(res)
|
||||
}
|
||||
|
||||
@@ -682,14 +688,29 @@ function validateSubTables(): boolean {
|
||||
ElMessage.warning('请选择人员类型')
|
||||
return false
|
||||
}
|
||||
const person = personnelOptions.value.find(p => p.id === row.personnelId)
|
||||
if (
|
||||
!validatePersonnelTypeRoleCompat(person?.personnelType, row.role, message =>
|
||||
ElMessage.warning(message)
|
||||
)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
if (
|
||||
!validatePersonnelAffiliationVehicleCompat(person?.personnelAffiliation, 'self', message =>
|
||||
ElMessage.warning(message)
|
||||
)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const hasTrailer = Boolean(formData.trailerNo?.trim())
|
||||
const { headCerts, trailerCerts } = splitVehicleCertsByPart(certList.value)
|
||||
return validateVehicleCertNamesNotEmpty(
|
||||
[headCerts, ...(hasTrailer ? [trailerCerts] : [])],
|
||||
message => ElMessage.warning(message)
|
||||
)
|
||||
const certErrors = collectSelfVehicleRequiredCertErrors(certList.value, formData.trailerNo)
|
||||
if (certErrors.length) {
|
||||
ElMessage.warning(formatRequiredCertErrors(certErrors))
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
async function fetchData() {
|
||||
@@ -794,6 +815,7 @@ function handleAdd() {
|
||||
remark: ''
|
||||
})
|
||||
resetSubTables()
|
||||
certList.value = ensureSelfVehicleCertSlots([], formData.trailerNo)
|
||||
loadPersonnelOptions()
|
||||
dialogVisible.value = true
|
||||
}
|
||||
@@ -845,17 +867,20 @@ async function handleEdit(row: Vehicle) {
|
||||
age: p.age ?? null
|
||||
}))
|
||||
)
|
||||
certList.value = (detail.certList || []).map(c => ({
|
||||
_key: c.id || c.certId || `cert-${Math.random().toString(36).slice(2, 10)}`,
|
||||
certId: c.id || c.certId,
|
||||
vehiclePart: c.vehiclePart,
|
||||
certName: c.certName,
|
||||
certNo: c.certNo,
|
||||
expireDate: c.expireDate,
|
||||
remindDays: c.remindDays ?? 30,
|
||||
attachmentUrls: c.attachmentUrls || ''
|
||||
}))
|
||||
certFormListRef.value?.clearUploadRefs()
|
||||
certList.value = ensureSelfVehicleCertSlots(
|
||||
(detail.certList || []).map(c => ({
|
||||
_key: c.id || c.certId || `cert-${Math.random().toString(36).slice(2, 10)}`,
|
||||
certId: c.id || c.certId,
|
||||
vehiclePart: c.vehiclePart,
|
||||
certName: c.certName,
|
||||
certNo: c.certNo,
|
||||
expireDate: c.expireDate,
|
||||
remindDays: c.remindDays ?? 30,
|
||||
attachmentUrls: c.attachmentUrls || ''
|
||||
})),
|
||||
formData.trailerNo
|
||||
)
|
||||
certFormListRef.value?.refreshRequiredSlots?.()
|
||||
} catch {
|
||||
dialogVisible.value = false
|
||||
ElMessage.error('加载车辆详情失败')
|
||||
|
||||
@@ -335,6 +335,8 @@
|
||||
upload-folder="pl/vehicle-cert"
|
||||
:cert-name-options="certNameOptions"
|
||||
layout="table"
|
||||
:enforce-self-vehicle-required="formData.vehicleType === 'self'"
|
||||
:enforce-carrier-vehicle-required="formData.vehicleType === 'carrier'"
|
||||
/>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
@@ -381,7 +383,10 @@
|
||||
:trailer-no="viewDetail?.trailerNo"
|
||||
:vehicle-id="viewDetail?.id"
|
||||
:personnel-list="viewPersonnelList"
|
||||
:check-self-vehicle-certs="viewDetail?.vehicleType === 'self'"
|
||||
:vehicle-cert-check-type="viewVehicleCertCheckType"
|
||||
:check-linked-personnel-certs="
|
||||
viewDetail?.vehicleType === 'self' || viewDetail?.vehicleType === 'carrier'
|
||||
"
|
||||
cert-layout="table"
|
||||
/>
|
||||
</template>
|
||||
@@ -416,12 +421,18 @@ import { pickBxList, pickBxPageResult } from '@/utils/http/bx'
|
||||
import { DEFAULT_LIST_PAGE_SIZE } from '@/utils/table/tableConfig'
|
||||
import { formatDateTime } from '@/utils/format'
|
||||
import { formatPlVehiclePlateDisplay, normalizePlPersonnelSummaryDisplay } from '@/utils/pl-display'
|
||||
import {
|
||||
collectCarrierVehicleRequiredCertErrors,
|
||||
collectSelfVehicleRequiredCertErrors,
|
||||
ensureVehicleCertSlots,
|
||||
formatRequiredCertErrors
|
||||
} from '@/utils/pl-cert-slots'
|
||||
import {
|
||||
PL_VEHICLE_CERT_NAME_OPTIONS,
|
||||
splitVehicleCertsByPart,
|
||||
getSelfVehicleCertMissingSuffix,
|
||||
validateVehicleCertNamesNotEmpty
|
||||
getVehicleCertMissingSuffix,
|
||||
type PlVehicleCertCheckType
|
||||
} from '@/utils/pl-vehicle-cert'
|
||||
import { validatePersonnelTypeRoleCompat, validatePersonnelAffiliationVehicleCompat } from '@/utils/pl-personnel-cert'
|
||||
import { canDeleteByCreator, filterDeletableByCreator, allRowsDeletableByCreator, CREATOR_DELETE_TIP } from '@/utils/creator-delete'
|
||||
|
||||
type CertListItem = CertRow & { _key?: string }
|
||||
@@ -534,12 +545,19 @@ const viewHeroMeta = computed(() => {
|
||||
return parts.join(' · ')
|
||||
})
|
||||
|
||||
const viewVehicleCertCheckType = computed<PlVehicleCertCheckType | undefined>(() => {
|
||||
const type = viewDetail.value?.vehicleType
|
||||
return type === 'self' || type === 'carrier' ? type : undefined
|
||||
})
|
||||
|
||||
const viewDialogTitle = computed(() => {
|
||||
const plate = viewDetail.value?.licensePlate || '车辆详情'
|
||||
if (!viewDetail.value || viewDetail.value.vehicleType !== 'self') return plate
|
||||
const checkType = viewVehicleCertCheckType.value
|
||||
if (!viewDetail.value || !checkType) return plate
|
||||
return (
|
||||
plate +
|
||||
getSelfVehicleCertMissingSuffix({
|
||||
getVehicleCertMissingSuffix({
|
||||
vehicleType: checkType,
|
||||
certList: viewDetail.value.certList,
|
||||
trailerNo: viewDetail.value.trailerNo
|
||||
})
|
||||
@@ -682,7 +700,13 @@ function resolvePersonnelRows(rows: VehiclePersonnelRow[]) {
|
||||
}
|
||||
|
||||
async function loadPersonnelOptions() {
|
||||
const res = await personnelApi.list({ status: '1', pageSize: 100 })
|
||||
const affiliation =
|
||||
formData.vehicleType === 'carrier' ? 'carrier' : formData.vehicleType === 'self' ? 'self' : undefined
|
||||
const res = await personnelApi.list({
|
||||
status: '1',
|
||||
pageSize: 100,
|
||||
personnelAffiliation: affiliation
|
||||
})
|
||||
personnelOptions.value = pickBxList<Personnel>(res)
|
||||
}
|
||||
|
||||
@@ -736,14 +760,41 @@ function validateSubTables(): boolean {
|
||||
ElMessage.warning('请选择人员类型')
|
||||
return false
|
||||
}
|
||||
if (formData.vehicleType === 'self' || formData.vehicleType === 'carrier') {
|
||||
const person = personnelOptions.value.find(p => p.id === row.personnelId)
|
||||
if (
|
||||
!validatePersonnelTypeRoleCompat(person?.personnelType, row.role, message =>
|
||||
ElMessage.warning(message)
|
||||
)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
if (
|
||||
!validatePersonnelAffiliationVehicleCompat(
|
||||
person?.personnelAffiliation,
|
||||
formData.vehicleType,
|
||||
message => ElMessage.warning(message)
|
||||
)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const hasTrailer = Boolean(formData.trailerNo?.trim())
|
||||
const { headCerts, trailerCerts } = splitVehicleCertsByPart(certList.value)
|
||||
return validateVehicleCertNamesNotEmpty(
|
||||
[headCerts, ...(hasTrailer ? [trailerCerts] : [])],
|
||||
message => ElMessage.warning(message)
|
||||
)
|
||||
if (formData.vehicleType === 'self') {
|
||||
const certErrors = collectSelfVehicleRequiredCertErrors(certList.value, formData.trailerNo)
|
||||
if (certErrors.length) {
|
||||
ElMessage.warning(formatRequiredCertErrors(certErrors))
|
||||
return false
|
||||
}
|
||||
} else if (formData.vehicleType === 'carrier') {
|
||||
const certErrors = collectCarrierVehicleRequiredCertErrors(certList.value, formData.trailerNo)
|
||||
if (certErrors.length) {
|
||||
ElMessage.warning(formatRequiredCertErrors(certErrors))
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
async function fetchData() {
|
||||
@@ -850,6 +901,7 @@ function handleAdd() {
|
||||
remark: ''
|
||||
})
|
||||
resetSubTables()
|
||||
certList.value = ensureVehicleCertSlots([], formData.trailerNo, formData.vehicleType as PlVehicleCertCheckType)
|
||||
loadPersonnelOptions()
|
||||
dialogVisible.value = true
|
||||
}
|
||||
@@ -898,7 +950,7 @@ async function handleEdit(row: Vehicle) {
|
||||
age: p.age ?? null
|
||||
}))
|
||||
)
|
||||
certList.value = (detail.certList || []).map(c => ({
|
||||
const mappedCerts = (detail.certList || []).map(c => ({
|
||||
_key: c.id || c.certId || `cert-${Math.random().toString(36).slice(2, 10)}`,
|
||||
certId: c.id || c.certId,
|
||||
vehiclePart: c.vehiclePart,
|
||||
@@ -908,7 +960,15 @@ async function handleEdit(row: Vehicle) {
|
||||
remindDays: c.remindDays ?? 30,
|
||||
attachmentUrls: c.attachmentUrls || ''
|
||||
}))
|
||||
certFormListRef.value?.clearUploadRefs()
|
||||
certList.value =
|
||||
detail.vehicleType === 'self' || detail.vehicleType === 'carrier'
|
||||
? ensureVehicleCertSlots(
|
||||
mappedCerts,
|
||||
formData.trailerNo,
|
||||
detail.vehicleType as PlVehicleCertCheckType
|
||||
)
|
||||
: mappedCerts
|
||||
certFormListRef.value?.refreshRequiredSlots?.()
|
||||
} finally {
|
||||
dialogLoading.value = false
|
||||
}
|
||||
@@ -993,6 +1053,22 @@ async function handleDelete(row: Vehicle) {
|
||||
fetchData()
|
||||
}
|
||||
|
||||
watch(
|
||||
() => formData.vehicleType,
|
||||
async type => {
|
||||
if (!dialogVisible.value) return
|
||||
await loadPersonnelOptions()
|
||||
if (type === 'self' || type === 'carrier') {
|
||||
certList.value = ensureVehicleCertSlots(
|
||||
certList.value,
|
||||
formData.trailerNo,
|
||||
type as PlVehicleCertCheckType
|
||||
)
|
||||
certFormListRef.value?.refreshRequiredSlots?.()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
useDbInstantSearch(
|
||||
() => ({
|
||||
q: searchForm.q,
|
||||
|
||||
Reference in new issue
Block a user