Files

118 lines
3.8 KiB
Python
Raw Permalink 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.
"""task-166 HTTP 客户端契约盘点工具。
盘点 backend-java 各 HTTP 客户端(OSS/RustFS/图片下载/紫鸟/LLM)的现状:
超时(connect/read/write/call)、重试、幂等性,产出机器可读契约表供审计文档使用。
本工具只读,不改代码。
"""
from __future__ import annotations
import argparse
import json
import re
import sys
from dataclasses import dataclass, field
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent.parent
CONFIG_ROOT = REPO_ROOT / "src/main/java/com/nanri/aiimage/config"
MAIN_ROOT = REPO_ROOT / "src/main/java"
# 超时/重试配置字段(config 包)
TIMEOUT_FIELD_RE = re.compile(
r"private\s+\w+\s+(\w*(?:Timeout|Retries|Retry|MaxRetries)\w*)\s*=\s*([^;]+);",
re.IGNORECASE,
)
# 代码中 HttpClient/Client 构建的超时/重试配置
CLIENT_CONF_RE = re.compile(
r"(connectTimeout|readTimeout|writeTimeout|callTimeout|operationTimeout|"
r"maxRetries|retry|setConnectTimeout|setReadTimeout|HttpClient\.newBuilder)",
re.IGNORECASE,
)
# 客户端类识别
CLIENT_CLASS_RE = re.compile(
r"(OssStorageService|RustfsObjectStorageService|ZiniaoShopSwitchService|"
r"SimilarAsinLlmService|AppearancePatentLlmClient|ImageDownloader|ImageDownload|"
r"imageDownload|HttpClient|WebClient|RestTemplate)",
)
def extract_timeout_configs() -> list[dict]:
"""从 config 包提取超时/重试配置字段。"""
entries: list[dict] = []
for java_file in sorted(CONFIG_ROOT.rglob("*.java")):
text = java_file.read_text(encoding="utf-8")
for match in TIMEOUT_FIELD_RE.finditer(text):
entries.append(
{
"config_class": java_file.stem,
"field": match.group(1),
"default": match.group(2).strip(),
"source": str(java_file.relative_to(REPO_ROOT)).replace("\\", "/"),
}
)
return entries
def extract_client_sites() -> list[dict]:
"""扫描 main 源码中客户端构建/配置调用点。"""
sites: list[dict] = []
for java_file in sorted(MAIN_ROOT.rglob("*.java")):
text = java_file.read_text(encoding="utf-8")
for line_no, line in enumerate(text.splitlines(), start=1):
if not CLIENT_CONF_RE.search(line) or "import " in line:
continue
if CLIENT_CLASS_RE.search(line) or "Timeout" in line or "Retry" in line:
sites.append(
{
"file": str(java_file.relative_to(REPO_ROOT)).replace(
"\\", "/"
),
"line": line_no,
"snippet": line.strip()[:110],
}
)
return sites
@dataclass
class Report:
timeout_configs: list[dict] = field(default_factory=list)
client_sites: list[dict] = field(default_factory=list)
def to_dict(self) -> dict:
return {
"timeout_configs": self.timeout_configs,
"client_sites": self.client_sites,
}
def scan() -> Report:
return Report(
timeout_configs=extract_timeout_configs(), client_sites=extract_client_sites()
)
def main() -> None:
parser = argparse.ArgumentParser(description="HTTP 客户端契约盘点")
parser.add_argument("--json", help="输出 JSON 报告路径(默认 stdout")
args = parser.parse_args()
report = scan()
payload = report.to_dict()
if args.json:
Path(args.json).write_text(
json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8"
)
else:
print(json.dumps(payload, ensure_ascii=False, indent=2))
print(
f"timeout configs: {len(payload['timeout_configs'])}, "
f"client sites: {len(payload['client_sites'])}",
file=sys.stderr,
)
if __name__ == "__main__":
main()