From d8df5c47e2f67b4d6a854c95f95bf4d141df929b Mon Sep 17 00:00:00 2001 From: zlwu <704541015@qq.com> Date: Tue, 29 Sep 2026 12:08:48 +0800 Subject: [PATCH] feat(api): allow withdraw of in-approval vehicle recharge applies Applicants can return an in-approval apply to draft while keeping prior approval rows. Co-authored-by: Cursor --- backend/app/api/pl/vehicle_recharge.py | 13 ++++++ .../services/pl_vehicle_recharge_service.py | 46 +++++++++++++++++-- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/backend/app/api/pl/vehicle_recharge.py b/backend/app/api/pl/vehicle_recharge.py index 87c25d254..29b7c0565 100644 --- a/backend/app/api/pl/vehicle_recharge.py +++ b/backend/app/api/pl/vehicle_recharge.py @@ -336,6 +336,19 @@ def cancel_apply( return svc.cancel_apply(db, tenant_no, user, id, mine_vehicles=mine_vehicles) +@apply_router.post("/{id}/withdraw", response_model=VehicleRechargeApplyRead) +def withdraw_apply( + id: str, + mine_vehicles: bool = Query(default=False, alias="mineVehicles"), + db: Session = Depends(get_db), + user: DbUserContext = Depends(get_db_current_user), +) -> VehicleRechargeApplyRead: + tenant_no = get_tenant_no(db, user.to_tenant_user_dict()) + return svc.withdraw_apply( + db, tenant_no, user, id, mine_vehicles=mine_vehicles + ) + + @apply_router.delete("/{ids}", status_code=status.HTTP_204_NO_CONTENT) def delete_applies( ids: str, diff --git a/backend/app/services/pl_vehicle_recharge_service.py b/backend/app/services/pl_vehicle_recharge_service.py index 797f09784..ab6d97f93 100644 --- a/backend/app/services/pl_vehicle_recharge_service.py +++ b/backend/app/services/pl_vehicle_recharge_service.py @@ -30,7 +30,10 @@ from app.schemas.pl.vehicle_recharge import ( VehicleRechargeRecordUpdate, ) from app.services.pl_data_scope import apply_pl_scope, assert_pl_row_in_scope -from app.services.pl_vehicle_mine_vehicles import license_plates_for_user_personnel +from app.services.pl_vehicle_mine_vehicles import ( + license_plates_for_user_personnel, + should_restrict_mine_vehicles, +) _EDITABLE_STATUSES = frozenset({"draft", "rejected"}) _BIZ_TYPE_VEHICLE_RECHARGE = "vehicle_recharge" @@ -135,11 +138,13 @@ def _get_apply_row( ) if not row: raise HTTPException(status_code=404, detail="申请单不存在") - if mine_vehicles: + if should_restrict_mine_vehicles( + db, tenant_no, user.id, mine_vehicles=mine_vehicles + ): plates = license_plates_for_user_personnel(db, tenant_no, user.id) if row.license_plate not in plates: raise HTTPException(status_code=403, detail="无权查看该申请") - else: + elif not mine_vehicles: assert_pl_row_in_scope(db, row, PlVehicleRechargeApply, user, tenant_no) return row @@ -530,11 +535,16 @@ def list_applies( if ids: query = query.filter(PlVehicleRechargeApply.id.in_(ids)) - if mine_vehicles: + if should_restrict_mine_vehicles( + db, tenant_no, user.id, mine_vehicles=mine_vehicles + ): plates = license_plates_for_user_personnel(db, tenant_no, user.id) if not plates: return [], 0 query = query.filter(PlVehicleRechargeApply.license_plate.in_(plates)) + elif mine_vehicles: + # 管理员 + mineVehicles:本租户全量,跳过挂靠与数据范围 + pass else: query = apply_pl_scope(query, PlVehicleRechargeApply, user, db, tenant_no) total = query.count() @@ -567,6 +577,10 @@ def get_apply( def _assert_mine_vehicle_plate( db: Session, tenant_no: str, user: DbUserContext, plate: str ) -> None: + if not should_restrict_mine_vehicles( + db, tenant_no, user.id, mine_vehicles=True + ): + return plates = license_plates_for_user_personnel(db, tenant_no, user.id) if (plate or "").strip() not in plates: raise HTTPException(status_code=403, detail="只能选择本人车辆") @@ -684,6 +698,30 @@ def cancel_apply( return apply_to_read(row, db) +def withdraw_apply( + db: Session, + tenant_no: str, + user: DbUserContext, + apply_id: str, + *, + mine_vehicles: bool = False, +) -> VehicleRechargeApplyRead: + row = _get_apply_row( + db, tenant_no, apply_id, user, mine_vehicles=mine_vehicles + ) + if (row.applicant_id or "").strip() != (user.id or "").strip(): + raise HTTPException(status_code=403, detail="仅申请人可撤回") + if row.status != "in_approval": + raise HTTPException(status_code=400, detail="仅审批中的申请可撤回") + row.status = "draft" + row.current_node = None + row.update_by = user.id + row.update_time = datetime.now() + db.commit() + db.refresh(row) + return apply_to_read(row, db, include_detail=True) + + def submit_apply( db: Session, tenant_no: str,