base_data.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787
  1. """运输计划管理 - 基础数据 API(承运商、车辆、人员)"""
  2. from typing import Optional
  3. from fastapi import APIRouter, Depends, HTTPException, Query, status
  4. from sqlalchemy import case, func, or_
  5. from sqlalchemy.orm import Session
  6. from app.core.database import get_db
  7. from app.core.db_auth import DbUserContext, get_db_current_user
  8. from app.services.pl_data_scope import apply_pl_scope, assert_pl_row_in_scope
  9. from app.core.tenant_context import get_tenant_no
  10. from app.core.utils import generate_uuid
  11. from app.models.pl_models import (
  12. PlCarrier,
  13. PlPersonnel,
  14. PlVehicle,
  15. PlVehicleDriver,
  16. PlVehiclePersonnel,
  17. )
  18. from app.schemas.pl import (
  19. CarrierCreate,
  20. CarrierRead,
  21. CarrierUpdate,
  22. DriverCreate,
  23. DriverRead,
  24. DriverUpdate,
  25. PersonnelCreate,
  26. PersonnelDetailRead,
  27. PersonnelRead,
  28. PersonnelUpdate,
  29. VehicleCreate,
  30. VehicleDetailRead,
  31. VehicleDriverCreate,
  32. VehicleRead,
  33. VehicleUpdate,
  34. )
  35. from app.services.pl_cert_service import (
  36. certs_to_read,
  37. list_owner_certs,
  38. nearest_expire_date,
  39. save_owner_certs,
  40. )
  41. from app.services.pl_personnel_service import (
  42. load_vehicle_personnel_rows,
  43. load_vehicle_personnel_summaries,
  44. save_vehicle_personnel,
  45. )
  46. router = APIRouter(prefix="", tags=["运输计划-基础数据"])
  47. # =============================================================================
  48. # 承运商 API
  49. # =============================================================================
  50. @router.get("/carrier", response_model=list[CarrierRead])
  51. def list_carriers(
  52. q: Optional[str] = Query(default=None, description="模糊搜索"),
  53. status: Optional[str] = Query(default=None, description="状态"),
  54. db: Session = Depends(get_db),
  55. user: DbUserContext = Depends(get_db_current_user),
  56. ) -> list[CarrierRead]:
  57. tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
  58. query = db.query(PlCarrier).filter(
  59. PlCarrier.tenant_no == tenant_no,
  60. PlCarrier.del_flag == 0,
  61. )
  62. if status:
  63. query = query.filter(PlCarrier.status == status)
  64. if q:
  65. query = query.filter(
  66. or_(
  67. func.lower(PlCarrier.carrier_name).contains(q.lower()),
  68. func.lower(PlCarrier.contact_name).contains(q.lower()),
  69. )
  70. )
  71. query = apply_pl_scope(query, PlCarrier, user, db, tenant_no)
  72. query = query.order_by(
  73. case((PlCarrier.order_num.is_(None), 1), else_=0).asc(),
  74. PlCarrier.create_time.desc(),
  75. )
  76. return query.all()
  77. @router.post("/carrier", response_model=CarrierRead, status_code=status.HTTP_201_CREATED)
  78. def create_carrier(
  79. payload: CarrierCreate,
  80. db: Session = Depends(get_db),
  81. user: DbUserContext = Depends(get_db_current_user),
  82. ) -> CarrierRead:
  83. tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
  84. carrier = PlCarrier(
  85. id=generate_uuid(),
  86. tenant_no=tenant_no,
  87. carrier_name=payload.carrier_name,
  88. contact_name=payload.contact_name,
  89. contact_phone=payload.contact_phone,
  90. status=payload.status,
  91. remark=payload.remark,
  92. create_by=user.id,
  93. create_dept_id=user.dept_id,
  94. )
  95. db.add(carrier)
  96. db.commit()
  97. db.refresh(carrier)
  98. return carrier
  99. @router.get("/carrier/{id}", response_model=CarrierRead)
  100. def get_carrier(
  101. id: str,
  102. db: Session = Depends(get_db),
  103. user: DbUserContext = Depends(get_db_current_user),
  104. ) -> CarrierRead:
  105. tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
  106. carrier = _get_carrier(db, tenant_no, id, user)
  107. return carrier
  108. @router.put("/carrier/{id}", response_model=CarrierRead)
  109. def update_carrier(
  110. id: str,
  111. payload: CarrierUpdate,
  112. db: Session = Depends(get_db),
  113. user: DbUserContext = Depends(get_db_current_user),
  114. ) -> CarrierRead:
  115. tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
  116. carrier = _get_carrier(db, tenant_no, id, user)
  117. for key, value in payload.model_dump(exclude_unset=True).items():
  118. setattr(carrier, key, value)
  119. carrier.update_by = user.id
  120. db.commit()
  121. db.refresh(carrier)
  122. return carrier
  123. @router.delete("/carrier/{id}", status_code=status.HTTP_204_NO_CONTENT)
  124. def delete_carrier(
  125. id: str,
  126. db: Session = Depends(get_db),
  127. user: DbUserContext = Depends(get_db_current_user),
  128. ) -> None:
  129. tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
  130. carrier = _get_carrier(db, tenant_no, id, user)
  131. carrier.del_flag = 1
  132. carrier.update_by = user.id
  133. db.commit()
  134. def _get_carrier(db: Session, tenant_no: str, carrier_id: str, user: DbUserContext) -> PlCarrier:
  135. carrier = (
  136. db.query(PlCarrier)
  137. .filter(
  138. PlCarrier.id == carrier_id,
  139. PlCarrier.tenant_no == tenant_no,
  140. PlCarrier.del_flag == 0,
  141. )
  142. .first()
  143. )
  144. if not carrier:
  145. raise HTTPException(status_code=404, detail="承运商不存在")
  146. assert_pl_row_in_scope(db, carrier, PlCarrier, user, tenant_no)
  147. return carrier
  148. # =============================================================================
  149. # 车辆 API
  150. # =============================================================================
  151. def _vehicle_to_read(
  152. vehicle: PlVehicle,
  153. summary: Optional[str] = None,
  154. default_driver_name: Optional[str] = None,
  155. ) -> VehicleRead:
  156. data = VehicleRead.model_validate(vehicle)
  157. if summary is not None:
  158. data.personnel_summary = summary
  159. if default_driver_name is not None:
  160. data.default_driver_name = default_driver_name
  161. return data
  162. def _vehicle_to_detail(
  163. db: Session, tenant_no: str, vehicle: PlVehicle
  164. ) -> VehicleDetailRead:
  165. summaries = load_vehicle_personnel_summaries(db, tenant_no, [vehicle.id])
  166. summary, default_name = summaries.get(vehicle.id, ("", None))
  167. personnel_list = load_vehicle_personnel_rows(db, tenant_no, vehicle.id)
  168. cert_list = certs_to_read(
  169. list_owner_certs(db, tenant_no, "vehicle", vehicle.id)
  170. )
  171. base = _vehicle_to_read(vehicle, summary, default_name)
  172. return VehicleDetailRead(
  173. **base.model_dump(by_alias=False),
  174. personnel_list=personnel_list,
  175. cert_list=cert_list,
  176. )
  177. @router.get("/vehicle", response_model=list[VehicleRead])
  178. def list_vehicles(
  179. q: Optional[str] = Query(default=None, description="模糊搜索车牌号"),
  180. vehicle_type: Optional[str] = Query(default=None, alias="vehicleType", description="车辆类型"),
  181. carrier_id: Optional[str] = Query(default=None, alias="carrierId", description="承运商ID"),
  182. vehicle_status: Optional[str] = Query(default=None, alias="vehicleStatus", description="车辆状态"),
  183. db: Session = Depends(get_db),
  184. user: DbUserContext = Depends(get_db_current_user),
  185. ) -> list[VehicleRead]:
  186. tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
  187. query = db.query(PlVehicle).filter(
  188. PlVehicle.tenant_no == tenant_no,
  189. PlVehicle.del_flag == 0,
  190. )
  191. if vehicle_type:
  192. query = query.filter(PlVehicle.vehicle_type == vehicle_type)
  193. if carrier_id:
  194. query = query.filter(PlVehicle.carrier_id == carrier_id)
  195. if vehicle_status:
  196. query = query.filter(PlVehicle.vehicle_status == vehicle_status)
  197. if q:
  198. query = query.filter(PlVehicle.license_plate.contains(q))
  199. query = apply_pl_scope(query, PlVehicle, user, db, tenant_no)
  200. query = query.order_by(
  201. case((PlVehicle.order_num.is_(None), 1), else_=0).asc(),
  202. PlVehicle.create_time.desc(),
  203. )
  204. vehicles = query.all()
  205. summaries = load_vehicle_personnel_summaries(
  206. db, tenant_no, [v.id for v in vehicles]
  207. )
  208. return [
  209. _vehicle_to_read(v, *summaries.get(v.id, ("", None)))
  210. for v in vehicles
  211. ]
  212. @router.post("/vehicle", response_model=VehicleDetailRead, status_code=status.HTTP_201_CREATED)
  213. def create_vehicle(
  214. payload: VehicleCreate,
  215. db: Session = Depends(get_db),
  216. user: DbUserContext = Depends(get_db_current_user),
  217. ) -> VehicleDetailRead:
  218. tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
  219. existing = (
  220. db.query(PlVehicle)
  221. .filter(
  222. PlVehicle.license_plate == payload.license_plate,
  223. PlVehicle.del_flag == 0,
  224. )
  225. .first()
  226. )
  227. if existing:
  228. raise HTTPException(status_code=400, detail="车牌号已存在")
  229. vehicle = PlVehicle(
  230. id=generate_uuid(),
  231. tenant_no=tenant_no,
  232. vehicle_type=payload.vehicle_type,
  233. carrier_id=payload.carrier_id,
  234. carrier_name=payload.carrier_name,
  235. license_plate=payload.license_plate,
  236. trailer_no=payload.trailer_no,
  237. load_capacity=payload.load_capacity,
  238. vehicle_status=payload.vehicle_status,
  239. remark=payload.remark,
  240. create_by=user.id,
  241. create_dept_id=user.dept_id,
  242. )
  243. db.add(vehicle)
  244. db.flush()
  245. try:
  246. save_vehicle_personnel(
  247. db,
  248. vehicle.id,
  249. payload.personnel_list,
  250. tenant_no,
  251. user_id=user.id,
  252. dept_id=user.dept_id,
  253. )
  254. save_owner_certs(
  255. db,
  256. "vehicle",
  257. vehicle.id,
  258. payload.cert_list,
  259. tenant_no,
  260. user_id=user.id,
  261. dept_id=user.dept_id,
  262. )
  263. db.commit()
  264. except HTTPException:
  265. db.rollback()
  266. raise
  267. except Exception:
  268. db.rollback()
  269. raise
  270. db.refresh(vehicle)
  271. return _vehicle_to_detail(db, tenant_no, vehicle)
  272. @router.get("/vehicle/{id}", response_model=VehicleDetailRead)
  273. def get_vehicle(
  274. id: str,
  275. db: Session = Depends(get_db),
  276. user: DbUserContext = Depends(get_db_current_user),
  277. ) -> VehicleDetailRead:
  278. tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
  279. vehicle = _get_vehicle(db, tenant_no, id, user)
  280. return _vehicle_to_detail(db, tenant_no, vehicle)
  281. @router.put("/vehicle/{id}", response_model=VehicleDetailRead)
  282. def update_vehicle(
  283. id: str,
  284. payload: VehicleUpdate,
  285. db: Session = Depends(get_db),
  286. user: DbUserContext = Depends(get_db_current_user),
  287. ) -> VehicleDetailRead:
  288. tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
  289. vehicle = _get_vehicle(db, tenant_no, id, user)
  290. personnel_list = payload.personnel_list
  291. cert_list = payload.cert_list
  292. data = payload.model_dump(
  293. exclude_unset=True, exclude={"personnel_list", "cert_list"}
  294. )
  295. for key, value in data.items():
  296. setattr(vehicle, key, value)
  297. vehicle.update_by = user.id
  298. try:
  299. if personnel_list is not None:
  300. save_vehicle_personnel(
  301. db,
  302. vehicle.id,
  303. personnel_list,
  304. tenant_no,
  305. user_id=user.id,
  306. dept_id=user.dept_id,
  307. )
  308. if cert_list is not None:
  309. save_owner_certs(
  310. db,
  311. "vehicle",
  312. vehicle.id,
  313. cert_list,
  314. tenant_no,
  315. user_id=user.id,
  316. dept_id=user.dept_id,
  317. )
  318. db.commit()
  319. except HTTPException:
  320. db.rollback()
  321. raise
  322. except Exception:
  323. db.rollback()
  324. raise
  325. db.refresh(vehicle)
  326. return _vehicle_to_detail(db, tenant_no, vehicle)
  327. @router.delete("/vehicle/{id}", status_code=status.HTTP_204_NO_CONTENT)
  328. def delete_vehicle(
  329. id: str,
  330. db: Session = Depends(get_db),
  331. user: DbUserContext = Depends(get_db_current_user),
  332. ) -> None:
  333. tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
  334. vehicle = _get_vehicle(db, tenant_no, id, user)
  335. vehicle.del_flag = 1
  336. vehicle.update_by = user.id
  337. db.commit()
  338. def _get_vehicle(db: Session, tenant_no: str, vehicle_id: str, user: DbUserContext) -> PlVehicle:
  339. vehicle = (
  340. db.query(PlVehicle)
  341. .filter(
  342. PlVehicle.id == vehicle_id,
  343. PlVehicle.tenant_no == tenant_no,
  344. PlVehicle.del_flag == 0,
  345. )
  346. .first()
  347. )
  348. if not vehicle:
  349. raise HTTPException(status_code=404, detail="车辆不存在")
  350. assert_pl_row_in_scope(db, vehicle, PlVehicle, user, tenant_no)
  351. return vehicle
  352. # =============================================================================
  353. # 人员 API
  354. # =============================================================================
  355. def _personnel_to_read(
  356. db: Session, tenant_no: str, personnel: PlPersonnel
  357. ) -> PersonnelRead:
  358. nearest = nearest_expire_date(db, tenant_no, "personnel", personnel.id)
  359. data = PersonnelRead.model_validate(personnel)
  360. data.nearest_expire_date = nearest
  361. return data
  362. def _personnel_to_detail(
  363. db: Session, tenant_no: str, personnel: PlPersonnel
  364. ) -> PersonnelDetailRead:
  365. base = _personnel_to_read(db, tenant_no, personnel)
  366. cert_list = certs_to_read(
  367. list_owner_certs(db, tenant_no, "personnel", personnel.id)
  368. )
  369. return PersonnelDetailRead(
  370. **base.model_dump(by_alias=False),
  371. cert_list=cert_list,
  372. )
  373. @router.get("/personnel", response_model=list[PersonnelRead])
  374. def list_personnel(
  375. q: Optional[str] = Query(default=None, description="模糊搜索姓名/手机号"),
  376. status: Optional[str] = Query(default=None, description="状态"),
  377. db: Session = Depends(get_db),
  378. user: DbUserContext = Depends(get_db_current_user),
  379. ) -> list[PersonnelRead]:
  380. tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
  381. query = db.query(PlPersonnel).filter(
  382. PlPersonnel.tenant_no == tenant_no,
  383. PlPersonnel.del_flag == 0,
  384. )
  385. if status:
  386. query = query.filter(PlPersonnel.status == status)
  387. if q:
  388. query = query.filter(
  389. or_(
  390. func.lower(PlPersonnel.personnel_name).contains(q.lower()),
  391. PlPersonnel.phone_no.contains(q),
  392. )
  393. )
  394. query = apply_pl_scope(query, PlPersonnel, user, db, tenant_no)
  395. query = query.order_by(
  396. case((PlPersonnel.order_num.is_(None), 1), else_=0).asc(),
  397. PlPersonnel.create_time.desc(),
  398. )
  399. rows = query.all()
  400. return [_personnel_to_read(db, tenant_no, p) for p in rows]
  401. @router.post("/personnel", response_model=PersonnelDetailRead, status_code=status.HTTP_201_CREATED)
  402. def create_personnel(
  403. payload: PersonnelCreate,
  404. db: Session = Depends(get_db),
  405. user: DbUserContext = Depends(get_db_current_user),
  406. ) -> PersonnelDetailRead:
  407. tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
  408. personnel = PlPersonnel(
  409. id=generate_uuid(),
  410. tenant_no=tenant_no,
  411. personnel_name=payload.personnel_name,
  412. phone_no=payload.phone_no,
  413. age=payload.age,
  414. id_card_no=payload.id_card_no,
  415. status=payload.status,
  416. remark=payload.remark,
  417. create_by=user.id,
  418. create_dept_id=user.dept_id,
  419. )
  420. db.add(personnel)
  421. db.flush()
  422. try:
  423. save_owner_certs(
  424. db,
  425. "personnel",
  426. personnel.id,
  427. payload.cert_list,
  428. tenant_no,
  429. user_id=user.id,
  430. dept_id=user.dept_id,
  431. )
  432. db.commit()
  433. except Exception:
  434. db.rollback()
  435. raise
  436. db.refresh(personnel)
  437. return _personnel_to_detail(db, tenant_no, personnel)
  438. @router.get("/personnel/{id}", response_model=PersonnelDetailRead)
  439. def get_personnel(
  440. id: str,
  441. db: Session = Depends(get_db),
  442. user: DbUserContext = Depends(get_db_current_user),
  443. ) -> PersonnelDetailRead:
  444. tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
  445. personnel = _get_personnel(db, tenant_no, id, user)
  446. return _personnel_to_detail(db, tenant_no, personnel)
  447. @router.put("/personnel/{id}", response_model=PersonnelDetailRead)
  448. def update_personnel(
  449. id: str,
  450. payload: PersonnelUpdate,
  451. db: Session = Depends(get_db),
  452. user: DbUserContext = Depends(get_db_current_user),
  453. ) -> PersonnelDetailRead:
  454. tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
  455. personnel = _get_personnel(db, tenant_no, id, user)
  456. cert_list = payload.cert_list
  457. data = payload.model_dump(exclude_unset=True, exclude={"cert_list"})
  458. for key, value in data.items():
  459. setattr(personnel, key, value)
  460. personnel.update_by = user.id
  461. try:
  462. if cert_list is not None:
  463. save_owner_certs(
  464. db,
  465. "personnel",
  466. personnel.id,
  467. cert_list,
  468. tenant_no,
  469. user_id=user.id,
  470. dept_id=user.dept_id,
  471. )
  472. db.commit()
  473. except Exception:
  474. db.rollback()
  475. raise
  476. db.refresh(personnel)
  477. return _personnel_to_detail(db, tenant_no, personnel)
  478. @router.delete("/personnel/{id}", status_code=status.HTTP_204_NO_CONTENT)
  479. def delete_personnel(
  480. id: str,
  481. db: Session = Depends(get_db),
  482. user: DbUserContext = Depends(get_db_current_user),
  483. ) -> None:
  484. tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
  485. personnel = _get_personnel(db, tenant_no, id, user)
  486. personnel.del_flag = 1
  487. personnel.update_by = user.id
  488. db.commit()
  489. def _get_personnel(db: Session, tenant_no: str, personnel_id: str, user: DbUserContext) -> PlPersonnel:
  490. personnel = (
  491. db.query(PlPersonnel)
  492. .filter(
  493. PlPersonnel.id == personnel_id,
  494. PlPersonnel.tenant_no == tenant_no,
  495. PlPersonnel.del_flag == 0,
  496. )
  497. .first()
  498. )
  499. if not personnel:
  500. raise HTTPException(status_code=404, detail="人员不存在")
  501. assert_pl_row_in_scope(db, personnel, PlPersonnel, user, tenant_no)
  502. return personnel
  503. # =============================================================================
  504. # 司机 API(兼容别名,内部使用 pl_personnel)
  505. # =============================================================================
  506. def _personnel_to_driver_read(personnel: PlPersonnel) -> DriverRead:
  507. return DriverRead(
  508. id=personnel.id,
  509. driver_name=personnel.personnel_name,
  510. phone_no=personnel.phone_no,
  511. id_card_no=personnel.id_card_no,
  512. tenant_no=personnel.tenant_no,
  513. order_num=personnel.order_num,
  514. remark=personnel.remark,
  515. create_by=personnel.create_by,
  516. create_dept_id=personnel.create_dept_id,
  517. create_time=personnel.create_time,
  518. update_by=personnel.update_by,
  519. update_time=personnel.update_time,
  520. del_flag=personnel.del_flag,
  521. )
  522. @router.get("/driver", response_model=list[DriverRead])
  523. def list_drivers(
  524. q: Optional[str] = Query(default=None, description="模糊搜索姓名/手机号"),
  525. db: Session = Depends(get_db),
  526. user: DbUserContext = Depends(get_db_current_user),
  527. ) -> list[DriverRead]:
  528. tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
  529. query = db.query(PlPersonnel).filter(
  530. PlPersonnel.tenant_no == tenant_no,
  531. PlPersonnel.del_flag == 0,
  532. )
  533. if q:
  534. query = query.filter(
  535. or_(
  536. func.lower(PlPersonnel.personnel_name).contains(q.lower()),
  537. PlPersonnel.phone_no.contains(q),
  538. )
  539. )
  540. query = apply_pl_scope(query, PlPersonnel, user, db, tenant_no)
  541. query = query.order_by(
  542. case((PlPersonnel.order_num.is_(None), 1), else_=0).asc(),
  543. PlPersonnel.create_time.desc(),
  544. )
  545. return [_personnel_to_driver_read(p) for p in query.all()]
  546. @router.post("/driver", response_model=DriverRead, status_code=status.HTTP_201_CREATED)
  547. def create_driver(
  548. payload: DriverCreate,
  549. db: Session = Depends(get_db),
  550. user: DbUserContext = Depends(get_db_current_user),
  551. ) -> DriverRead:
  552. tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
  553. personnel = PlPersonnel(
  554. id=generate_uuid(),
  555. tenant_no=tenant_no,
  556. personnel_name=payload.driver_name,
  557. phone_no=payload.phone_no,
  558. id_card_no=payload.id_card_no,
  559. status="1",
  560. remark=payload.remark,
  561. create_by=user.id,
  562. create_dept_id=user.dept_id,
  563. )
  564. db.add(personnel)
  565. db.commit()
  566. db.refresh(personnel)
  567. return _personnel_to_driver_read(personnel)
  568. @router.get("/driver/{id}", response_model=DriverRead)
  569. def get_driver(
  570. id: str,
  571. db: Session = Depends(get_db),
  572. user: DbUserContext = Depends(get_db_current_user),
  573. ) -> DriverRead:
  574. tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
  575. personnel = _get_personnel(db, tenant_no, id, user)
  576. return _personnel_to_driver_read(personnel)
  577. @router.put("/driver/{id}", response_model=DriverRead)
  578. def update_driver(
  579. id: str,
  580. payload: DriverUpdate,
  581. db: Session = Depends(get_db),
  582. user: DbUserContext = Depends(get_db_current_user),
  583. ) -> DriverRead:
  584. tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
  585. personnel = _get_personnel(db, tenant_no, id, user)
  586. if payload.driver_name is not None:
  587. personnel.personnel_name = payload.driver_name
  588. if payload.phone_no is not None:
  589. personnel.phone_no = payload.phone_no
  590. if payload.id_card_no is not None:
  591. personnel.id_card_no = payload.id_card_no
  592. if payload.remark is not None:
  593. personnel.remark = payload.remark
  594. personnel.update_by = user.id
  595. db.commit()
  596. db.refresh(personnel)
  597. return _personnel_to_driver_read(personnel)
  598. @router.delete("/driver/{id}", status_code=status.HTTP_204_NO_CONTENT)
  599. def delete_driver(
  600. id: str,
  601. db: Session = Depends(get_db),
  602. user: DbUserContext = Depends(get_db_current_user),
  603. ) -> None:
  604. tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
  605. personnel = _get_personnel(db, tenant_no, id, user)
  606. personnel.del_flag = 1
  607. personnel.update_by = user.id
  608. db.commit()
  609. # =============================================================================
  610. # 车辆-司机绑定 API(兼容,写入 pl_vehicle_personnel)
  611. # =============================================================================
  612. @router.post("/vehicle-driver", status_code=status.HTTP_201_CREATED)
  613. def bind_vehicle_driver(
  614. payload: VehicleDriverCreate,
  615. db: Session = Depends(get_db),
  616. user: DbUserContext = Depends(get_db_current_user),
  617. ) -> dict:
  618. tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
  619. _get_vehicle(db, tenant_no, payload.vehicle_id, user)
  620. _get_personnel(db, tenant_no, payload.driver_id, user)
  621. if payload.is_default == 1:
  622. db.query(PlVehiclePersonnel).filter(
  623. PlVehiclePersonnel.vehicle_id == payload.vehicle_id,
  624. PlVehiclePersonnel.role == "driver",
  625. PlVehiclePersonnel.del_flag == 0,
  626. ).update({"is_default": 0})
  627. vd = PlVehiclePersonnel(
  628. id=generate_uuid(),
  629. tenant_no=tenant_no,
  630. vehicle_id=payload.vehicle_id,
  631. personnel_id=payload.driver_id,
  632. role="driver",
  633. is_default=payload.is_default,
  634. create_by=user.id,
  635. create_dept_id=user.dept_id,
  636. )
  637. db.add(vd)
  638. db.commit()
  639. return {"id": vd.id}
  640. @router.delete("/vehicle-driver/{id}", status_code=status.HTTP_204_NO_CONTENT)
  641. def unbind_vehicle_driver(
  642. id: str,
  643. db: Session = Depends(get_db),
  644. user: DbUserContext = Depends(get_db_current_user),
  645. ) -> None:
  646. tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
  647. vd = (
  648. db.query(PlVehiclePersonnel)
  649. .filter(
  650. PlVehiclePersonnel.id == id,
  651. PlVehiclePersonnel.tenant_no == tenant_no,
  652. PlVehiclePersonnel.del_flag == 0,
  653. )
  654. .first()
  655. )
  656. if not vd:
  657. vd = (
  658. db.query(PlVehicleDriver)
  659. .filter(
  660. PlVehicleDriver.id == id,
  661. PlVehicleDriver.tenant_no == tenant_no,
  662. PlVehicleDriver.del_flag == 0,
  663. )
  664. .first()
  665. )
  666. if vd:
  667. if isinstance(vd, PlVehiclePersonnel):
  668. assert_pl_row_in_scope(db, vd, PlVehiclePersonnel, user, tenant_no)
  669. else:
  670. assert_pl_row_in_scope(db, vd, PlVehicleDriver, user, tenant_no)
  671. vd.del_flag = 1
  672. db.commit()