|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
# bruno-sync 全量对账(票 04 t9):源码 tags 钥匙 vs 文档仓文件夹钥匙 双向差集
|
|
|
|
|
# 钥匙 = (METHOD, url, tag全路径/文档文件夹相对路径)
|
|
|
|
|
# 修正版:注解区扫描(@Operation 可在 mapping 之前或之后),覆盖四种盲区:
|
|
|
|
|
# @PreAuthorize 嵌套括号 / 裸字符串 tags / 无括号 @PostMapping / @Operation 在 @PostMapping 之后
|
|
|
|
|
import os, sys, io, re
|
|
|
|
|
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
|
|
|
|
REPO = r'E:\code\crm-backend-matt'
|
|
|
|
|
DOCS = r'E:\code\crm-api-docs'
|
|
|
|
|
MODS = ['crm-auth','crm-file','crm-lead','crm-opportunity','crm-rule','crm-preference','crm-dict']
|
|
|
|
|
BT = chr(96)
|
|
|
|
|
src = {}
|
|
|
|
|
for m in MODS:
|
|
|
|
|
base = os.path.join(REPO, m, 'src', 'main', 'java')
|
|
|
|
|
for dp, dns, fns in os.walk(base):
|
|
|
|
|
for fn in sorted(fns):
|
|
|
|
|
if not fn.endswith('Controller.java'):
|
|
|
|
|
continue
|
|
|
|
|
txt = open(os.path.join(dp, fn), encoding='utf-8').read()
|
|
|
|
|
mt = re.search(r'@RequestMapping\(\s*"([^"]+)"', txt)
|
|
|
|
|
cls_prefix = mt.group(1) if mt else ''
|
|
|
|
|
ct = re.search(r'@Tag\((?:[^()]*?)name\s*=\s*"([^"]+)"', txt) or re.search(r'@Tag\(\s*"([^"]+)"', txt)
|
|
|
|
|
cls_tag = ct.group(1) if ct else ''
|
|
|
|
|
for mm in re.finditer(r'@(Get|Post|Put|Delete|Patch)Mapping(?:\(([^)]*)\))?', txt):
|
|
|
|
|
verb, args = mm.group(1), mm.group(2) or ''
|
|
|
|
|
pm = re.search(r'"([^"]*)"', args)
|
|
|
|
|
path = pm.group(1) if pm else ''
|
|
|
|
|
wstart = max(0, mm.start() - 800)
|
|
|
|
|
pa = re.search(r'public\s', txt[mm.end():mm.end()+600])
|
|
|
|
|
bend = mm.end() + (pa.start() if pa else 600)
|
|
|
|
|
region = txt[wstart:bend]
|
|
|
|
|
gs = re.findall(r'@Operation\((?:[^()]|\([^()]*\))*\)', region)
|
|
|
|
|
g = gs[-1] if gs else ''
|
|
|
|
|
mtags = []
|
|
|
|
|
if g:
|
|
|
|
|
t = re.search(r'tags\s*=\s*(\{[^}]*\}|"[^"]+")', g, re.S)
|
|
|
|
|
if t:
|
|
|
|
|
mtags = re.findall(r'"([^"]+)"', t.group(1))
|
|
|
|
|
tags = mtags if mtags else ([cls_tag] if cls_tag else [])
|
|
|
|
|
url = cls_prefix + path
|
|
|
|
|
for t in tags:
|
|
|
|
|
src.setdefault((verb.upper(), url, t), []).append(fn)
|
|
|
|
|
|
|
|
|
|
doc = {}
|
|
|
|
|
for dp, dns, fns in os.walk(DOCS):
|
|
|
|
|
dns[:] = [d for d in dns if d.lower() != 'environments']
|
|
|
|
|
rel = os.path.relpath(dp, DOCS).replace('\\', '/')
|
|
|
|
|
tag = '' if rel == '.' else rel
|
|
|
|
|
for fn in sorted(fns):
|
|
|
|
|
if not fn.endswith('.bru') or fn in ('folder.bru', 'collection.bru'):
|
|
|
|
|
continue
|
|
|
|
|
txt = open(os.path.join(dp, fn), encoding='utf-8').read()
|
|
|
|
|
mv = re.search(r'^[ \t]*(get|post|put|delete|patch)[ \t]*\{', txt, re.M | re.I)
|
|
|
|
|
if not mv:
|
|
|
|
|
continue
|
|
|
|
|
verb = mv.group(1).upper()
|
|
|
|
|
ul = re.search(r'url:[^\n]*', txt)
|
|
|
|
|
if not ul:
|
|
|
|
|
continue
|
|
|
|
|
u = ul.group(0).split(':', 1)[1].strip()
|
|
|
|
|
if u.startswith(BT) and u.endswith(BT):
|
|
|
|
|
u = u[1:-1]
|
|
|
|
|
u = re.sub(r'\{\{[^}]*\}\}', '', u).strip()
|
|
|
|
|
u = re.sub(r'^https?://[^/\s]+', '', u).strip()
|
|
|
|
|
u = re.sub(r'^/crm-api(?=/api/)', '', u).strip()
|
|
|
|
|
u = u.split('?', 1)[0].strip()
|
|
|
|
|
doc.setdefault((verb, u, tag), []).append(fn)
|
|
|
|
|
|
|
|
|
|
miss_doc = sorted(set(src) - set(doc))
|
|
|
|
|
miss_src = sorted(set(doc) - set(src))
|
|
|
|
|
print('src keys:', len(src), '; doc keys:', len(doc))
|
|
|
|
|
print('--- doc-has-src-missing', len(miss_doc), '---')
|
|
|
|
|
for k in miss_doc:
|
|
|
|
|
print(' | '.join(k), ' <=', ','.join(src[k]))
|
|
|
|
|
print('--- src-has-doc-missing', len(miss_src), '---')
|
|
|
|
|
for k in miss_src:
|
|
|
|
|
print(' | '.join(k), ' <=', ','.join(doc[k]))
|