| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 |
- # -*- coding: utf-8 -*-
- """扫描萤火商城 PHP 源码,导出接口清单(控制器+方法)为 CSV 和 Markdown"""
- import os, re, csv
- ROOT = r"F:\shop\shop\app"
- OUT = str(ROOT)
- MODULES = ["admin", "api", "store", "timer"]
- MAGIC = re.compile(r"^__")
- def snake(name: str) -> str:
- return re.sub(r"(?<=[a-z0-9])([A-Z])", r"_\1", name).lower()
- rows = []
- for module in MODULES:
- base = os.path.join(ROOT, module, "controller")
- for dirpath, _, files in os.walk(base):
- for fn in sorted(files):
- if not fn.endswith(".php"):
- continue
- path = os.path.join(dirpath, fn)
- rel = os.path.relpath(path, base).replace("\\", "/")
- text = open(path, encoding="utf-8", errors="replace").read()
- # 类名
- m = re.search(r"class\s+(\w+)", text)
- if not m:
- continue
- cls = m.group(1)
- # 控制器 URL(TP6 多级控制器用点号连接,全部转下划线)
- # 例:goods/Goods.php → "goods";goods/Comment.php → "goods.comment"
- parts = rel.split("/")
- parts[-1] = parts[-1][:-4]
- ctrl_url = ".".join(snake(p) for p in parts)
- # 逐方法解析
- for m in re.finditer(r"(/\*\*(.*?)\*/)?\s*(?:protected|public|private)?\s*function\s+(\w+)\s*\(([^)]*)\)", text):
- comment, body_args, name = m.group(2), m.group(4), m.group(3)
- if MAGIC.match(name):
- continue
- # 只统计 public(ThinkPHP 路由可达)
- before = text[: m.start()]
- if "protected" in m.group(0) or "private" in m.group(0):
- continue
- brief = ""
- if comment:
- for line in comment.splitlines():
- line = line.strip().lstrip("*").strip()
- if line and not line.startswith("@"):
- brief = line
- break
- action = snake(name)
- url = f"/{module}/" + (ctrl_url + "/" if ctrl_url else "") + action
- line_no = text[: m.start()].count("\n") + 1
- rows.append({
- "module": module,
- "controller": cls,
- "controller_file": rel,
- "controller_url": ctrl_url,
- "action": name,
- "url": url,
- "args": " ".join(body_args.split()),
- "comment": brief,
- "line": line_no,
- })
- rows.sort(key=lambda r: (MODULES.index(r["module"]), r["controller_file"], r["line"]))
- os.makedirs(OUT, exist_ok=True)
- csv_path = os.path.join(OUT, "API清单.csv")
- with open(csv_path, "w", newline="", encoding="utf-8-sig") as f:
- w = csv.DictWriter(f, fieldnames=list(rows[0].keys()))
- w.writeheader()
- w.writerows(rows)
- md_path = os.path.join(OUT, "API清单.md")
- with open(md_path, "w", encoding="utf-8") as f:
- f.write("# 萤火商城接口清单(Java 重写对齐基线)\n\n")
- f.write("> 自动扫描自 `F:\\shop\\shop`(ThinkPHP 6 多应用路由:`/模块/控制器/方法`,"
- "多级控制器用点号连接)。响应统一为 `{\"status\":200,\"message\":\"success\",\"data\":{...}}`。\n\n")
- total = len(rows)
- f.write(f"**接口总数:{total}**\n\n")
- for module in MODULES:
- sub = [r for r in rows if r["module"] == module]
- f.write(f"\n## 模块 {module}({len(sub)} 个接口)\n\n")
- cur = None
- for r in sub:
- key = r["controller_file"]
- if key != cur:
- cur = key
- f.write(f"\n### {r['controller']}(`app/{module}/controller/{key}`)\n\n")
- f.write("| 方法 | URL | 参数 | 说明 |\n|---|---|---|---|\n")
- args = r["args"].replace("|", "\\|") or "—"
- comment = (r["comment"] or "").replace("|", "\\|") or "—"
- f.write(f"| {r['action']} | `{r['url']}` | {args} | {comment} |\n")
- print(f"接口总数: {len(rows)}")
- from collections import Counter
- from _root import ROOT
- print(Counter(r["module"] for r in rows))
- print("CSV:", csv_path)
- print("MD :", md_path)
|