Files
Yb-QaAutomationHub/tests/core/db_helper.py
T
xst 52b73f456c 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 兼容
2026-09-08 11:38:17 +08:00

152 lines
5.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""MySQL 数据库操作工具 — 测试中的数据库校验/数据修改。
连接配置在 tests/config/test_config.yaml 的 database 段维护。
Usage:
from tests.core.db_helper import update_risk_control_status
affected = update_risk_control_status("RHJC040026071600021457570057", status=1)
"""
import pymysql
from contextlib import contextmanager
from tests.core.config_loader import load_config
def _resolve_config(config: dict | None) -> dict:
"""显式传入优先,否则读 yaml 的 database 段。"""
if config is not None:
return config
cfg = dict(load_config()["database"])
cfg.setdefault("charset", "utf8mb4")
return cfg
def get_connection(config: dict | None = None):
"""获取数据库连接。"""
return pymysql.connect(**_resolve_config(config))
def execute_sql(sql: str, params: tuple | None = None, config: dict | None = None) -> int:
"""执行 SQL(INSERT/UPDATE/DELETE),返回影响行数。"""
conn = get_connection(config)
try:
with conn.cursor() as cursor:
affected = cursor.execute(sql, params)
conn.commit()
return affected
finally:
conn.close()
def query_sql(sql: str, params: tuple | None = None, config: dict | None = None) -> list[dict]:
"""执行查询 SQL,返回结果列表。"""
conn = get_connection(config)
try:
with conn.cursor(pymysql.cursors.DictCursor) as cursor:
cursor.execute(sql, params)
return cursor.fetchall()
finally:
conn.close()
def update_risk_control_status(order_code: str, status: int = 1, config: dict | None = None) -> int:
"""更新运单风控状态。
执行 SQL:
UPDATE tms_order_detail_ext SET risk_control_status = <status>
WHERE order_detail_code IN (
SELECT `code` FROM `tms_order_detail` WHERE order_code = '<order_code>'
)
Args:
order_code: 运输单号(对应 tms_order_detail.order_code)
status: 风控状态值,默认 1
config: 数据库连接配置(不传则读 yaml database 段)
Returns:
影响行数
"""
sql = (
"UPDATE tms_order_detail_ext SET risk_control_status = %s "
"WHERE order_detail_code IN ("
" SELECT `code` FROM `tms_order_detail` WHERE order_code = %s"
")"
)
return execute_sql(sql, (status, order_code), config)
class Db:
"""长连接数据库客户端 — 用例/pipeline 级复用。
与上面的模块级函数(每语句新建连接)不同,本类在一个实例内复用单条连接:
- execute/query 前自动 ping 重连,规避长空闲连接被服务端断开
- 事务外 execute 保持逐语句提交(与旧行为一致)
- transaction() 内的 execute 不再单独提交,由上下文统一 commit/rollback
- mogrify() 渲染参数化 SQL 为完整语句(dry-run 预览用,不执行)
Usage:
db = Db() # 不传 config 则读 yaml database 段
affected = db.execute("UPDATE t SET a = %s WHERE code = %s", (1, code))
rows = db.query("SELECT * FROM t WHERE code = %s", (code,))
with db.transaction():
db.execute(sql1)
db.execute(sql2)
db.close()
"""
def __init__(self, config: dict | None = None):
self._kwargs = _resolve_config(config)
self._conn: pymysql.connections.Connection | None = None
self._in_transaction = False
def _connection(self):
if self._conn is None:
self._conn = pymysql.connect(**self._kwargs)
else:
self._conn.ping(reconnect=True)
return self._conn
def execute(self, sql: str, params: tuple | None = None) -> int:
"""执行写语句,返回影响行数。事务内调用不单独提交。"""
conn = self._connection()
with conn.cursor() as cursor:
affected = cursor.execute(sql, params)
if not self._in_transaction:
conn.commit()
return affected
def query(self, sql: str, params: tuple | None = None) -> list[dict]:
"""执行查询,返回 DictCursor 结果列表。"""
conn = self._connection()
with conn.cursor(pymysql.cursors.DictCursor) as cursor:
cursor.execute(sql, params)
return cursor.fetchall()
@contextmanager
def transaction(self):
"""事务上下文:块内所有 execute 在退出时统一提交,异常统一回滚。"""
conn = self._connection()
self._in_transaction = True
try:
yield conn
conn.commit()
except Exception:
conn.rollback()
raise
finally:
self._in_transaction = False
def mogrify(self, sql: str, params: tuple | None = None) -> str:
"""渲染参数化 SQL 为最终语句(含引号转义),仅预览不执行。"""
conn = self._connection()
with conn.cursor() as cursor:
return cursor.mogrify(sql, params)
def close(self) -> None:
"""关闭连接(幂等,可重复调用)。"""
if self._conn is not None:
self._conn.close()
self._conn = None