compare_batch.py 4.9 KB

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