轻松聘
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

188 lines
7.0 KiB

import json, re
with open("scripts/openapi.json", encoding="utf-8") as f:
spec = json.load(f)
real_paths = set()
for p, methods in spec.get("paths", {}).items():
for m in methods:
real_paths.add(p)
real_base = set()
for p in real_paths:
seg = [s for s in p.split("/") if s and "{" not in s and "$" not in s]
if seg:
real_base.add("/" + seg[0])
with open("src/api/api.ts", encoding="utf-8") as f:
raw = f.read()
lines = raw.split("\n") # keep as list, we will rejoin
func_re = re.compile(r"^(\s*)(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\(")
static_req = re.compile(r"request(?:<[^>]*>)?\(\s*['\"]([^'\"]+)['\"]")
tmpl_req = re.compile(r"request(?:<[^>]*>)?\(\s*`([^`]*)`")
# find function start indices
starts = []
for i, ln in enumerate(lines):
m = func_re.match(ln)
if m:
starts.append((m.group(2), i))
func_ranges = []
for idx, (name, s) in enumerate(starts):
e = starts[idx+1][1] if idx+1 < len(starts) else len(lines)
func_ranges.append((name, s, e))
PATH_FIX_STATIC = {
"fetchConversations": ("'/messages/conversations'", "'/conversations'"),
"fetchNotifications": ("'/messages/notifications'", "'/notifications'"),
"fetchInterviews": ("'/messages/interviews'", "'/interviews/me'"),
"fetchJobPreference": ("'/profile/me/job-preference'", "'/profile/me/preferences'"),
"saveJobPreference": ("'/profile/me/job-preference'", "'/profile/me/preferences'"),
"fetchResume": ("'/resume'", "'/profile/me/resume'"),
"fetchFavorites": ("'/profile/me/favorites'", "'/jobs/favorites'"),
}
JOBPROGRESS_OLD = "'/tools/job-progress', { method: 'GET', auth: false }"
JOBPROGRESS_NEW = "'/profile/me/career-progress', { method: 'GET' }"
TEMPLATE_FIX = {
"fetchRankData": ("/jobs/rank", "/jobs/${jobId}/competition-ranking"),
"fetchCandidateDetail": ("/enterprise/candidates/${candidateId}", "/company/candidates/${candidateId}"),
"fetchInterviewScore": ("/enterprise/interviews/${interviewId}/score", "/interviews/${interviewId}/score"),
"fetchInterviewScoreDraft": ("/enterprise/interviews/${interviewId}/score/draft", "/interviews/${interviewId}/score"),
"fetchConversation": ("/chat/conversations/${id}", "/conversations/${id}"),
}
def get_info(sl):
static = []; tmpl = []
hmi = any("if (isMockMode())" in l for l in sl)
hnmi = any("if (!isMockMode())" in l for l in sl)
for l in sl:
m = static_req.search(l)
if m: static.append(m.group(1))
t = tmpl_req.search(l)
if t: tmpl.append(t.group(1))
return static, tmpl, hmi, hnmi
def is_phantom(p):
if p in real_paths: return False
segs = [s for s in p.split("/") if s and "{" not in s and "$" not in s]
base = "/" + segs[0] if segs else ""
return base not in real_base
DAI_RE = re.compile(r'【待确认[^】]*】')
def strip_dai(line):
if DAI_RE.search(line):
return DAI_RE.sub('', line).rstrip() + "\n"
return line
changed = {"pathfix": [], "revert": [], "strip_only": []}
new_full = []
def find_close(sl, open_i):
depth = 0
for j in range(open_i, len(sl)):
depth += sl[j].count("{") - sl[j].count("}")
if depth == 0 and j > open_i:
return j
return None
# preserve leading lines before the first function (imports, top-level consts, banners)
new_full = list(lines[0:func_ranges[0][1]])
for name, s, e in func_ranges:
sl = lines[s:e]
static, tmpl, hmi, hnmi = get_info(sl)
in_openapi = any(p in real_paths for p in static) or any(not is_phantom(t) for t in tmpl)
dai = any("【待确认】" in l for l in sl)
is_pathfix = name in PATH_FIX_STATIC or name in TEMPLATE_FIX or name == "fetchJobProgress"
phantom = (dai or any(is_phantom(p) for p in static) or any(is_phantom(t) for t in tmpl)) and not in_openapi
if is_pathfix:
out = []
for ln in sl:
if name in PATH_FIX_STATIC:
o, n = PATH_FIX_STATIC[name]
if o in ln: ln = ln.replace(o, n)
ou = o.strip("'\"")
nu = n.strip("'\"")
if ou in ln: ln = ln.replace(ou, nu)
elif name == "fetchJobProgress":
if JOBPROGRESS_OLD in ln: ln = ln.replace(JOBPROGRESS_OLD, JOBPROGRESS_NEW)
if "/tools/job-progress" in ln: ln = ln.replace("/tools/job-progress", "/profile/me/career-progress")
elif name in TEMPLATE_FIX:
o, n = TEMPLATE_FIX[name]
if o in ln: ln = ln.replace(o, n)
ou = o.replace("`", "")
nu = n.replace("`", "")
if ou in ln: ln = ln.replace(ou, nu)
out.append(strip_dai(ln))
new_full.extend(out)
changed["pathfix"].append(name)
continue
if not phantom:
if dai:
new_full.extend(strip_dai(ln) for ln in sl)
else:
new_full.extend(sl)
continue
# REVERT (phantom -> always mock)
if hnmi:
out = []
i = 0
while i < len(sl):
if "if (!isMockMode())" in sl[i]:
ci = find_close(sl, i)
i = (ci + 1) if ci is not None else i + 1
continue
out.append(strip_dai(sl[i]))
i += 1
new_full.extend(out)
changed["revert"].append(name)
else:
# pattern A: collapse if(isMockMode()){...} and drop trailing return request
out = []
i = 0
collapsed = False
while i < len(sl):
if "if (isMockMode())" in sl[i]:
ci = find_close(sl, i)
if ci is not None:
inner = sl[i+1:ci]
inner_out = []
for il in inner:
if "return request" in il:
continue
if il.lstrip().startswith("//") and "【待确认】" in il:
continue
inner_out.append(il)
inner_out = [l[2:] if l.startswith(" ") else l for l in inner_out]
out.append(" // 后端未提供 OpenAPI 契约:保持 mock(翻真实开关也不触网),待后端就绪再接真实分支")
out.extend(inner_out)
i = ci + 1
collapsed = True
continue
if "return request" in sl[i]:
i += 1
continue
out.append(strip_dai(sl[i]))
i += 1
if not collapsed:
# dai but no isMockMode block and no real branch -> just strip
changed["strip_only"].append(name)
else:
changed["revert"].append(name)
new_full.extend(out)
# preserve trailing lines after the last function
new_full.extend(lines[func_ranges[-1][2]:])
with open("src/api/api.ts", "w", encoding="utf-8") as f:
f.write("\n".join(new_full))
print("PATHFIX:", len(changed["pathfix"]))
print("REVERT:", len(changed["revert"]))
print("STRIP_ONLY:", len(changed["strip_only"]), changed["strip_only"])
print("--- revert ---")
for n in changed["revert"]:
print(" ", n)