delivery.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694
  1. """配送管理接口"""
  2. from typing import Optional, List
  3. from datetime import datetime
  4. from fastapi import APIRouter, Depends, Query
  5. from sqlalchemy.orm import Session
  6. from app.core.database import get_db
  7. from app.core.auth import get_current_user
  8. from app.core.response import success_response, error_response
  9. from app.crud.region import region_crud
  10. from app.crud.user import user_crud
  11. from app.crud.order import order_crud, order_item_crud
  12. from app.crud.product import product_crud
  13. from app.crud.unit import unit_crud
  14. from app.crud.address import address_crud
  15. from app.core.order_status_compat import derive_legacy_status_from_three
  16. router = APIRouter(prefix="/web", tags=["运营端-配送管理"])
  17. def _numeric_to_float_optional(v) -> Optional[float]:
  18. if v is None:
  19. return None
  20. return float(v)
  21. def _category_path_display(db: Session, category_id: Optional[str]) -> str:
  22. """一级/二级分类用 / 连接(与订单详情一致)。"""
  23. from app.crud.category import category_crud
  24. if not category_id:
  25. return ""
  26. names: List[str] = []
  27. cid = str(category_id).strip()
  28. seen = set()
  29. depth = 0
  30. while cid and depth < 24 and cid not in seen:
  31. seen.add(cid)
  32. cat = category_crud.get(db, cid)
  33. if not cat:
  34. break
  35. names.insert(0, (getattr(cat, "name", None) or "").strip())
  36. pid = getattr(cat, "parent_id", None)
  37. cid = str(pid).strip() if pid else ""
  38. depth += 1
  39. return "/".join([n for n in names if n])
  40. def _supplier_pickup_address_block(db: Session, supplier_id: Optional[str]) -> dict:
  41. """供应商默认取货地址;无默认时取第一条 pickup 类型地址。"""
  42. empty = {"fullAddress": None, "contactPerson": None, "contactPhone": None}
  43. if not supplier_id:
  44. return empty
  45. addr = address_crud.get_default(db, supplier_id, "pickup")
  46. if not addr:
  47. rows = address_crud.get_by_user_and_type(db, supplier_id, "pickup")
  48. addr = rows[0] if rows else None
  49. if not addr:
  50. return empty
  51. segs = [
  52. getattr(addr, "province", None),
  53. getattr(addr, "city", None),
  54. getattr(addr, "district", None),
  55. getattr(addr, "detail_address", None),
  56. ]
  57. full = "".join(s for s in segs if s)
  58. return {
  59. "fullAddress": full or None,
  60. "contactPerson": addr.contact_person,
  61. "contactPhone": addr.contact_phone,
  62. }
  63. def require_admin(current_user: dict = Depends(get_current_user)):
  64. """验证运营人员权限"""
  65. if current_user.get("role") != "admin":
  66. return error_response(403, "需要运营人员权限")
  67. return current_user
  68. # 配送任务状态映射
  69. DELIVERY_STATUS_MAP = {
  70. "pending": "待取货",
  71. "assigned": "已分配",
  72. "picking": "取货中",
  73. "delivering": "配送中",
  74. "completed": "已完成",
  75. "cancelled": "已取消"
  76. }
  77. @router.get("/delivery-tasks", response_model=dict)
  78. def list_delivery_tasks(
  79. page: int = Query(1, ge=1, description="页码"),
  80. pageSize: int = Query(10, ge=1, le=100, description="每页条数"),
  81. regionId: Optional[str] = Query(None, description="区域ID"),
  82. status: Optional[str] = Query(None, description="状态"),
  83. orderNo: Optional[str] = Query(None, description="订单号"),
  84. db: Session = Depends(get_db),
  85. current_user: dict = Depends(require_admin)
  86. ):
  87. """获取配送任务列表"""
  88. from app.crud.delivery import delivery_task_crud, delivery_task_item_crud
  89. tasks, total = delivery_task_crud.get_multi_with_filters(
  90. db,
  91. region_id=regionId,
  92. status=status,
  93. order_no=orderNo,
  94. skip=(page - 1) * pageSize,
  95. limit=pageSize
  96. )
  97. region_map = {}
  98. supplier_map = {}
  99. deliver_map = {}
  100. client_map = {}
  101. result = []
  102. for task in tasks:
  103. # 获取区域名称
  104. if task.region_id and task.region_id not in region_map:
  105. region = region_crud.get(db, task.region_id)
  106. region_map[task.region_id] = region.name if region else None
  107. # 获取供应商信息
  108. if task.supplier_id and task.supplier_id not in supplier_map:
  109. supplier = user_crud.get(db, task.supplier_id)
  110. supplier_map[task.supplier_id] = {
  111. "name": supplier.real_name if supplier else None,
  112. "phone": supplier.phone if supplier else None
  113. }
  114. # 获取配送员信息
  115. if task.deliver_id and task.deliver_id not in deliver_map:
  116. deliver = user_crud.get(db, task.deliver_id)
  117. deliver_map[task.deliver_id] = {
  118. "name": deliver.real_name if deliver else None,
  119. "phone": deliver.phone if deliver else None
  120. }
  121. # 获取订单信息
  122. order_info = {}
  123. if task.order_id:
  124. order = order_crud.get(db, task.order_id)
  125. if order:
  126. # 获取客户信息(列表页需要客户名称)
  127. if order.client_id and order.client_id not in client_map:
  128. client = user_crud.get(db, order.client_id)
  129. client_map[order.client_id] = {
  130. "name": client.real_name if client else None,
  131. "phone": client.phone if client else None
  132. }
  133. order_info = {
  134. "orderNo": order.order_no,
  135. "totalAmount": float(order.total_amount) if order.total_amount else 0,
  136. "clientId": order.client_id,
  137. "clientName": client_map.get(order.client_id, {}).get("name") if order.client_id else None,
  138. "contactPerson": order.contact_person,
  139. "contactPhone": order.contact_phone,
  140. "deliveryAddress": order.delivery_address
  141. }
  142. # 获取任务明细数量
  143. items = delivery_task_item_crud.get_by_task(db, task.id)
  144. item_count = len(items)
  145. # 供应商取货地址(用于列表展示)
  146. supplier_pickup = _supplier_pickup_address_block(db, task.supplier_id)
  147. result.append({
  148. "id": task.id,
  149. "taskNo": task.task_no,
  150. "orderId": task.order_id,
  151. "orderNo": order_info.get("orderNo"),
  152. "regionId": task.region_id,
  153. "regionName": region_map.get(task.region_id),
  154. "supplierId": task.supplier_id,
  155. "supplierName": supplier_map.get(task.supplier_id, {}).get("name"),
  156. "supplierAddress": supplier_pickup.get("fullAddress"),
  157. "deliverId": task.deliver_id,
  158. "deliverName": deliver_map.get(task.deliver_id, {}).get("name"),
  159. "deliverPhone": deliver_map.get(task.deliver_id, {}).get("phone"),
  160. "status": task.status,
  161. "totalAmount": order_info.get("totalAmount"),
  162. "itemCount": item_count,
  163. "clientId": order_info.get("clientId"),
  164. "clientName": order_info.get("clientName"),
  165. "contactPerson": order_info.get("contactPerson"),
  166. "contactPhone": order_info.get("contactPhone"),
  167. "deliveryAddress": order_info.get("deliveryAddress"),
  168. "dispatchRemark": task.dispatch_remark,
  169. "createAt": str(task.create_at) if task.create_at else None,
  170. "assignTime": str(task.create_at) if task.deliver_id else None,
  171. "pickupTime": str(task.pickup_time) if task.pickup_time else None,
  172. "deliveryTime": str(task.delivery_time) if task.delivery_time else None,
  173. "expireTime": str(task.effective_time) if task.effective_time else None,
  174. "isExpiring": False
  175. })
  176. return success_response({
  177. "list": result,
  178. "total": total,
  179. "page": page,
  180. "pageSize": pageSize,
  181. "totalPages": (total + pageSize - 1) // pageSize
  182. })
  183. @router.get("/delivery-tasks/{task_id}", response_model=dict)
  184. def get_delivery_task(
  185. task_id: str,
  186. db: Session = Depends(get_db),
  187. current_user: dict = Depends(require_admin)
  188. ):
  189. """获取配送任务详情"""
  190. from app.crud.delivery import delivery_task_crud, delivery_task_item_crud
  191. task = delivery_task_crud.get(db, task_id)
  192. if not task:
  193. return error_response(404, "配送任务不存在")
  194. # 获取任务明细
  195. items = delivery_task_item_crud.get_by_task(db, task_id)
  196. # 获取关联信息
  197. region_name = None
  198. if task.region_id:
  199. region = region_crud.get(db, task.region_id)
  200. region_name = region.name if region else None
  201. supplier_info = {"name": None, "phone": None}
  202. if task.supplier_id:
  203. supplier = user_crud.get(db, task.supplier_id)
  204. if supplier:
  205. supplier_info = {"name": supplier.real_name, "phone": supplier.phone}
  206. deliver_info = {"name": None, "phone": None}
  207. if task.deliver_id:
  208. deliver = user_crud.get(db, task.deliver_id)
  209. if deliver:
  210. deliver_info = {"name": deliver.real_name, "phone": deliver.phone}
  211. # 获取订单信息
  212. order_info = {}
  213. if task.order_id:
  214. order = order_crud.get(db, task.order_id)
  215. if order:
  216. order_info = {
  217. "orderNo": order.order_no,
  218. "totalAmount": float(order.total_amount) if order.total_amount else 0,
  219. "contactPerson": order.contact_person,
  220. "contactPhone": order.contact_phone,
  221. "deliveryAddress": order.delivery_address
  222. }
  223. pickup_addr = _supplier_pickup_address_block(db, task.supplier_id)
  224. # 处理任务明细(商品展示字段对齐运营端订单明细)
  225. item_details = []
  226. for item in items:
  227. product = product_crud.get(db, item.product_id)
  228. order_line = order_item_crud.get(db, item.order_item_id) if item.order_item_id else None
  229. unit_row = None
  230. if order_line and order_line.unit_id:
  231. unit_row = unit_crud.get(db, order_line.unit_id)
  232. elif product and product.unit_id:
  233. unit_row = unit_crud.get(db, product.unit_id)
  234. image_url = getattr(product, "image_url", None) if product else None
  235. category_path = ""
  236. if product and getattr(product, "category_id", None):
  237. category_path = _category_path_display(db, str(product.category_id))
  238. measure_value = _numeric_to_float_optional(getattr(product, "measure_value", None)) if product else None
  239. unit_price = float(order_line.unit_price) if order_line and order_line.unit_price is not None else None
  240. amount = float(order_line.amount) if order_line and order_line.amount is not None else None
  241. client = user_crud.get(db, item.client_id) if item.client_id else None
  242. # 供货/售价字段:配送任务明细接口目前不计算价格清单的供货价,先以订单行售价兜底,
  243. # 确保运营端「配送列表」展开明细对齐「订单列表」列结构时有数据可展示。
  244. selling_unit_price = unit_price
  245. selling_amount = amount
  246. supplier_unit_price = unit_price
  247. supplier_amount = amount
  248. item_details.append({
  249. "id": item.id,
  250. "orderItemId": item.order_item_id,
  251. "productId": item.product_id,
  252. "productName": product.name if product else None,
  253. "imageUrl": image_url,
  254. "categoryName": category_path or None,
  255. "measureValue": measure_value,
  256. "unitName": unit_row.name if unit_row else None,
  257. "unitPrice": unit_price,
  258. "amount": amount,
  259. "sellingUnitPrice": selling_unit_price,
  260. "sellingAmount": selling_amount,
  261. "supplierId": task.supplier_id,
  262. "supplierName": supplier_info.get("name"),
  263. "supplierUnitPrice": supplier_unit_price,
  264. "supplierAmount": supplier_amount,
  265. "quantity": float(item.quantity) if item.quantity else 0,
  266. "deliveredQuantity": float(item.delivered_quantity) if item.delivered_quantity else 0,
  267. "clientName": client.real_name if client else None,
  268. "clientPhone": client.phone if client else None,
  269. "clientAddress": item.delivery_address,
  270. "contactPerson": item.contact_person,
  271. "contactPhone": item.contact_phone,
  272. "deliveryAddress": item.delivery_address,
  273. "status": item.status
  274. })
  275. # 处理图片
  276. pickup_images = []
  277. if task.pickup_image_url:
  278. pickup_images = task.pickup_image_url.split(",")
  279. delivery_images = []
  280. if task.delivery_image_url:
  281. delivery_images = task.delivery_image_url.split(",")
  282. return success_response({
  283. "id": task.id,
  284. "taskNo": task.task_no,
  285. "orderId": task.order_id,
  286. "orderNo": order_info.get("orderNo"),
  287. "regionId": task.region_id,
  288. "regionName": region_name,
  289. "supplierId": task.supplier_id,
  290. "supplierName": supplier_info.get("name"),
  291. "supplierPhone": supplier_info.get("phone"),
  292. "supplierAddress": pickup_addr.get("fullAddress"),
  293. "supplierPickup": {
  294. "address": pickup_addr.get("fullAddress"),
  295. "contactPerson": pickup_addr.get("contactPerson"),
  296. "contactPhone": pickup_addr.get("contactPhone"),
  297. },
  298. "customerDelivery": {
  299. "address": order_info.get("deliveryAddress"),
  300. "contactPerson": order_info.get("contactPerson"),
  301. "contactPhone": order_info.get("contactPhone"),
  302. },
  303. "deliverId": task.deliver_id,
  304. "deliverName": deliver_info.get("name"),
  305. "deliverPhone": deliver_info.get("phone"),
  306. "status": task.status,
  307. "totalAmount": order_info.get("totalAmount"),
  308. "createAt": str(task.create_at) if task.create_at else None,
  309. "assignTime": str(task.create_at) if task.deliver_id else None,
  310. "pickupTime": str(task.pickup_time) if task.pickup_time else None,
  311. "pickupRemark": task.pickup_remark,
  312. "pickupImages": pickup_images,
  313. "deliveryTime": str(task.delivery_time) if task.delivery_time else None,
  314. "deliveryRemark": task.delivery_remark,
  315. "deliveryImages": delivery_images,
  316. "dispatchRemark": task.dispatch_remark,
  317. "items": item_details
  318. })
  319. @router.post("/delivery-tasks/assign", response_model=dict)
  320. def assign_delivery_task(
  321. request: dict,
  322. db: Session = Depends(get_db),
  323. current_user: dict = Depends(require_admin)
  324. ):
  325. """分配配送任务"""
  326. from app.crud.delivery import delivery_task_crud
  327. from app.crud.order import order_crud
  328. order_id = request.get("orderId")
  329. deliver_id = request.get("deliverId")
  330. if not order_id:
  331. return error_response(400, "请选择订单")
  332. if not deliver_id:
  333. return error_response(400, "请选择配送员")
  334. # 验证配送员存在
  335. deliver = user_crud.get(db, deliver_id)
  336. if not deliver:
  337. return error_response(404, "配送员不存在")
  338. if deliver.role != "deliver":
  339. return error_response(400, "该用户不是配送员")
  340. task = delivery_task_crud.assign_deliver(
  341. db,
  342. order_id=order_id,
  343. deliver_id=deliver_id,
  344. operator_id=current_user.get("user_id")
  345. )
  346. if not task:
  347. return error_response(400, "分配失败,请检查订单状态")
  348. # 阶段D前收尾:分配配送优先维护 delivery_status
  349. order = order_crud.get(db, order_id)
  350. if order:
  351. order.delivery_status = "delivering"
  352. if order.order_status != "cancelled":
  353. order.order_status = "pending_confirm"
  354. order.status = derive_legacy_status_from_three(
  355. order_status=order.order_status,
  356. delivery_status=order.delivery_status,
  357. pay_status=order.pay_status,
  358. )
  359. order.update_at = datetime.now()
  360. db.commit()
  361. return success_response({
  362. "id": task.id,
  363. "status": task.status,
  364. "message": "分配成功"
  365. })
  366. @router.post("/delivery-tasks/assign-batch", response_model=dict)
  367. def assign_delivery_task_batch(
  368. request: dict,
  369. db: Session = Depends(get_db),
  370. current_user: dict = Depends(require_admin)
  371. ):
  372. """批量分配配送任务"""
  373. from app.crud.delivery import delivery_task_crud
  374. from app.crud.order import order_crud
  375. assignments = request.get("assignments", [])
  376. if not assignments:
  377. return error_response(400, "请选择要分配的订单")
  378. results = []
  379. for assignment in assignments:
  380. order_id = assignment.get("orderId")
  381. deliver_id = assignment.get("deliverId")
  382. if order_id and deliver_id:
  383. task = delivery_task_crud.assign_deliver(
  384. db,
  385. order_id=order_id,
  386. deliver_id=deliver_id,
  387. operator_id=current_user.get("user_id")
  388. )
  389. if task:
  390. # 阶段D前收尾:分配配送优先维护 delivery_status
  391. order = order_crud.get(db, order_id)
  392. if order:
  393. order.delivery_status = "delivering"
  394. if order.order_status != "cancelled":
  395. order.order_status = "pending_confirm"
  396. order.status = derive_legacy_status_from_three(
  397. order_status=order.order_status,
  398. delivery_status=order.delivery_status,
  399. pay_status=order.pay_status,
  400. )
  401. order.update_at = datetime.now()
  402. db.commit()
  403. results.append({"orderId": order_id, "success": True})
  404. else:
  405. results.append({"orderId": order_id, "success": False, "reason": "分配失败"})
  406. success_count = sum(1 for r in results if r.get("success"))
  407. return success_response({
  408. "total": len(results),
  409. "success": success_count,
  410. "failed": len(results) - success_count,
  411. "results": results
  412. })
  413. @router.get("/delivery-tasks/warnings", response_model=dict)
  414. def list_delivery_warnings(
  415. page: int = Query(1, ge=1, description="页码"),
  416. pageSize: int = Query(10, ge=1, le=100, description="每页条数"),
  417. regionId: Optional[str] = Query(None, description="区域ID"),
  418. withinMinutes: int = Query(60, description="多少分钟内即将过期"),
  419. db: Session = Depends(get_db),
  420. current_user: dict = Depends(require_admin)
  421. ):
  422. """获取配送时效预警列表"""
  423. from app.crud.delivery import delivery_task_crud
  424. tasks, total, warning_count = delivery_task_crud.get_warning_tasks(
  425. db,
  426. region_id=regionId,
  427. within_minutes=withinMinutes,
  428. skip=(page - 1) * pageSize,
  429. limit=pageSize
  430. )
  431. region_map = {}
  432. deliver_map = {}
  433. result = []
  434. for task in tasks:
  435. if task.region_id and task.region_id not in region_map:
  436. region = region_crud.get(db, task.region_id)
  437. region_map[task.region_id] = region.name if region else None
  438. if task.deliver_id and task.deliver_id not in deliver_map:
  439. deliver = user_crud.get(db, task.deliver_id)
  440. deliver_map[task.deliver_id] = deliver.real_name if deliver else None
  441. order_info = {}
  442. if task.order_id:
  443. order = order_crud.get(db, task.order_id)
  444. if order:
  445. order_info = {"orderNo": order.order_no}
  446. # 计算剩余时间和预警级别
  447. remaining_minutes = 0
  448. warning_level = "normal"
  449. is_overdue = False
  450. if task.effective_time:
  451. now = datetime.now()
  452. remaining = task.effective_time - now
  453. remaining_minutes = int(remaining.total_seconds() / 60)
  454. if remaining_minutes < 0:
  455. is_overdue = True
  456. warning_level = "overdue"
  457. elif remaining_minutes <= 15:
  458. warning_level = "urgent"
  459. elif remaining_minutes <= 30:
  460. warning_level = "warning"
  461. result.append({
  462. "id": task.id,
  463. "taskNo": task.task_no,
  464. "orderId": task.order_id,
  465. "orderNo": order_info.get("orderNo"),
  466. "regionId": task.region_id,
  467. "regionName": region_map.get(task.region_id),
  468. "deliverId": task.deliver_id,
  469. "deliverName": deliver_map.get(task.deliver_id),
  470. "status": task.status,
  471. "expireTime": str(task.effective_time) if task.effective_time else None,
  472. "remainingMinutes": remaining_minutes,
  473. "isOverdue": is_overdue,
  474. "warningLevel": warning_level
  475. })
  476. return success_response({
  477. "list": result,
  478. "total": total,
  479. "warningCount": warning_count,
  480. "page": page,
  481. "pageSize": pageSize,
  482. "totalPages": (total + pageSize - 1) // pageSize
  483. })
  484. @router.post("/delivery-tasks/{task_id}/cancel", response_model=dict)
  485. def cancel_delivery_task(
  486. task_id: str,
  487. request: dict,
  488. db: Session = Depends(get_db),
  489. current_user: dict = Depends(require_admin)
  490. ):
  491. """取消配送任务"""
  492. from app.crud.delivery import delivery_task_crud
  493. task = delivery_task_crud.get(db, task_id)
  494. if not task:
  495. return error_response(404, "配送任务不存在")
  496. reason = request.get("reason")
  497. task = delivery_task_crud.cancel_task(
  498. db,
  499. task_id=task_id,
  500. reason=reason,
  501. operator_id=current_user.get("user_id"),
  502. operator_real_name=current_user.get("real_name"),
  503. )
  504. if not task:
  505. return error_response(400, "当前状态不支持取消")
  506. return success_response({
  507. "id": task.id,
  508. "status": task.status,
  509. "message": "取消成功"
  510. })
  511. @router.post("/delivery-tasks/{task_id}/reassign", response_model=dict)
  512. def reassign_delivery_task(
  513. task_id: str,
  514. request: dict,
  515. db: Session = Depends(get_db),
  516. current_user: dict = Depends(require_admin)
  517. ):
  518. """重新分配配送任务"""
  519. from app.crud.delivery import delivery_task_crud
  520. task = delivery_task_crud.get(db, task_id)
  521. if not task:
  522. return error_response(404, "配送任务不存在")
  523. new_deliver_id = request.get("deliverId")
  524. if not new_deliver_id:
  525. return error_response(400, "请选择新配送员")
  526. # 验证新配送员
  527. new_deliver = user_crud.get(db, new_deliver_id)
  528. if not new_deliver:
  529. return error_response(404, "配送员不存在")
  530. if new_deliver.role != "deliver":
  531. return error_response(400, "该用户不是配送员")
  532. task = delivery_task_crud.reassign(
  533. db,
  534. task_id=task_id,
  535. new_deliver_id=new_deliver_id,
  536. operator_id=current_user.get("user_id"),
  537. operator_real_name=current_user.get("real_name"),
  538. )
  539. if not task:
  540. return error_response(400, "当前状态不支持重新分配")
  541. return success_response({
  542. "id": task.id,
  543. "deliverId": task.deliver_id,
  544. "message": "重新分配成功"
  545. })
  546. @router.get("/delivery-tasks/pending", response_model=dict)
  547. def list_pending_delivery_tasks(
  548. page: int = Query(1, ge=1, description="页码"),
  549. pageSize: int = Query(10, ge=1, le=100, description="每页条数"),
  550. regionId: Optional[str] = Query(None, description="区域ID"),
  551. db: Session = Depends(get_db),
  552. current_user: dict = Depends(require_admin)
  553. ):
  554. """获取待分配的配送任务"""
  555. from app.crud.delivery import delivery_task_crud, delivery_task_item_crud
  556. tasks, total = delivery_task_crud.get_pending_list(
  557. db,
  558. region_id=regionId,
  559. skip=(page - 1) * pageSize,
  560. limit=pageSize
  561. )
  562. region_map = {}
  563. result = []
  564. for task in tasks:
  565. if task.region_id and task.region_id not in region_map:
  566. region = region_crud.get(db, task.region_id)
  567. region_map[task.region_id] = region.name if region else None
  568. order_info = {}
  569. if task.order_id:
  570. order = order_crud.get(db, task.order_id)
  571. if order:
  572. order_info = {
  573. "orderNo": order.order_no,
  574. "totalAmount": float(order.total_amount) if order.total_amount else 0,
  575. "contactPerson": order.contact_person,
  576. "contactPhone": order.contact_phone,
  577. "deliveryAddress": order.delivery_address
  578. }
  579. items = delivery_task_item_crud.get_by_task(db, task.id)
  580. result.append({
  581. "id": task.id,
  582. "taskNo": task.task_no,
  583. "orderId": task.order_id,
  584. "orderNo": order_info.get("orderNo"),
  585. "regionId": task.region_id,
  586. "regionName": region_map.get(task.region_id),
  587. "status": task.status,
  588. "totalAmount": order_info.get("totalAmount"),
  589. "itemCount": len(items),
  590. "createAt": str(task.create_at) if task.create_at else None
  591. })
  592. return success_response({
  593. "list": result,
  594. "total": total,
  595. "page": page,
  596. "pageSize": pageSize,
  597. "totalPages": (total + pageSize - 1) // pageSize
  598. })