- GET 全量扫描 279 个接口并留档(HTTP200=90, 业务成功=76)
- POST 安全探测 24 个只读/计算接口(白名单制, 支付/短信/删除类零触碰)
- 订单写入接口契约经 chunk 源码静态分析补全:
orders CRUD / 创建订单{contract_id,drawing_data,pic_url} / windoor_default 保存
- 确认付费墙语义 code=10086 及到期/角色权限响应
- doc/API.md 新增整站采集结果章节, 更新订单模块契约表
64 lines
3.2 KiB
Python
64 lines
3.2 KiB
Python
#!/usr/bin/env python3
|
||
"""POST 安全探测:仅探测搜索/计算/详情语义的接口(空最小请求体)。
|
||
绝不触碰:支付、短信、账号绑定、删除/更新类、文件上传类接口。"""
|
||
import json, os, re, time, urllib.request, urllib.error
|
||
|
||
BASE = "https://zzc-serv.thinkerx.com/api/"
|
||
TOKEN = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczpcL1wvenpjLXNlcnYudGhpbmtlcnguY29tXC9hcGlcL3dlY2hhdC1sb2dpbi13aXRoLXBob25lIiwiaWF0IjoxNzg5OTA5NTIwLCJleHAiOjE3ODk5OTU5MjAsIm5iZiI6MTc4OTkwOTUyMCwianRpIjoiTGVyV0J4VTZONTVOMlNwQSIsInN1YiI6MzU2Mjg1NiwicHJ2IjoiNTRjMjZlNjE2MjNhYTQzMzZkZTQ1YTcxNGIwYzdhYWIxZTJhYzA3NSJ9.MXBrpBh3gseq8I91EQklxsgLfHafUKwNxc24JdcUkcc"
|
||
OUT = r"D:\Code\huamenchuang\data\full-site\post"
|
||
os.makedirs(OUT, exist_ok=True)
|
||
|
||
# 只读/计算语义白名单(详情查询、列表搜索、纯计算、受付费墙保护)
|
||
SAFE_POST = [
|
||
"orders/", "sunny_house_order/", "after_sales_orders/", "windoor_default",
|
||
"order-by-pid", "get_order_process", "record_by_contract_id",
|
||
"contract_bars_with_stock", "contract_share", "contract_share/",
|
||
"cut_solution", "offer-price", "offer-price/",
|
||
"optimize1d-ai", "optimize1d-test", "three-d-data",
|
||
"product_by_warehouse", "product-order-format", "product-order-format/",
|
||
"order_format_by_warehouse", "is-repeat-order", "cj-orders-status",
|
||
"xf_contract_workshop", "ai_product",
|
||
]
|
||
|
||
def safe_name(ep):
|
||
return re.sub(r'[^A-Za-z0-9_-]', '_', ep).strip('_') or 'root'
|
||
|
||
manifest = []
|
||
for ep in SAFE_POST:
|
||
body = json.dumps({"page": 1}).encode()
|
||
req = urllib.request.Request(BASE + ep, method="POST", headers={
|
||
"Authorization": f"Bearer {TOKEN}",
|
||
"X-Requested-With": "XMLHttpRequest",
|
||
"Accept": "application/json",
|
||
"Content-Type": "application/json",
|
||
}, data=body)
|
||
try:
|
||
with urllib.request.urlopen(req, timeout=20) as r:
|
||
status, text = r.status, r.read().decode("utf-8", "replace")
|
||
except urllib.error.HTTPError as e:
|
||
status, text = e.code, e.read().decode("utf-8", "replace")[:4000]
|
||
except Exception as e:
|
||
status, text = -1, str(e)[:200]
|
||
entry = {"endpoint": ep, "method": "POST", "status": status, "size": len(text)}
|
||
try:
|
||
j = json.loads(text)
|
||
if isinstance(j, dict):
|
||
entry["code"] = j.get("code")
|
||
entry["message"] = (j.get("message") or "")[:50]
|
||
entry["data_type"] = type(j.get("data")).__name__
|
||
# 记录校验错误里的字段清单(Laravel validation)
|
||
if j.get("errors"):
|
||
entry["required_fields"] = list(j["errors"].keys())
|
||
if j.get("data") and isinstance(j.get("data"), list):
|
||
entry["data_len"] = len(j["data"])
|
||
except Exception:
|
||
pass
|
||
json.dump({"endpoint": ep, "status": status, "body": text[:100000]},
|
||
open(f"{OUT}\\{safe_name(ep)}.json", "w", encoding="utf-8"), ensure_ascii=False)
|
||
manifest.append(entry)
|
||
print(f"{ep:38s} HTTP {status} code={entry.get('code')} {entry.get('message','')}", flush=True)
|
||
time.sleep(0.3)
|
||
|
||
json.dump(manifest, open(r"D:\Code\huamenchuang\data\full-site\post-manifest.json", "w", encoding="utf-8"), ensure_ascii=False, indent=1)
|
||
print(f"\n完成: {len(SAFE_POST)} 个 POST 探测")
|