compare_batch.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. # -*- coding: utf-8 -*-
  2. """Java 实现与线上 PHP 黄金样本逐字段对比。
  3. 用法:python compare_batch.py [名称...]
  4. """
  5. import sys
  6. import json
  7. import requests
  8. from pathlib import Path
  9. from _root import ROOT
  10. GOLDEN = Path(str(ROOT / "golden"))
  11. BASE = "http://localhost:18080"
  12. TOKEN = "testsession123"
  13. # 本地服务不走系统代理(沙箱代理会把 127.0.0.1 转发成 502)
  14. SESSION = requests.Session()
  15. SESSION.trust_env = False
  16. # (黄金样本名, Java 路径, 参数)
  17. CASES = [
  18. ("store_info", "/store/store/info", None),
  19. ("store_menu_list", "/store/store.menu/list", None),
  20. ("store_role_list", "/store/store.role/list", None),
  21. ("store_user_list", "/store/store.user/list", {"page": 1}),
  22. ("store_address_all", "/store/store.address/all", None),
  23. ("category_list", "/store/category/list", None),
  24. ("goods_service_list", "/store/goods.service/list", {"page": 1}),
  25. ("goods_service_all", "/store/goods.service/all", None),
  26. ("files_list", "/store/files/list", {"page": 1}),
  27. ("files_group_list", "/store/files.group/list", None),
  28. ("region_all", "/store/region/all", None),
  29. ("region_tree", "/store/region/tree", None),
  30. ("user_list", "/store/user/list", {"page": 1}),
  31. ("user_grade_all", "/store/user.grade/all", None),
  32. ("order_list", "/store/order/list", {"page": 1, "dataType": "all"}),
  33. ("setting_detail", "/store/setting/detail", {"key": "store"}),
  34. ("setting_cache_items", "/store/setting.cache/items", None),
  35. ("setting_delivery_all", "/store/setting.delivery/all", None),
  36. ("setting_express_all", "/store/setting.express/all", None),
  37. ("setting_printer_all", "/store/setting.printer/all", None),
  38. ("page_list", "/store/page/list", {"page": 1}),
  39. ("market_coupon_list", "/store/market.coupon/list", {"page": 1}),
  40. ("market_coupon_detail_10001", "/store/market.coupon/detail", {"couponId": 10001}),
  41. ("market_coupon_detail_10002", "/store/market.coupon/detail", {"couponId": 10002}),
  42. ("market_coupon_detail_0", "/store/market.coupon/detail", {"couponId": 99999}),
  43. ("market_points_log", "/store/market.points/log", {"page": 1}),
  44. ("recharge_plan_list", "/store/market.recharge.plan/list", {"page": 1}),
  45. ("article_list", "/store/content.article/list", {"page": 1}),
  46. ("article_category_list", "/store/content.article.category/list", None),
  47. ("help_list", "/store/content.help/list", {"page": 1}),
  48. ("user_balance_log", "/store/user.balance/log", {"page": 1}),
  49. ]
  50. def diff(a, b, path="$", out=None, limit=12):
  51. out = out if out is not None else []
  52. if len(out) >= limit:
  53. return out
  54. if type(a) != type(b):
  55. # int/float 视为同类数值
  56. if isinstance(a, (int, float)) and isinstance(b, (int, float)) and not isinstance(a, bool) and not isinstance(b, bool):
  57. if float(a) != float(b):
  58. out.append(f"{path}: php={a!r} java={b!r}")
  59. return out
  60. out.append(f"{path}: 类型不同 php={type(a).__name__} java={type(b).__name__} (php={a!r} java={b!r})")
  61. return out
  62. if isinstance(a, dict):
  63. for k in a:
  64. if k not in b:
  65. out.append(f"{path}.{k}: Java 缺失 (php={a[k]!r})")
  66. else:
  67. diff(a[k], b[k], f"{path}.{k}", out, limit)
  68. for k in b:
  69. if k not in a:
  70. out.append(f"{path}.{k}: Java 多出 (java={b[k]!r})")
  71. return out
  72. if isinstance(a, list):
  73. if len(a) != len(b):
  74. out.append(f"{path}: 长度不同 php={len(a)} java={len(b)}")
  75. for i in range(min(len(a), len(b))):
  76. diff(a[i], b[i], f"{path}[{i}]", out, limit)
  77. return out
  78. if a != b:
  79. out.append(f"{path}: php={a!r} java={b!r}")
  80. return out
  81. def main():
  82. only = set(sys.argv[1:])
  83. ok = fail = skip = 0
  84. for name, path, params in CASES:
  85. if only and name not in only:
  86. continue
  87. golden_file = GOLDEN / f"{name}.json"
  88. if not golden_file.exists():
  89. print(f"[skip] {name} 无黄金样本")
  90. skip += 1
  91. continue
  92. php = json.loads(golden_file.read_text(encoding="utf-8"))
  93. r = SESSION.get(BASE + path, headers={"Access-Token": TOKEN}, params=params or {}, timeout=90)
  94. try:
  95. java = r.json()
  96. except Exception:
  97. print(f"[FAIL] {name}: 非 JSON HTTP={r.status_code} {r.text[:150]}")
  98. fail += 1
  99. continue
  100. if java.get("status") != 200:
  101. print(f"[FAIL] {name}: java status={java.get('status')} msg={java.get('message')}")
  102. fail += 1
  103. continue
  104. diffs = diff(php.get("data"), java.get("data"))
  105. if diffs:
  106. fail += 1
  107. print(f"[DIFF] {name} ({len(diffs)}+ 处)")
  108. for d in diffs:
  109. print(" ", d)
  110. else:
  111. ok += 1
  112. print(f"[PASS] {name}")
  113. print(f"\n通过 {ok} / 差异 {fail} / 跳过 {skip}")
  114. if __name__ == "__main__":
  115. main()