|
|
@@ -0,0 +1,164 @@
|
|
|
+# -*- coding: utf-8 -*-
|
|
|
+"""把 store / admin 模块控制器的 @RequestMapping(method={GET,POST}) 改成 @GetMapping / @PostMapping。
|
|
|
+
|
|
|
+HTTP 方法依据两个后台前端仓库的实际 axios 调用:
|
|
|
+ store: F:/shop/yoshop2.0-store/src/api/**/*.js
|
|
|
+ admin: F:/shop/yoshop2.0-admin/src/api/api.config.js + 各调用文件(接口少,显式列表)
|
|
|
+用法:python migrate_store_admin_mapping.py [--apply]
|
|
|
+"""
|
|
|
+import re
|
|
|
+import sys
|
|
|
+from pathlib import Path
|
|
|
+
|
|
|
+sys.path.insert(0, str(Path(__file__).parent))
|
|
|
+from _root import ROOT # noqa: E402
|
|
|
+
|
|
|
+JAVA = ROOT / "yoshop-java/src/main/java/cn/yoshop"
|
|
|
+STORE_API = Path(r"F:/shop/yoshop2.0-store/src/api")
|
|
|
+
|
|
|
+APPLY = "--apply" in sys.argv
|
|
|
+
|
|
|
+# admin 前端接口 -> 方法(api.config.js 里的 key 与调用处 method 对应)
|
|
|
+ADMIN_METHODS = {
|
|
|
+ "/admin/passport/login": "Post",
|
|
|
+ "/admin/passport/logout": "Post",
|
|
|
+ "/admin/admin.user/detail": "Get",
|
|
|
+ "/admin/admin.user/renew": "Post",
|
|
|
+ "/admin/store/index": "Get",
|
|
|
+ "/admin/store/superLogin": "Get",
|
|
|
+ "/admin/store/recycle": "Get",
|
|
|
+ "/admin/store/add": "Post",
|
|
|
+ "/admin/store/edit": "Post",
|
|
|
+ "/admin/store/recovery": "Post",
|
|
|
+ "/admin/store/delete": "Post",
|
|
|
+ "/admin/store/move": "Post",
|
|
|
+ "/admin/store.api/index": "Get",
|
|
|
+ "/admin/store.api/add": "Post",
|
|
|
+ "/admin/store.api/edit": "Post",
|
|
|
+ "/admin/store.api/delete": "Post",
|
|
|
+ "/admin/store.menu/index": "Get",
|
|
|
+ "/admin/store.menu/info": "Get",
|
|
|
+ "/admin/store.menu/add": "Post",
|
|
|
+ "/admin/store.menu/edit": "Post",
|
|
|
+ "/admin/store.menu/setApis": "Post",
|
|
|
+ "/admin/store.menu/delete": "Post",
|
|
|
+ "/admin/setting.cache/clear": "Post",
|
|
|
+ "/admin/setting.science/info": "Get",
|
|
|
+ "/admin/setting.queue/test": "Post",
|
|
|
+ "/admin/setting.timer/test": "Post",
|
|
|
+}
|
|
|
+
|
|
|
+MAP_RE = re.compile(r"(\w+)\s*:\s*'([^']+)'")
|
|
|
+CALL_RE = re.compile(r"axios\(\{(.*?)\}\)", re.S)
|
|
|
+URL_RE = re.compile(r"url:\s*([A-Za-z0-9_.]+|'[^']*')")
|
|
|
+METHOD_RE = re.compile(r"method:\s*'(\w+)'")
|
|
|
+
|
|
|
+
|
|
|
+def parse_store_front():
|
|
|
+ table = {}
|
|
|
+ for f in sorted(STORE_API.rglob("*.js")):
|
|
|
+ text = f.read_text(encoding="utf-8", errors="ignore")
|
|
|
+ const = re.search(r"const\s+api\s*=\s*\{(.*?)\n\}", text, re.S)
|
|
|
+ local = dict(MAP_RE.findall(const.group(1))) if const else {}
|
|
|
+ for block in CALL_RE.findall(text):
|
|
|
+ um, mm = URL_RE.search(block), METHOD_RE.search(block)
|
|
|
+ if not um or not mm:
|
|
|
+ continue
|
|
|
+ url = um.group(1).strip("'")
|
|
|
+ if not url.startswith("/"):
|
|
|
+ parts = [p for p in url.split(".") if p != "api"]
|
|
|
+ if parts[-1] not in local:
|
|
|
+ print(f"!! 跳过无法解析的 url: {url} ({f.name})")
|
|
|
+ continue
|
|
|
+ url = local[parts[-1]]
|
|
|
+ table["/store" + url] = mm.group(1).capitalize()
|
|
|
+ return table
|
|
|
+
|
|
|
+
|
|
|
+ANN_RE = re.compile(r"@RequestMapping\((.*?)\)", re.S)
|
|
|
+VALUE_RE = re.compile(r'value\s*=\s*(\{[^}]*\}|"[^"]*")')
|
|
|
+METHOD_ATTR_RE = re.compile(r'method\s*=\s*(RequestMethod\.\w+|\{[^}]*\})')
|
|
|
+PRODUCES_RE = re.compile(r'produces\s*=\s*("[^"]*"|[A-Za-z0-9_.]+)')
|
|
|
+
|
|
|
+
|
|
|
+def norm_path(p):
|
|
|
+ if "_" in p:
|
|
|
+ head, *rest = p.split("_")
|
|
|
+ return head + "".join(w[:1].upper() + w[1:] for w in rest)
|
|
|
+ return p
|
|
|
+
|
|
|
+
|
|
|
+def migrate(module: str, methods: dict):
|
|
|
+ ctrl = JAVA / module / "controller"
|
|
|
+ total = 0
|
|
|
+ for f in sorted(ctrl.glob("*.java")):
|
|
|
+ src = f.read_text(encoding="utf-8")
|
|
|
+ cls = re.search(r'^@RequestMapping\("([^"]+)"\)', src, re.M)
|
|
|
+ prefix = cls.group(1) if cls else ""
|
|
|
+ missed = []
|
|
|
+
|
|
|
+ def repl(m):
|
|
|
+ nonlocal total
|
|
|
+ body = m.group(1)
|
|
|
+ vm = VALUE_RE.search(body)
|
|
|
+ if not vm:
|
|
|
+ return m.group(0)
|
|
|
+ value_expr = vm.group(1)
|
|
|
+ paths = re.findall(r'"([^"]*)"', value_expr)
|
|
|
+ full = prefix + paths[0]
|
|
|
+ key = norm_path(full)
|
|
|
+ if key not in methods:
|
|
|
+ missed.append(full)
|
|
|
+ return m.group(0)
|
|
|
+ ann = methods[key]
|
|
|
+ parts = [f"value = {value_expr}"]
|
|
|
+ pm = PRODUCES_RE.search(body)
|
|
|
+ if pm:
|
|
|
+ parts.append(f"produces = {pm.group(1)}")
|
|
|
+ total += 1
|
|
|
+ return f"@{ann}Mapping({', '.join(parts)})"
|
|
|
+
|
|
|
+ new_src = ANN_RE.sub(repl, src)
|
|
|
+ if missed:
|
|
|
+ print(f" !! {module}/{f.name} 前端未找到: {missed}")
|
|
|
+ if new_src == src:
|
|
|
+ continue
|
|
|
+ if "RequestMethod." not in new_src:
|
|
|
+ new_src = new_src.replace(
|
|
|
+ "import org.springframework.web.bind.annotation.RequestMethod;\n", "")
|
|
|
+ for name in ("GetMapping", "PostMapping"):
|
|
|
+ imp = f"import org.springframework.web.bind.annotation.{name};"
|
|
|
+ if f"@{name}(" in new_src and imp not in new_src:
|
|
|
+ new_src = new_src.replace(
|
|
|
+ "import org.springframework.web.bind.annotation.RequestMapping;",
|
|
|
+ imp + "\nimport org.springframework.web.bind.annotation.RequestMapping;", 1)
|
|
|
+ # 组内排序,tools.jackson / com.baomidou 保持原有靠前位置
|
|
|
+ lines = new_src.split("\n")
|
|
|
+ out, i = [], 0
|
|
|
+ while i < len(lines):
|
|
|
+ if lines[i].startswith("import "):
|
|
|
+ j = i
|
|
|
+ while j < len(lines) and lines[j].startswith("import "):
|
|
|
+ j += 1
|
|
|
+ grp = lines[i:j]
|
|
|
+ head = [x for x in grp if x.startswith(("import com.baomidou.", "import tools.jackson."))]
|
|
|
+ rest = sorted(x for x in grp if x not in head)
|
|
|
+ out.extend(sorted(head) + rest)
|
|
|
+ i = j
|
|
|
+ else:
|
|
|
+ out.append(lines[i])
|
|
|
+ i += 1
|
|
|
+ new_src = "\n".join(out)
|
|
|
+ if APPLY:
|
|
|
+ f.write_text(new_src, encoding="utf-8")
|
|
|
+ return total
|
|
|
+
|
|
|
+
|
|
|
+store_methods = parse_store_front()
|
|
|
+# 前端不调用的 PHP 生命周期钩子(无副作用、无请求体):按浏览器/探活方式用 GET
|
|
|
+store_methods["/store/controller/initialize"] = "Get"
|
|
|
+store_methods["/store/statistics.data/initialize"] = "Get"
|
|
|
+print("store 前端接口数:", len(store_methods))
|
|
|
+n1 = migrate("store", store_methods)
|
|
|
+n2 = migrate("admin", ADMIN_METHODS)
|
|
|
+print(f"{'已改写' if APPLY else '待改写'} store={n1} admin={n2}")
|