# -*- coding: utf-8 -*- # t10-verify.py — 票 10 验收:源码接口钥匙 ↔ Bruno 集合 docs 首行钥匙 双零差集 # 钥匙 = METHOD + 完整路径(两侧均按 (METHOD, path) 去重比对;tag 多副本在集合侧归一) import io, os, re, sys sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') ROOT = r'D:\code\crm-backend-matt' DOCS = r'D:\code\crm-api-docs' SRC_ROOTS = ['crm-auth/src/main/java', 'crm-file/src/main/java', 'crm-lead/src/main/java', 'crm-opportunity/src/main/java', 'crm-rule/src/main/java', 'crm-preference/src/main/java', 'crm-dict/src/main/java'] def norm(p): if not p: return '' p = '/' + p.lstrip('/') while '//' in p: p = p.replace('//', '/') return p.rstrip('/') or '/' # ---- 源码钥匙 ---- rx_ctrl = re.compile(r'@(?:Rest)?Controller\b') rx_cls = re.compile(r'@RequestMapping\(\s*(?:value\s*=\s*)?"([^"]+)"') rx_map = re.compile(r'@(Get|Post|Put|Delete)Mapping\(\s*(?:value\s*=\s*)?"?([^")\s]*)"?') rx_map0 = re.compile(r'@(Get|Post|Put|Delete)Mapping\b(?!\()') src = {} def add_key(method, path): src.setdefault((method.upper(), norm(path)), []).append(rel) for r in SRC_ROOTS: base = os.path.join(ROOT, r) for root, dirs, files in os.walk(base): for fn in files: if not fn.endswith('.java'): continue p = os.path.join(root, fn) rel = os.path.relpath(p, ROOT) txt = open(p, 'r', encoding='utf-8', errors='replace').read() if not rx_ctrl.search(txt): continue mcls = rx_cls.search(txt) basep = mcls.group(1) if mcls else '' for m in rx_map.finditer(txt): add_key(m.group(1), basep + m.group(2)) for m in rx_map0.finditer(txt): add_key(m.group(1), basep) # ---- 集合钥匙 ---- rx_doc = re.compile(r'docs\s*\{[^`]*?`([^`]+)`', re.S) doc = {} bad = [] for root, dirs, files in os.walk(DOCS): if 'environments' in root: continue for fn in files: if not fn.endswith('.bru') or fn == 'collection.bru': continue p = os.path.join(root, fn) txt = open(p, 'r', encoding='utf-8-sig', errors='replace').read() if 'generated' not in txt[:400]: continue m = rx_doc.search(txt) if not m: bad.append(('NO-KEY', os.path.relpath(p, DOCS))) continue parts = m.group(1).strip().split(None, 1) if len(parts) != 2: bad.append(('BAD-KEY', os.path.relpath(p, DOCS) + ' :: ' + m.group(1))) continue rel = os.path.relpath(p, DOCS) doc.setdefault((parts[0].upper(), norm(parts[1])), []).append(rel) # ---- 比对 ---- missing = sorted(set(src) - set(doc)) extra = sorted(set(doc) - set(src)) print('source keys: %d collection keys: %d' % (len(src), len(doc))) print('') if missing: print('---- MISSING(源码有、集合无)%d ----' % len(missing)) for k in missing: print(' %-6s %-58s %s' % (k[0], k[1], src[k][0])) if extra: print('---- EXTRA(集合有、源码无)%d ----' % len(extra)) for k in extra: print(' %-6s %-58s %s' % (k[0], k[1], doc[k][0])) if bad: print('---- UNRECOGNIZED %d ----' % len(bad)) for b in bad: print(' %s %s' % b) print('') print('RESULT:', 'PASS 双零差集' if not missing and not extra and not bad else 'FAIL')