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.
118 lines
5.0 KiB
118 lines
5.0 KiB
# -*- coding: utf-8 -*-
|
|
"""票 11 t11-5 — verify_sync.py:bruno-sync 口径对账验证(三向差集)。
|
|
|
|
1. 源码侧:正则扫 crm-opportunity 全部 8 控制器 + crm-rule 商机规则 3 控制器 +
|
|
crm-preference SavedViewController 的 @(Get|Post)Mapping,拼类级 @RequestMapping 得全量钥匙集。
|
|
2. 文件侧:遍历 D:\\code\\crm-api-docs\\商机模块 全部 .bru(排除 folder.bru),
|
|
有 meta tags:[generated] 者从 docs 首行反引号提钥匙(对账钥匙 = METHOD + 路径)。
|
|
3. 报告三向差集:源码有文件无 / 文件有源码无 / 两侧都有。
|
|
预期(handoff §4,口径勘误):源码唯一钥匙 71 全命中、双零差集;
|
|
接口文件 77 = 71 唯一 + 3 接口(page / board/cards / board/stage-summary)
|
|
× 3 页面副本多出的 6 文件。
|
|
|
|
结论口径:sync 若重跑,结构已镜像源码 → 全部「不动」;实参值与示例在 docs 块属表述
|
|
(铁律 1)→ 不会丢。票面「跑一次 /bruno-sync 验证示例不丢」以此对账 + 抽查达成。
|
|
|
|
正则覆盖说明:@PostMapping 允许无参(如 OpportunityCreateController 新建商机,
|
|
端点路径=类级 base 本身),故 Mapping 后的 ("...") 为可选。
|
|
"""
|
|
import io
|
|
import os
|
|
import re
|
|
import sys
|
|
from collections import defaultdict
|
|
|
|
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
|
|
|
REPO = r'd:\code\crm-backend-matt'
|
|
DOCS = os.path.join(r'D:\code\crm-api-docs', '商机模块')
|
|
|
|
# ---------- 1. 源码侧 ----------
|
|
SCAN = [
|
|
(os.path.join(REPO, r'crm-opportunity\src\main\java\com\crm\opportunity\controller'), None),
|
|
(os.path.join(REPO, r'crm-rule\src\main\java\com\crm\rule\controller'),
|
|
['OpportunityPoolRuleController.java', 'OpportunityStageTemplateController.java',
|
|
'OpportunitySchemeCardTemplateController.java']),
|
|
(os.path.join(REPO, r'crm-preference\src\main\java\com\crm\preference\controller'),
|
|
['SavedViewController.java']),
|
|
]
|
|
|
|
re_base = re.compile(r'@RequestMapping\("([^"]+)"\)')
|
|
# Mapping 路径可选:@PostMapping(无参,端点=类级 base)与 @PostMapping("/xxx") 两种形态都覆盖
|
|
re_map = re.compile(r'@(Get|Post)Mapping\b(?:\("([^"]*)"\))?')
|
|
|
|
src_keys = set()
|
|
per_file = []
|
|
for cdir, only in SCAN:
|
|
for fn in sorted(os.listdir(cdir)):
|
|
if not fn.endswith('.java'):
|
|
continue
|
|
if only is not None and fn not in only:
|
|
continue
|
|
txt = open(os.path.join(cdir, fn), encoding='utf-8').read()
|
|
mb = re_base.search(txt)
|
|
base = mb.group(1) if mb else ''
|
|
eps = re_map.findall(txt)
|
|
per_file.append((fn, base, len(eps)))
|
|
for m, p in eps:
|
|
# 无参形态 p=None → 端点路径就是类级 base
|
|
src_keys.add('%s %s' % (m.upper(), base + (p or '')))
|
|
|
|
# ---------- 2. 文件侧 ----------
|
|
re_key = re.compile(r'`(GET|POST) ([^`]+)`')
|
|
re_tags = re.compile(r'tags:\s*\[([^\]]*)\]', re.S)
|
|
|
|
file_keys = defaultdict(list)
|
|
n_files = 0
|
|
for dp, dn, fn in os.walk(DOCS):
|
|
for f in sorted(fn):
|
|
if not f.endswith('.bru') or f == 'folder.bru':
|
|
continue
|
|
path = os.path.join(dp, f)
|
|
txt = open(path, encoding='utf-8').read()
|
|
n_files += 1
|
|
mt = re_tags.search(txt)
|
|
if not mt or 'generated' not in mt.group(1):
|
|
print(' ! 非 generated(私产,跳过):', os.path.relpath(path, DOCS))
|
|
continue
|
|
mk = re_key.search(txt)
|
|
if not mk:
|
|
print(' ! 钥匙提取失败:', os.path.relpath(path, DOCS))
|
|
continue
|
|
file_keys[mk.group(1) + ' ' + mk.group(2).strip()].append(
|
|
os.path.relpath(path, DOCS))
|
|
|
|
# ---------- 3. 三向对比 ----------
|
|
only_src = sorted(src_keys - set(file_keys))
|
|
only_file = sorted(set(file_keys) - src_keys)
|
|
both = src_keys & set(file_keys)
|
|
|
|
print('== 源码侧(12 控制器) ==')
|
|
for fn, base, n in per_file:
|
|
print(' %-46s %-28s %2d 端点' % (fn, base, n))
|
|
print('源码钥匙数:', len(src_keys))
|
|
print()
|
|
print('== 文件侧 ==')
|
|
print('商机模块 .bru 接口文件:', n_files, ' 唯一钥匙:', len(file_keys))
|
|
dup = {k: v for k, v in file_keys.items() if len(v) > 1}
|
|
if dup:
|
|
print('同钥匙多副本(页面归属合法,各副本独立对账):')
|
|
for k, v in sorted(dup.items()):
|
|
print(' %s ×%d → %s' % (k, len(v), '; '.join(v)))
|
|
print()
|
|
print('== 三向对比 ==')
|
|
print('两侧都有:', len(both))
|
|
print('源码有文件无:', len(only_src))
|
|
for k in only_src:
|
|
print(' -', k)
|
|
print('文件有源码无:', len(only_file))
|
|
for k in only_file:
|
|
print(' +', k, '→', '; '.join(file_keys[k]))
|
|
print()
|
|
ok = (len(only_src) == 0 and len(only_file) == 0
|
|
and len(src_keys) == 71 and n_files == 77)
|
|
print('结论:', '✅ 71 唯一钥匙全命中、双零差集;77 文件 = 71 + 3 接口 × 3 页面副本多 6 —— '
|
|
'结构已镜像源码,sync 重跑全部「不动」;实参值与示例在 docs 块属表述(铁律 1),'
|
|
'不会被 sync 丢弃。' if ok
|
|
else '❌ 存在差集或计数不符,见上清单,需人工裁决(铁律 4)。')
|
|
sys.exit(0 if ok else 1)
|
|
|