compare_order_batch.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. # -*- coding: utf-8 -*-
  2. """订单链路实时对比:同一请求分别打线上 PHP 与本地 Java,逐字段 diff。
  3. PHP 侧必须把参数直接拼进 URL(用 requests 的 params 传参会二次编码 s 参数,
  4. ThinkPHP 端会解析失败报 "controller not exists")。
  5. 用法:python compare_order_batch.py [用例名...]
  6. """
  7. import json
  8. import os
  9. import re
  10. import sys
  11. from pathlib import Path
  12. import requests
  13. PHP_BASE = os.environ.get("YOSHOP_PHP_BASE", "https://shop.qinys.cn/index.php?s=")
  14. JAVA_BASE = "http://127.0.0.1:18080"
  15. TOKEN = "testsession123"
  16. SESSION = requests.Session()
  17. SESSION.trust_env = False
  18. CASES = [
  19. ("delivery_list",
  20. "/store/order.delivery/list&page=1", "/store/order.delivery/list?page=1"),
  21. ("delivery_list_filter",
  22. "/store/order.delivery/list&page=1&deliveryMethod=10&searchType=10&searchValue=369583",
  23. "/store/order.delivery/list?page=1&deliveryMethod=10&searchType=10&searchValue=369583"),
  24. ("delivery_list_empty",
  25. "/store/order.delivery/list&page=1&searchValue=NOT-EXIST",
  26. "/store/order.delivery/list?page=1&searchValue=NOT-EXIST"),
  27. ("delivery_detail",
  28. "/store/order.delivery/detail&deliveryId=10001", "/store/order.delivery/detail?deliveryId=10001"),
  29. ("order_detail_10001",
  30. "/store/order/detail&orderId=10001", "/store/order/detail?orderId=10001"),
  31. ("order_detail_10002",
  32. "/store/order/detail&orderId=10002", "/store/order/detail?orderId=10002"),
  33. ("order_list_all",
  34. "/store/order/list&dataType=all&page=1", "/store/order/list?dataType=all&page=1"),
  35. ("refund_list",
  36. "/store/order.refund/list&page=1", "/store/order.refund/list?page=1"),
  37. ("refund_detail_missing",
  38. "/store/order.refund/detail&orderRefundId=1", "/store/order.refund/detail?orderRefundId=1"),
  39. ("traces_disabled",
  40. "/store/order.delivery/traces&deliveryId=10001", "/store/order.delivery/traces?deliveryId=10001"),
  41. ]
  42. def php_token() -> str:
  43. if os.environ.get("YOSHOP_TOKEN"):
  44. return os.environ["YOSHOP_TOKEN"]
  45. src = Path(r"F:\import goods\upload_goods.py").read_text(encoding="utf-8")
  46. m = re.search(r'TOKEN\s*=\s*"([^"]+)"', src)
  47. return m.group(1) if m else TOKEN
  48. def diff(a, b, path="$", out=None, limit=25):
  49. out = out if out is not None else []
  50. if len(out) >= limit:
  51. return out
  52. if type(a) != type(b):
  53. if isinstance(a, (int, float)) and isinstance(b, (int, float)) \
  54. and not isinstance(a, bool) and not isinstance(b, bool):
  55. if float(a) != float(b):
  56. out.append(f"{path}: php={a!r} java={b!r}")
  57. return out
  58. out.append(f"{path}: 类型不同 php={type(a).__name__} java={type(b).__name__} (php={a!r} java={b!r})")
  59. return out
  60. if isinstance(a, dict):
  61. for k in a:
  62. if k not in b:
  63. out.append(f"{path}.{k}: Java 缺失 (php={a[k]!r})")
  64. else:
  65. diff(a[k], b[k], f"{path}.{k}", out, limit)
  66. for k in b:
  67. if k not in a:
  68. out.append(f"{path}.{k}: Java 多出 (java={b[k]!r})")
  69. return out
  70. if isinstance(a, list):
  71. if len(a) != len(b):
  72. out.append(f"{path}: 长度不同 php={len(a)} java={len(b)}")
  73. for i in range(min(len(a), len(b))):
  74. diff(a[i], b[i], f"{path}[{i}]", out, limit)
  75. return out
  76. if a != b:
  77. out.append(f"{path}: php={a!r} java={b!r}")
  78. return out
  79. def main():
  80. only = set(sys.argv[1:])
  81. token = php_token()
  82. ok = fail = 0
  83. for name, php_path, java_path in CASES:
  84. if only and name not in only:
  85. continue
  86. try:
  87. php = SESSION.get(PHP_BASE + php_path, headers={"Access-Token": token}, timeout=60).json()
  88. except Exception as e:
  89. print(f"[skip] {name} 拉取 PHP 失败: {e}")
  90. continue
  91. try:
  92. java = SESSION.get(JAVA_BASE + java_path, headers={"Access-Token": TOKEN}, timeout=60).json()
  93. except Exception as e:
  94. print(f"[FAIL] {name} 拉取 Java 失败: {e}")
  95. fail += 1
  96. continue
  97. if php.get("status") != java.get("status"):
  98. print(f"[DIFF] {name} status php={php.get('status')} java={java.get('status')} "
  99. f"msg php={php.get('message')!r} java={java.get('message')!r}")
  100. fail += 1
  101. continue
  102. if php.get("status") != 200:
  103. # 错误响应也比对 message / data(如物流查询未开启)
  104. diffs = diff(php, java)
  105. if diffs:
  106. fail += 1
  107. print(f"[DIFF] {name} (错误响应)")
  108. for d in diffs:
  109. print(" ", d)
  110. else:
  111. ok += 1
  112. print(f"[PASS] {name} (错误响应一致: {php.get('message')})")
  113. continue
  114. diffs = diff(php.get("data"), java.get("data"))
  115. if diffs:
  116. fail += 1
  117. print(f"[DIFF] {name} ({len(diffs)}+ 处)")
  118. for d in diffs:
  119. print(" ", d)
  120. else:
  121. ok += 1
  122. print(f"[PASS] {name}")
  123. print(f"\n通过 {ok} / 差异 {fail}")
  124. if __name__ == "__main__":
  125. main()