refactor(api): yun8_sync 并入 tests 分层框架(接口层 apis/yun8 + 用例层 api + Allure)

- tests/core: 新增 BaseHttpClient 统一 HTTP 封装(直连绕行/超时/日志/自定义编码);
  db_helper 追加 Db 长连接类(事务/mogrify/自动重连),既有函数不动
- tests/apis/yun8: 21 个 step 方法按域拆分为 5 个 Mixin(driver/accounting/
  audit_payment/receipt/third_party),Yun8Pipeline 24 步编排原样迁移,
  SQL 全部参数化并集中至 sql_templates
- tests/api: 5 阶段有序 P0 E2E 用例(业务失败由 print 改为真实报错)+
  README 迁移(删除明文密码段,修正步骤编号漂移)
- yun8_sync 目录退役:配置并入 test_config.yaml 的 yun8_api 段;
  造数 CLI 保留(python -m tests.apis.yun8.cli,兼容旧 config.yaml 结构)
- pytest.ini 注册 api/yun8_sync marker;requirements.txt 补 requests>=2.28
- 静态验证: compileall / 全仓 collect 24 条 / marker 过滤 / CLI --help /
  客户端与 pipeline 离线实例化 / SQL 占位符核对 / cross_end 兼容
This commit is contained in:
xst
2026-09-08 11:38:17 +08:00
parent e62e3224bf
commit 52b73f456c
26 changed files with 2147 additions and 1786 deletions
+163
View File
@@ -0,0 +1,163 @@
"""司机端接口 Mixin — step01-05(对应 Postman Collection 司机端 5 个接口)。
所有方法签名/返回值/默认值与旧 yun8_sync/client.py 逐字一致,
仅将原始 requests.Session 调用替换为 BaseHttpClient(self.driver_http)。
Usage:
from tests.apis.yun8.driver_api import DriverApi
class Yun8ApiClient(DriverApi, ...): ...
"""
from datetime import datetime
from tests.core.http_client import BaseHttpClient
def _current_time_str() -> str:
"""Postman pre-request: 生成 'YYYY-MM-DD HH:MM:SS' 格式的当前时间"""
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
class DriverApi:
"""司机端接口(挂载于 Yun8ApiClient,依赖 driver_http / dp 属性)。"""
# ==================================================================
# Step 01: 查询货源
# ==================================================================
def step01_query_goods(self, page_size: int = 10, page_num: int = 1) -> dict:
"""
Postman Test Script:
pm.collectionVariables.set("mainOrderCode", jsonData.data.records[0].code);
pm.collectionVariables.set("residueNumber", jsonData.data.records[0].lastWeight);
Returns: {"mainOrderCode": str, "residueNumber": str}
"""
params = {
"isFromOrderHall": "1",
"sortType": "desc",
"sortField": "gmt_created",
"pageSize": page_size,
"pageNum": page_num,
}
data = self.driver_http.get("/ntocc-tms-api/order/orderHallList", params)
records = data.get("data", {}).get("records", [])
if not records:
raise RuntimeError("查询货源: 无记录返回")
r = records[0]
return {
"mainOrderCode": r.get("code", ""),
"residueNumber": str(r.get("lastWeight", "")),
"shipperLatitude": str(r.get("shipperLatitude", "")),
"shipperLongitude": str(r.get("shipperLongitude", "")),
"consigneeLatitude": str(r.get("consigneeLatitude", "")),
"consigneeLongitude": str(r.get("consigneeLongitude", "")),
}
# ==================================================================
# Step 02: 接单
# ==================================================================
def step02_accept_order(self, main_order_code: str, residue_number: str,
message_code: str = "",
latitude: str = None, longitude: str = None) -> dict:
"""
Postman Body (urlencoded):
residueNumber / perWeight / latitude / messageCode / mainOrderCode / longitude
latitude/longitude 优先取货源接口的 shipperLat/shipperLng,没有则用 config
"""
data = {
"residueNumber": residue_number,
"perWeight": self.dp.get("per_weight", "1"),
"latitude": latitude or self.dp.get("latitude", ""),
"messageCode": message_code,
"mainOrderCode": main_order_code,
"longitude": longitude or self.dp.get("longitude", ""),
}
return self.driver_http.post("/ntocc-tms-api/orderDetail/receive", data=data)
# ==================================================================
# Step 03: 查询待运输单
# ==================================================================
def step03_query_pending_transport(self, page_size: int = 10, page_num: int = 1) -> dict:
"""
Postman Test Script:
pm.collectionVariables.set("code", jsonData.data.records[0].code);
pm.collectionVariables.set("vehicleCode", jsonData.data.records[0].vehicleCode);
pm.collectionVariables.set("orderCode", jsonData.data.records[0].orderCode);
Returns: {"code": str, "vehicleCode": str, "orderCode": str}
"""
params = {
"assignmentStatus": "waitTrans",
"placeOfReceipt": "",
"gmtCreatedEnd": "",
"gmtCreatedStart": "",
"pageSize": page_size,
"placeOfShipment": "",
"pageNum": page_num,
"shipmentName": "",
"status": "1",
"vehicleLicense": "",
}
data = self.driver_http.get("/ntocc-tms-api/orderDetail", params)
records = data.get("data", {}).get("records", [])
if not records:
raise RuntimeError("查询待运输单: 无记录返回")
r = records[0]
return {
"code": r.get("code", ""),
"vehicleCode": r.get("vehicleCode", ""),
"orderCode": r.get("orderCode", ""),
}
# ==================================================================
# Step 04: 装货
# ==================================================================
def step04_load_goods(self, code: str, vehicle_code: str,
img_url: str = "", quantity: str = "",
weight: str = None,
latitude: str = None, longitude: str = None) -> dict:
"""
Postman Pre-request Script: 生成 currentTime
Body: imgUrl / orderType / quantity / code / latitude / weight / vehicleCode / time / longitude
latitude/longitude 优先取货源接口的 shipperLat/shipperLng
"""
if weight is None:
weight = self.dp.get("weight", "1")
body = {
"imgUrl": img_url or self.dp.get("load_img_url", ""),
"orderType": "1",
"quantity": quantity,
"code": code,
"latitude": latitude or self.dp.get("latitude", ""),
"weight": weight,
"vehicleCode": vehicle_code,
"time": _current_time_str(),
"longitude": longitude or self.dp.get("longitude", ""),
}
return self.driver_http.put("/ntocc-tms-api/orderDetail/fill", data=body)
# ==================================================================
# Step 05: 卸货
# ==================================================================
def step05_unload_goods(self, code: str, img_url: str = "",
quantity: str = "", weight: str = None,
latitude: str = None, longitude: str = None) -> dict:
"""
Postman Pre-request Script: 生成 currentTime
Body: imgUrl / code / quantity / latitude / weight / time / longitude
latitude/longitude 优先取货源接口的 consigneeLat/consigneeLng
"""
if weight is None:
weight = self.dp.get("weight", "1")
body = {
"imgUrl": img_url or self.dp.get("unload_img_url", ""),
"code": code,
"quantity": quantity,
"latitude": latitude or self.dp.get("latitude", ""),
"weight": weight,
"time": _current_time_str(),
"longitude": longitude or self.dp.get("longitude", ""),
}
return self.driver_http.put("/ntocc-tms-api/orderDetail/uploadTicketImage", data=body)