| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 |
- """运输计划 - 证书 CRUD 与到期扫描"""
- from __future__ import annotations
- from datetime import date
- from typing import Optional
- from sqlalchemy.orm import Session
- from app.core.utils import generate_uuid
- from app.models.pl_models import PlCert, PlDocumentReminder, PlPersonnel, PlVehicle
- from app.schemas.pl.base_data import CertRead, CertRow
- def list_owner_certs(
- db: Session, tenant_no: str, owner_type: str, owner_id: str
- ) -> list[PlCert]:
- return (
- db.query(PlCert)
- .filter(
- PlCert.tenant_no == tenant_no,
- PlCert.owner_type == owner_type,
- PlCert.owner_id == owner_id,
- PlCert.del_flag == 0,
- )
- .order_by(PlCert.order_num.asc(), PlCert.create_time.asc())
- .all()
- )
- def list_owner_certs_batch(
- db: Session, tenant_no: str, owner_type: str, owner_ids: list[str]
- ) -> dict[str, list[PlCert]]:
- if not owner_ids:
- return {}
- rows = (
- db.query(PlCert)
- .filter(
- PlCert.tenant_no == tenant_no,
- PlCert.owner_type == owner_type,
- PlCert.owner_id.in_(owner_ids),
- PlCert.del_flag == 0,
- )
- .order_by(PlCert.order_num.asc(), PlCert.create_time.asc())
- .all()
- )
- grouped: dict[str, list[PlCert]] = {}
- for cert in rows:
- grouped.setdefault(cert.owner_id, []).append(cert)
- return grouped
- def certs_to_read(certs: list[PlCert]) -> list[CertRead]:
- return [
- CertRead(
- id=c.id,
- cert_id=c.id,
- vehicle_part=c.vehicle_part,
- cert_name=c.cert_name,
- cert_no=c.cert_no,
- expire_date=c.expire_date,
- remind_days=c.remind_days,
- attachment_urls=c.attachment_urls,
- owner_type=c.owner_type,
- owner_id=c.owner_id,
- status=c.status,
- tenant_no=c.tenant_no,
- create_time=c.create_time,
- update_time=c.update_time,
- )
- for c in certs
- ]
- def _normalize_cert_row(row: CertRow | dict) -> CertRow:
- if isinstance(row, CertRow):
- return row
- return CertRow.model_validate(row)
- def save_owner_certs(
- db: Session,
- owner_type: str,
- owner_id: str,
- cert_rows: list[CertRow | dict],
- tenant_no: str,
- *,
- user_id: str,
- dept_id: str,
- ) -> None:
- """按 cert_id upsert;未出现在列表中的旧证软删。"""
- existing = list_owner_certs(db, tenant_no, owner_type, owner_id)
- existing_map = {c.id: c for c in existing}
- keep_ids: set[str] = set()
- for idx, row in enumerate(cert_rows):
- row = _normalize_cert_row(row)
- cert_id = row.cert_id
- if cert_id and cert_id in existing_map:
- cert = existing_map[cert_id]
- cert.vehicle_part = row.vehicle_part
- cert.cert_name = row.cert_name
- cert.cert_no = row.cert_no
- cert.expire_date = row.expire_date
- cert.remind_days = row.remind_days
- cert.attachment_urls = row.attachment_urls
- cert.order_num = idx
- cert.update_by = user_id
- keep_ids.add(cert.id)
- else:
- cert = PlCert(
- id=generate_uuid(),
- tenant_no=tenant_no,
- owner_type=owner_type,
- owner_id=owner_id,
- vehicle_part=row.vehicle_part,
- cert_name=row.cert_name,
- cert_no=row.cert_no,
- expire_date=row.expire_date,
- remind_days=row.remind_days,
- attachment_urls=row.attachment_urls,
- status="valid",
- order_num=idx,
- create_by=user_id,
- create_dept_id=dept_id,
- )
- db.add(cert)
- db.flush()
- keep_ids.add(cert.id)
- for cert in existing:
- if cert.id not in keep_ids:
- cert.del_flag = 1
- cert.update_by = user_id
- scan_cert_expiry(db, tenant_no)
- def nearest_expire_date(
- db: Session, tenant_no: str, owner_type: str, owner_id: str
- ) -> Optional[date]:
- row = (
- db.query(PlCert.expire_date)
- .filter(
- PlCert.tenant_no == tenant_no,
- PlCert.owner_type == owner_type,
- PlCert.owner_id == owner_id,
- PlCert.del_flag == 0,
- PlCert.expire_date.isnot(None),
- )
- .order_by(PlCert.expire_date.asc())
- .first()
- )
- return row[0] if row else None
- def _resolve_owner_label(
- db: Session, tenant_no: str, owner_type: str, owner_id: str
- ) -> str:
- if owner_type == "vehicle":
- vehicle = (
- db.query(PlVehicle)
- .filter(
- PlVehicle.id == owner_id,
- PlVehicle.tenant_no == tenant_no,
- PlVehicle.del_flag == 0,
- )
- .first()
- )
- return vehicle.license_plate if vehicle else owner_id
- personnel = (
- db.query(PlPersonnel)
- .filter(
- PlPersonnel.id == owner_id,
- PlPersonnel.tenant_no == tenant_no,
- PlPersonnel.del_flag == 0,
- )
- .first()
- )
- return personnel.personnel_name if personnel else owner_id
- def scan_cert_expiry(db: Session, tenant_no: Optional[str] = None) -> int:
- """扫描 pl_cert 生成到期提醒,返回新建条数。"""
- today = date.today()
- query = db.query(PlCert).filter(
- PlCert.del_flag == 0,
- PlCert.expire_date.isnot(None),
- PlCert.status == "valid",
- )
- if tenant_no:
- query = query.filter(PlCert.tenant_no == tenant_no)
- created_count = 0
- for cert in query.all():
- days_remaining = (cert.expire_date - today).days
- # 仅跳过尚未进入提醒窗口的证书;已过期(days_remaining < 0)仍需提醒
- if days_remaining > cert.remind_days:
- continue
- owner_label = _resolve_owner_label(
- db, cert.tenant_no, cert.owner_type, cert.owner_id
- )
- driver_id = cert.owner_id if cert.owner_type == "personnel" else None
- driver_name = owner_label if cert.owner_type == "personnel" else None
- existing = (
- db.query(PlDocumentReminder)
- .filter(
- PlDocumentReminder.cert_id == cert.id,
- PlDocumentReminder.status == "pending",
- PlDocumentReminder.del_flag == 0,
- )
- .first()
- )
- if existing:
- existing.owner_type = cert.owner_type
- existing.owner_id = cert.owner_id
- existing.owner_label = owner_label
- existing.driver_id = driver_id
- existing.driver_name = driver_name
- existing.document_type = cert.cert_name
- existing.document_no = cert.cert_no
- existing.expire_date = cert.expire_date
- existing.days_remaining = days_remaining
- continue
- reminder = PlDocumentReminder(
- id=generate_uuid(),
- tenant_no=cert.tenant_no,
- cert_id=cert.id,
- owner_type=cert.owner_type,
- owner_id=cert.owner_id,
- owner_label=owner_label,
- driver_id=driver_id,
- driver_name=driver_name,
- document_type=cert.cert_name,
- document_no=cert.cert_no,
- expire_date=cert.expire_date,
- days_remaining=days_remaining,
- status="pending",
- create_by=cert.create_by or "system",
- create_dept_id=cert.create_dept_id or "",
- )
- db.add(reminder)
- created_count += 1
- return created_count
|