| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872 |
- """配送任务CRUD"""
- import json
- from typing import Optional, List
- from datetime import datetime, timedelta
- from decimal import Decimal
- from sqlalchemy.orm import Session
- from sqlalchemy import and_, func, or_, select, exists
- from app.models.models import BizDeliveryTask, BizDeliveryTaskItem, BizDeliveryTaskHis, BizOrder
- from app.crud.base import CRUDBase
- from app.core.utils import generate_id
- class DeliveryTaskCRUD(CRUDBase[BizDeliveryTask]):
- """配送任务CRUD操作"""
- @staticmethod
- def _sync_legacy_status_from_dual(task: BizDeliveryTask) -> None:
- """根据双状态回写兼容字段 status。"""
- ds = (getattr(task, "deliverer_status", None) or "").strip()
- ss = (getattr(task, "supplier_prepare_status", None) or "").strip()
- if ds == "delivered":
- task.status = "completed"
- elif ds == "delivering":
- task.status = "delivering"
- elif ss == "stocking":
- task.status = "supplier_stocking"
- elif ss == "prepared":
- task.status = "supplier_ready"
- else:
- task.status = "assigned" if task.deliver_id else "pending"
- def get_by_order(self, db: Session, order_id: str) -> Optional[BizDeliveryTask]:
- """根据订单ID获取配送任务"""
- return db.query(BizDeliveryTask).filter(
- and_(
- BizDeliveryTask.order_id == order_id,
- BizDeliveryTask.is_del == 0
- )
- ).first()
- def get_multi_with_filters(
- self,
- db: Session,
- region_id: str = None,
- supplier_id: str = None,
- status: str = None,
- supplier_prepare_status: str = None,
- order_no: str = None,
- deliver_id: str = None,
- keyword: str = None,
- skip: int = 0,
- limit: int = 10
- ) -> tuple[List[BizDeliveryTask], int]:
- """多条件查询配送任务"""
- query = db.query(BizDeliveryTask).filter(BizDeliveryTask.is_del == 0)
- if region_id:
- query = query.filter(BizDeliveryTask.region_id == region_id)
- if supplier_id:
- query = query.filter(BizDeliveryTask.supplier_id == supplier_id)
- if supplier_prepare_status:
- parts = [s.strip() for s in supplier_prepare_status.split(",") if s.strip()]
- if parts:
- if len(parts) > 1:
- query = query.filter(BizDeliveryTask.supplier_prepare_status.in_(parts))
- else:
- p0 = parts[0]
- if p0 == "pending_prepare":
- query = query.filter(
- or_(
- BizDeliveryTask.supplier_prepare_status == "pending_prepare",
- BizDeliveryTask.supplier_prepare_status.is_(None),
- )
- )
- else:
- query = query.filter(BizDeliveryTask.supplier_prepare_status == p0)
- elif status:
- parts = [s.strip() for s in status.split(",") if s.strip()]
- if parts:
- if len(parts) > 1:
- query = query.filter(BizDeliveryTask.status.in_(parts))
- else:
- query = query.filter(BizDeliveryTask.status == parts[0])
- if order_no:
- query = query.filter(BizDeliveryTask.task_no.like(f"%{order_no}%"))
- if deliver_id:
- query = query.filter(BizDeliveryTask.deliver_id == deliver_id)
- if keyword:
- kw = (keyword or "").strip()
- if kw:
- pattern = f"%{kw}%"
- order_match = exists(
- select(1).select_from(BizOrder).where(
- and_(
- BizOrder.id == BizDeliveryTask.order_id,
- BizOrder.is_del == 0,
- or_(
- BizOrder.order_no.like(pattern),
- BizOrder.contact_person.like(pattern),
- BizOrder.contact_phone.like(pattern),
- ),
- )
- )
- )
- query = query.filter(
- or_(
- BizDeliveryTask.task_no.like(pattern),
- BizDeliveryTask.order_id.like(pattern),
- order_match,
- )
- )
- total = query.count()
- tasks = query.order_by(BizDeliveryTask.create_at.desc()).offset(skip).limit(limit).all()
- return tasks, total
- def get_by_deliver(
- self,
- db: Session,
- deliver_id: str,
- status: str = None,
- deliverer_status: str = None,
- skip: int = 0,
- limit: int = 10
- ) -> tuple[List[BizDeliveryTask], int]:
- """根据配送员获取配送任务。优先 deliverer_status(小程序);否则按兼容字段 status。"""
- query = db.query(BizDeliveryTask).filter(
- and_(
- BizDeliveryTask.deliver_id == deliver_id,
- BizDeliveryTask.is_del == 0,
- BizDeliveryTask.status != "cancelled",
- )
- )
- if deliverer_status:
- parts = [s.strip() for s in deliverer_status.split(",") if s.strip()]
- if parts:
- if len(parts) > 1:
- query = query.filter(BizDeliveryTask.deliverer_status.in_(parts))
- elif parts[0] == "pending_pickup":
- query = query.filter(
- or_(
- BizDeliveryTask.deliverer_status == "pending_pickup",
- BizDeliveryTask.deliverer_status.is_(None),
- )
- )
- else:
- query = query.filter(BizDeliveryTask.deliverer_status == parts[0])
- elif status:
- parts = [s.strip() for s in status.split(",") if s.strip()]
- if parts:
- if len(parts) > 1:
- query = query.filter(BizDeliveryTask.status.in_(parts))
- else:
- query = query.filter(BizDeliveryTask.status == parts[0])
- total = query.count()
- tasks = query.order_by(BizDeliveryTask.create_at.desc()).offset(skip).limit(limit).all()
- return tasks, total
- def get_by_supplier(
- self,
- db: Session,
- supplier_id: str,
- status: str = None,
- skip: int = 0,
- limit: int = 10
- ) -> tuple[List[BizDeliveryTask], int]:
- """根据供应商获取配送任务"""
- query = db.query(BizDeliveryTask).filter(
- and_(
- BizDeliveryTask.supplier_id == supplier_id,
- BizDeliveryTask.is_del == 0
- )
- )
- if status:
- query = query.filter(BizDeliveryTask.status == status)
- total = query.count()
- tasks = query.order_by(BizDeliveryTask.create_at.desc()).offset(skip).limit(limit).all()
- return tasks, total
- def get_pending_list(
- self,
- db: Session,
- region_id: str = None,
- skip: int = 0,
- limit: int = 10
- ) -> tuple[List[BizDeliveryTask], int]:
- """获取待分配配送的任务"""
- query = db.query(BizDeliveryTask).filter(
- and_(
- BizDeliveryTask.status == "pending",
- BizDeliveryTask.is_del == 0
- )
- )
- if region_id:
- query = query.filter(BizDeliveryTask.region_id == region_id)
- total = query.count()
- tasks = query.order_by(BizDeliveryTask.create_at.asc()).offset(skip).limit(limit).all()
- return tasks, total
- def assign_deliver(
- self,
- db: Session,
- order_id: str,
- deliver_id: str,
- operator_id: str = None
- ) -> Optional[BizDeliveryTask]:
- """分配配送员"""
- task = self.get_by_order(db, order_id)
- if not task:
- return None
- if task.status != "pending":
- return None
- task.deliver_id = deliver_id
- task.deliverer_status = "pending_pickup"
- task.supplier_prepare_status = "pending_prepare"
- self._sync_legacy_status_from_dual(task)
- task.update_at = datetime.now()
- db.commit()
- db.refresh(task)
- return task
- def assign_deliver_batch(
- self,
- db: Session,
- assignments: List[dict]
- ) -> List[BizDeliveryTask]:
- """批量分配配送员"""
- results = []
- for assignment in assignments:
- order_id = assignment.get("orderId")
- deliver_id = assignment.get("deliverId")
- task = self.assign_deliver(db, order_id, deliver_id)
- if task:
- results.append(task)
- return results
- def update_status(
- self,
- db: Session,
- task_id: str,
- new_status: str,
- operator_id: str = None,
- remark: str = None,
- image_urls: List[str] = None,
- operator_real_name: str = None,
- operator_role: str = "deliver",
- ) -> Optional[BizDeliveryTask]:
- """更新配送任务状态"""
- task = self.get(db, task_id)
- if not task:
- return None
- old_status = task.status
- task.status = new_status
- task.update_at = datetime.now()
- if new_status == "picking":
- # 配送员已取货:进入配送中(勿再保留 pending_pickup,否则列表/标题仍显示「待取货」)
- task.deliverer_status = "delivering"
- task.supplier_prepare_status = "picked_up"
- task.pickup_time = datetime.now()
- if remark:
- task.pickup_remark = remark
- if image_urls:
- task.pickup_image_url = ",".join(image_urls) if len(image_urls) > 0 else None
- elif new_status == "supplier_stocking":
- task.supplier_prepare_status = "stocking"
- elif new_status == "supplier_ready":
- task.supplier_prepare_status = "prepared"
- # 备货完成凭证(与配送员取货凭证共用字段,时间线区分为供应商操作)
- if remark:
- task.pickup_remark = remark
- if image_urls:
- task.pickup_image_url = ",".join(image_urls) if len(image_urls) > 0 else None
- elif new_status == "delivering":
- task.deliverer_status = "delivering"
- if remark:
- task.delivery_remark = remark
- if image_urls:
- task.delivery_image_url = ",".join(image_urls) if len(image_urls) > 0 else None
- elif new_status == "completed":
- task.deliverer_status = "delivered"
- # 送达确认时间:与模型注释一致;小程序取货后直达 completed,必须在此写入
- task.delivery_time = datetime.now()
- if remark:
- task.delivery_remark = remark
- if image_urls:
- task.delivery_image_url = ",".join(image_urls) if len(image_urls) > 0 else None
- self._sync_legacy_status_from_dual(task)
- # 写入订单「操作记录」:legacy task.status 若已为 picking,会与取货入参 new_status 相同而漏记;
- # 配送员确认取货时必须仍写入一条。
- if task.order_id:
- from app.crud.order import order_crud
- should_log_order = (old_status != new_status) or new_status == "picking"
- if should_log_order:
- order_crud._create_status_log(
- db,
- task.order_id,
- f"dt_{old_status}" if old_status is not None else "",
- f"dt_{new_status}",
- operator_id or "system",
- operator_role or "deliver",
- reason=remark,
- create_name=(operator_real_name or "").strip() or None,
- )
- db.commit()
- db.refresh(task)
- if new_status == "delivering":
- from app.crud.delivery_warning_resolve import auto_resolve_open_warnings_for_task
- auto_resolve_open_warnings_for_task(db, task_id)
- return task
- def cancel_task(
- self,
- db: Session,
- task_id: str,
- reason: str = None,
- operator_id: str = None,
- operator_real_name: str = None,
- ) -> Optional[BizDeliveryTask]:
- """取消配送任务"""
- task = self.get(db, task_id)
- if not task:
- return None
- if task.status in ["completed", "cancelled"]:
- return None
- old_status = task.status
- task.status = "cancelled"
- task.update_at = datetime.now()
- if task.order_id:
- from app.crud.order import order_crud
- order_crud._create_status_log(
- db,
- task.order_id,
- f"dt_{old_status}",
- "dt_cancelled",
- operator_id or "system",
- "admin",
- reason=reason,
- create_name=(operator_real_name or "").strip() or None,
- )
- db.commit()
- db.refresh(task)
- from app.crud.delivery_warning_resolve import auto_resolve_open_warnings_for_task
- auto_resolve_open_warnings_for_task(db, task_id)
- return task
- def reassign(
- self,
- db: Session,
- task_id: str,
- new_deliver_id: str,
- operator_id: str = None,
- operator_real_name: str = None,
- ) -> Optional[BizDeliveryTask]:
- """重新分配配送员"""
- from app.crud.order import order_crud
- from app.crud.user import user_crud
- task = self.get(db, task_id)
- if not task:
- return None
- if task.status in ["completed", "cancelled"]:
- return None
- old_deliver_id = task.deliver_id
- if old_deliver_id == new_deliver_id:
- return task
- updater = None
- if operator_id:
- updater = {"user_id": operator_id, "real_name": operator_real_name}
- self.update_with_history(
- db,
- task_id,
- {"deliver_id": new_deliver_id},
- updater=updater,
- )
- task = self.get(db, task_id)
- if not task:
- return None
- def _dn(uid: Optional[str]) -> Optional[str]:
- if not uid:
- return None
- u = user_crud.get(db, uid)
- return (u.real_name if u else None) or uid
- nu = user_crud.get(db, new_deliver_id)
- order_crud._create_status_log(
- db,
- task.order_id,
- "",
- "delivery.reassign",
- operator_id or "system",
- "admin",
- reason=json.dumps(
- {
- "fromDeliverId": old_deliver_id,
- "toDeliverId": new_deliver_id,
- "fromDeliverName": _dn(old_deliver_id),
- "toDeliverName": nu.real_name if nu else new_deliver_id,
- },
- ensure_ascii=False,
- ),
- create_name=(operator_real_name or "").strip() or None,
- )
- db.commit()
- db.refresh(task)
- return task
- def get_warning_tasks(
- self,
- db: Session,
- region_id: str = None,
- within_minutes: int = 60,
- skip: int = 0,
- limit: int = 10
- ) -> tuple[List[BizDeliveryTask], int, int]:
- """获取时效预警任务"""
- now = datetime.now()
- expire_time_threshold = now + timedelta(minutes=within_minutes)
- query = db.query(BizDeliveryTask).filter(
- and_(
- BizDeliveryTask.status.in_(["pending", "assigned", "picking"]),
- BizDeliveryTask.is_del == 0,
- BizDeliveryTask.effective_time <= expire_time_threshold
- )
- )
- if region_id:
- query = query.filter(BizDeliveryTask.region_id == region_id)
- total = query.count()
- warning_count = query.filter(
- BizDeliveryTask.effective_time <= now + timedelta(minutes=30)
- ).count()
- tasks = query.order_by(BizDeliveryTask.effective_time.asc()).offset(skip).limit(limit).all()
- return tasks, total, warning_count
- def _generate_task_no(self, db: Session) -> str:
- """生成配送单号"""
- today = datetime.now().strftime("%Y%m%d")
- prefix = f"DLV{today}"
- last_task = db.query(BizDeliveryTask).filter(
- BizDeliveryTask.task_no.like(f"{prefix}%")
- ).order_by(BizDeliveryTask.task_no.desc()).first()
- if last_task:
- last_no = int(last_task.task_no[-4:])
- new_no = last_no + 1
- else:
- new_no = 1
- return f"{prefix}{str(new_no).zfill(4)}"
- def create_from_order(
- self,
- db: Session,
- order_id: str,
- supplier_id: str,
- region_id: str,
- creator: dict = None
- ) -> BizDeliveryTask:
- """从订单创建配送任务"""
- task_no = self._generate_task_no(db)
- # 设置配送截止时间(默认4小时)
- effective_time = datetime.now() + timedelta(hours=4)
- task = BizDeliveryTask(
- id=generate_id(),
- task_no=task_no,
- order_id=order_id,
- region_id=region_id,
- supplier_id=supplier_id,
- status="pending",
- deliverer_status="pending_pickup",
- supplier_prepare_status="pending_prepare",
- effective_time=effective_time,
- create_by=creator.get("user_id") if creator else None,
- create_name=creator.get("real_name") if creator else None
- )
- db.add(task)
- db.flush()
- return task
- def create_delivery_task_from_order(
- self,
- db: Session,
- order_id: str,
- order_items: List[dict],
- supplier_id: str,
- region_id: str,
- creator: dict = None
- ) -> tuple:
- """从订单创建完整的配送任务(包含明细)"""
- from app.crud.order import order_item_crud
- # 创建配送任务
- task = self.create_from_order(
- db, order_id, supplier_id, region_id, creator
- )
- # 创建配送任务明细
- task_items = []
- for item_data in order_items:
- # 获取订单项信息
- order_item = order_item_crud.get(db, item_data.get("order_item_id"))
- if not order_item:
- continue
- task_item = BizDeliveryTaskItem(
- id=generate_id(),
- task_id=task.id,
- order_id=order_id,
- order_item_id=item_data.get("order_item_id"),
- product_id=item_data.get("product_id"),
- client_id=item_data.get("client_id"),
- delivery_address=item_data.get("delivery_address"),
- contact_person=item_data.get("contact_person"),
- contact_phone=item_data.get("contact_phone"),
- quantity=Decimal(str(item_data.get("quantity", 0))),
- status="pending",
- create_by=creator.get("user_id") if creator else None,
- create_name=creator.get("real_name") if creator else None
- )
- db.add(task_item)
- task_items.append(task_item)
- db.commit()
- db.refresh(task)
- for item in task_items:
- db.refresh(item)
- return task, task_items
- def get_expiring_tasks(
- self,
- db: Session,
- within_minutes: int = 60,
- region_id: str = None
- ) -> List[BizDeliveryTask]:
- """获取即将过期的配送任务(用于定时任务)"""
- now = datetime.now()
- expire_threshold = now + timedelta(minutes=within_minutes)
- query = db.query(BizDeliveryTask).filter(
- and_(
- BizDeliveryTask.status.in_(["pending", "assigned", "picking"]),
- BizDeliveryTask.is_del == 0,
- BizDeliveryTask.effective_time <= expire_threshold,
- BizDeliveryTask.effective_time >= now
- )
- )
- if region_id:
- query = query.filter(BizDeliveryTask.region_id == region_id)
- return query.order_by(BizDeliveryTask.effective_time.asc()).all()
- def update_with_history(self, db: Session, id: str, obj_in: dict, updater: dict = None) -> Optional[BizDeliveryTask]:
- """更新配送任务并记录历史"""
- original = self.get(db, id)
- if not original:
- return None
- his_data = {
- "id": generate_id(),
- "task_no": original.task_no,
- "order_id": original.order_id,
- "region_id": original.region_id,
- "supplier_id": original.supplier_id,
- "deliver_id": original.deliver_id,
- "status": original.status,
- "deliverer_status": getattr(original, "deliverer_status", None),
- "supplier_prepare_status": getattr(original, "supplier_prepare_status", None),
- "pickup_time": original.pickup_time,
- "pickup_remark": original.pickup_remark,
- "pickup_image_url": original.pickup_image_url,
- "delivery_time": original.delivery_time,
- "delivery_remark": original.delivery_remark,
- "delivery_image_url": original.delivery_image_url,
- "expire_warning_sent": getattr(original, "expire_warning_sent", False),
- "effective_time": original.effective_time,
- "dispatch_remark": getattr(original, "dispatch_remark", None),
- "create_at": original.create_at,
- "create_by": original.create_by,
- "create_name": original.create_name,
- "update_at": original.update_at,
- "update_by": original.update_by,
- }
- his = BizDeliveryTaskHis(**his_data)
- db.add(his)
- return self.update(db, id, obj_in, updater)
- # =========================================
- # 抢单相关方法
- # =========================================
- def get_pending_snatch_tasks(
- self,
- db: Session,
- skip: int = 0,
- limit: int = 10
- ) -> tuple[List[BizDeliveryTask], int]:
- """获取待抢单的任务(状态为pending且未分配配送员)"""
- query = db.query(BizDeliveryTask).filter(
- and_(
- BizDeliveryTask.status == "pending",
- BizDeliveryTask.is_del == 0
- )
- )
- total = query.count()
- tasks = query.order_by(BizDeliveryTask.effective_time.asc()).offset(skip).limit(limit).all()
- return tasks, total
- def count_pending_snatch_tasks(self, db: Session) -> int:
- """统计待抢单的任务数量"""
- return db.query(BizDeliveryTask).filter(
- and_(
- BizDeliveryTask.status == "pending",
- BizDeliveryTask.is_del == 0
- )
- ).count()
- def assign_to_deliver(
- self,
- db: Session,
- task_id: str,
- deliver_id: str,
- deliver_name: str = None
- ) -> bool:
- """分配任务给配送员(用于抢单)"""
- task = self.get(db, task_id)
- if not task:
- return False
- if task.status != "pending":
- return False
- if task.deliver_id is not None:
- return False
- task.deliver_id = deliver_id
- task.deliver_name = deliver_name
- task.deliverer_status = "pending_pickup"
- task.supplier_prepare_status = "pending_prepare"
- self._sync_legacy_status_from_dual(task)
- task.update_at = datetime.now()
- db.commit()
- db.refresh(task)
- return True
- def get_snatched_by_me(
- self,
- db: Session,
- deliver_id: str,
- skip: int = 0,
- limit: int = 10
- ) -> tuple[List[BizDeliveryTask], int]:
- """获取我抢到的任务"""
- query = db.query(BizDeliveryTask).filter(
- and_(
- BizDeliveryTask.deliver_id == deliver_id,
- BizDeliveryTask.status.in_(["pending", "assigned"]),
- BizDeliveryTask.is_del == 0
- )
- )
- total = query.count()
- tasks = query.order_by(BizDeliveryTask.effective_time.asc()).offset(skip).limit(limit).all()
- return tasks, total
- def count_snatched_by_me(self, db: Session, deliver_id: str) -> int:
- """统计我抢到的任务数量"""
- return db.query(BizDeliveryTask).filter(
- and_(
- BizDeliveryTask.deliver_id == deliver_id,
- BizDeliveryTask.status.in_(["pending", "assigned"]),
- BizDeliveryTask.is_del == 0
- )
- ).count()
- def count_today_snatched(self, db: Session, deliver_id: str, start_time: datetime) -> int:
- """统计今日抢单成功数量"""
- return db.query(BizDeliveryTask).filter(
- and_(
- BizDeliveryTask.deliver_id == deliver_id,
- BizDeliveryTask.status.in_(["pending", "assigned", "picking", "delivering", "completed"]),
- BizDeliveryTask.is_del == 0,
- BizDeliveryTask.update_at >= start_time
- )
- ).count()
- def cancel_snatch(self, db: Session, task_id: str) -> bool:
- """取消抢单"""
- task = self.get(db, task_id)
- if not task:
- return False
- if task.status not in ["pending", "assigned"]:
- return False
- task.deliver_id = None
- task.deliver_name = None
- task.status = "pending"
- task.update_at = datetime.now()
- db.commit()
- db.refresh(task)
- return True
- def get_history_by_deliver(
- self,
- db: Session,
- deliver_id: str,
- start_date: str = None,
- end_date: str = None,
- skip: int = 0,
- limit: int = 10
- ) -> tuple[List[BizDeliveryTask], int]:
- """获取配送员历史任务(已完成和已取消的)"""
- query = db.query(BizDeliveryTask).filter(
- and_(
- BizDeliveryTask.deliver_id == deliver_id,
- BizDeliveryTask.status.in_(["completed", "cancelled"]),
- BizDeliveryTask.is_del == 0
- )
- )
- if start_date:
- query = query.filter(BizDeliveryTask.create_at >= start_date)
- if end_date:
- query = query.filter(BizDeliveryTask.create_at <= end_date + " 23:59:59")
- total = query.count()
- tasks = query.order_by(BizDeliveryTask.delivery_time.desc()).offset(skip).limit(limit).all()
- return tasks, total
- class DeliveryTaskItemCRUD(CRUDBase[BizDeliveryTaskItem]):
- """配送任务明细CRUD操作"""
- def get_by_task(self, db: Session, task_id: str) -> List[BizDeliveryTaskItem]:
- """获取配送任务的所有明细"""
- return db.query(BizDeliveryTaskItem).filter(
- and_(
- BizDeliveryTaskItem.task_id == task_id,
- BizDeliveryTaskItem.is_del == 0
- )
- ).all()
- def get_delivered_quantity_by_order_item(
- self, db: Session, order_id: str
- ) -> dict[str, float]:
- """按订单行汇总当前配送任务明细上的已送达数量(与 biz_order_item.delivered_quantity 对齐展示用)"""
- task = db.query(BizDeliveryTask).filter(
- and_(BizDeliveryTask.order_id == order_id, BizDeliveryTask.is_del == 0)
- ).first()
- if not task:
- return {}
- out: dict[str, float] = {}
- for ti in self.get_by_task(db, task.id):
- oid = ti.order_item_id
- if not oid:
- continue
- q = float(ti.delivered_quantity or 0)
- prev = out.get(oid, 0.0)
- if q > prev:
- out[oid] = q
- return out
- def create_items(
- self,
- db: Session,
- task_id: str,
- items: List[dict],
- creator: dict = None
- ) -> List[BizDeliveryTaskItem]:
- """批量创建配送任务明细"""
- result = []
- for item in items:
- task_item = BizDeliveryTaskItem(
- id=generate_id(),
- task_id=task_id,
- order_id=item.get("order_id"),
- order_item_id=item.get("order_item_id"),
- product_id=item.get("product_id"),
- client_id=item.get("client_id"),
- delivery_address=item.get("delivery_address"),
- contact_person=item.get("contact_person"),
- contact_phone=item.get("contact_phone"),
- quantity=Decimal(str(item.get("quantity", 0))),
- status="pending",
- create_by=creator.get("user_id") if creator else None,
- create_name=creator.get("real_name") if creator else None
- )
- db.add(task_item)
- result.append(task_item)
- db.commit()
- for item in result:
- db.refresh(item)
- return result
- def update_item_status(
- self,
- db: Session,
- item_id: str,
- delivered_quantity: Decimal,
- status: str = "delivered"
- ) -> Optional[BizDeliveryTaskItem]:
- """更新配送明细状态"""
- item = self.get(db, item_id)
- if not item:
- return None
- item.delivered_quantity = delivered_quantity
- item.status = status
- if status == "delivered":
- item.delivery_time = datetime.now()
- if item.order_item_id:
- from app.crud.order import order_item_crud
- oi = order_item_crud.get(db, item.order_item_id)
- if oi is not None:
- oi.delivered_quantity = delivered_quantity
- oi.update_at = datetime.now()
- db.commit()
- db.refresh(item)
- return item
- delivery_task_crud = DeliveryTaskCRUD(BizDeliveryTask)
- delivery_task_item_crud = DeliveryTaskItemCRUD(BizDeliveryTaskItem)
|