This commit is contained in:
@@ -215,11 +215,6 @@ def compare_parameters(
|
||||
return changes
|
||||
|
||||
|
||||
def _method_identity(ep: ApiEndpoint) -> Tuple[str, str]:
|
||||
"""生成方法的唯一标识:(source_file, method_name)。"""
|
||||
return (ep.source_file or "", ep.method_name)
|
||||
|
||||
|
||||
def compare_endpoints(
|
||||
old_endpoints: Dict[str, ApiEndpoint],
|
||||
new_endpoints: Dict[str, ApiEndpoint],
|
||||
@@ -227,142 +222,170 @@ def compare_endpoints(
|
||||
"""
|
||||
对比新旧两个版本的全部 Controller 端点,生成变更报告列表。
|
||||
|
||||
匹配策略(方案 1):
|
||||
- 使用 (source_file, method_name) 作为「同一个 Java 方法」的唯一标识。
|
||||
- 只要同一个 Java 方法被修改,就分别判断「请求方式是否变」和「路径是否变」。
|
||||
- 支持「同时改请求方式 + 路径」时生成两条独立报告。
|
||||
- 支持「同时改请求方式/路径 + 参数」时生成多条独立报告。
|
||||
支持以下变更类型检测:
|
||||
- HTTP 方法变更(GET → POST 等)
|
||||
- URI 路径变更(路径重命名)
|
||||
- 新增 / 删除接口
|
||||
- 参数变更
|
||||
"""
|
||||
reports: List[EndpointChangeReport] = []
|
||||
|
||||
# 1. 构建基于方法标识的映射
|
||||
old_by_identity: Dict[Tuple[str, str], List[ApiEndpoint]] = {}
|
||||
new_by_identity: Dict[Tuple[str, str], List[ApiEndpoint]] = {}
|
||||
old_keys = set(old_endpoints.keys())
|
||||
new_keys = set(new_endpoints.keys())
|
||||
|
||||
for ep in old_endpoints.values():
|
||||
identity = _method_identity(ep)
|
||||
old_by_identity.setdefault(identity, []).append(ep)
|
||||
removed_keys = old_keys - new_keys
|
||||
added_keys = new_keys - old_keys
|
||||
common_keys = old_keys & new_keys
|
||||
|
||||
for ep in new_endpoints.values():
|
||||
identity = _method_identity(ep)
|
||||
new_by_identity.setdefault(identity, []).append(ep)
|
||||
# 收集未匹配的 removed / added
|
||||
unmatched_removed: List[Tuple[str, ApiEndpoint]] = []
|
||||
unmatched_added: List[Tuple[str, ApiEndpoint]] = []
|
||||
|
||||
all_identities = set(old_by_identity.keys()) | set(new_by_identity.keys())
|
||||
for key in removed_keys:
|
||||
unmatched_removed.append((key, old_endpoints[key]))
|
||||
for key in added_keys:
|
||||
unmatched_added.append((key, new_endpoints[key]))
|
||||
|
||||
for identity in all_identities:
|
||||
old_list = old_by_identity.get(identity, [])
|
||||
new_list = new_by_identity.get(identity, [])
|
||||
matched_removed: Set[str] = set()
|
||||
matched_added: Set[str] = set()
|
||||
|
||||
# 简单场景:同一个方法在旧版和新版各出现一次
|
||||
if len(old_list) == 1 and len(new_list) == 1:
|
||||
old_ep = old_list[0]
|
||||
new_ep = new_list[0]
|
||||
|
||||
# 判断请求方式是否改变
|
||||
if old_ep.http_method != new_ep.http_method:
|
||||
# 1. HTTP 方法变更检测(uri + controller 相同,但 method 不同)
|
||||
# 放宽匹配条件:只要同一个 Controller 的同一个 URI 请求方式改变,就识别为「修改请求方式」
|
||||
# 不再要求 method_name 相同(允许方法重命名场景)
|
||||
# 如果同时有参数变更,生成两条独立报告(方法变更 + 参数变更),互不干扰
|
||||
for r_key, r_ep in unmatched_removed:
|
||||
for a_key, a_ep in unmatched_added:
|
||||
if a_key in matched_added:
|
||||
continue
|
||||
if (
|
||||
r_ep.uri == a_ep.uri
|
||||
and r_ep.controller_class == a_ep.controller_class
|
||||
and r_ep.http_method != a_ep.http_method
|
||||
):
|
||||
# 先生成纯方法变更报告
|
||||
reports.append(
|
||||
EndpointChangeReport(
|
||||
uri=new_ep.uri,
|
||||
http_method=new_ep.http_method,
|
||||
controller_class=new_ep.controller_class,
|
||||
method_name=new_ep.method_name,
|
||||
source_file=new_ep.source_file,
|
||||
uri=a_ep.uri,
|
||||
http_method=a_ep.http_method,
|
||||
controller_class=a_ep.controller_class,
|
||||
method_name=a_ep.method_name,
|
||||
source_file=a_ep.source_file,
|
||||
is_method_changed=True,
|
||||
old_http_method=old_ep.http_method,
|
||||
old_http_method=r_ep.http_method,
|
||||
)
|
||||
)
|
||||
# 如果同时有参数变更,额外生成参数报告
|
||||
param_changes = compare_parameters(old_ep.parameters, new_ep.parameters)
|
||||
# 再检测参数变更,如果有则额外生成参数报告
|
||||
param_changes = compare_parameters(r_ep.parameters, a_ep.parameters)
|
||||
if param_changes:
|
||||
reports.append(
|
||||
EndpointChangeReport(
|
||||
uri=new_ep.uri,
|
||||
http_method=new_ep.http_method,
|
||||
controller_class=new_ep.controller_class,
|
||||
method_name=new_ep.method_name,
|
||||
source_file=new_ep.source_file,
|
||||
uri=a_ep.uri,
|
||||
http_method=a_ep.http_method,
|
||||
controller_class=a_ep.controller_class,
|
||||
method_name=a_ep.method_name,
|
||||
source_file=a_ep.source_file,
|
||||
parameter_changes=param_changes,
|
||||
)
|
||||
)
|
||||
matched_removed.add(r_key)
|
||||
matched_added.add(a_key)
|
||||
break
|
||||
|
||||
# 判断路径是否改变
|
||||
if old_ep.uri != new_ep.uri:
|
||||
# 2. URI 路径变更检测(method + controller + method_name 相同,但 uri 不同)
|
||||
# 如果同时有参数变更,生成两条独立报告(路径变更 + 参数变更),互不干扰
|
||||
for r_key, r_ep in unmatched_removed:
|
||||
if r_key in matched_removed:
|
||||
continue
|
||||
for a_key, a_ep in unmatched_added:
|
||||
if a_key in matched_added:
|
||||
continue
|
||||
if (
|
||||
r_ep.http_method == a_ep.http_method
|
||||
and r_ep.controller_class == a_ep.controller_class
|
||||
and r_ep.method_name == a_ep.method_name
|
||||
and r_ep.uri != a_ep.uri
|
||||
):
|
||||
# 先生成纯路径变更报告
|
||||
reports.append(
|
||||
EndpointChangeReport(
|
||||
uri=new_ep.uri,
|
||||
http_method=new_ep.http_method,
|
||||
controller_class=new_ep.controller_class,
|
||||
method_name=new_ep.method_name,
|
||||
source_file=new_ep.source_file,
|
||||
uri=a_ep.uri,
|
||||
http_method=a_ep.http_method,
|
||||
controller_class=a_ep.controller_class,
|
||||
method_name=a_ep.method_name,
|
||||
source_file=a_ep.source_file,
|
||||
is_renamed_endpoint=True,
|
||||
old_uri=old_ep.uri,
|
||||
old_uri=r_ep.uri,
|
||||
)
|
||||
)
|
||||
# 如果同时有参数变更,额外生成参数报告(避免重复)
|
||||
if old_ep.http_method == new_ep.http_method:
|
||||
param_changes = compare_parameters(old_ep.parameters, new_ep.parameters)
|
||||
if param_changes:
|
||||
reports.append(
|
||||
EndpointChangeReport(
|
||||
uri=new_ep.uri,
|
||||
http_method=new_ep.http_method,
|
||||
controller_class=new_ep.controller_class,
|
||||
method_name=new_ep.method_name,
|
||||
source_file=new_ep.source_file,
|
||||
parameter_changes=param_changes,
|
||||
)
|
||||
)
|
||||
|
||||
# 如果请求方式和路径都没变,只检测参数变更
|
||||
if old_ep.http_method == new_ep.http_method and old_ep.uri == new_ep.uri:
|
||||
param_changes = compare_parameters(old_ep.parameters, new_ep.parameters)
|
||||
# 再检测参数变更,如果有则额外生成参数报告
|
||||
param_changes = compare_parameters(r_ep.parameters, a_ep.parameters)
|
||||
if param_changes:
|
||||
reports.append(
|
||||
EndpointChangeReport(
|
||||
uri=new_ep.uri,
|
||||
http_method=new_ep.http_method,
|
||||
controller_class=new_ep.controller_class,
|
||||
method_name=new_ep.method_name,
|
||||
source_file=new_ep.source_file,
|
||||
uri=a_ep.uri,
|
||||
http_method=a_ep.http_method,
|
||||
controller_class=a_ep.controller_class,
|
||||
method_name=a_ep.method_name,
|
||||
source_file=a_ep.source_file,
|
||||
parameter_changes=param_changes,
|
||||
)
|
||||
)
|
||||
matched_removed.add(r_key)
|
||||
matched_added.add(a_key)
|
||||
break
|
||||
|
||||
elif len(old_list) == 0 and len(new_list) > 0:
|
||||
# 新增接口
|
||||
for new_ep in new_list:
|
||||
reports.append(
|
||||
EndpointChangeReport(
|
||||
uri=new_ep.uri,
|
||||
http_method=new_ep.http_method,
|
||||
controller_class=new_ep.controller_class,
|
||||
method_name=new_ep.method_name,
|
||||
source_file=new_ep.source_file,
|
||||
is_new_endpoint=True,
|
||||
parameter_changes=[
|
||||
ParameterChange(
|
||||
change_type=ChangeType.ADDED,
|
||||
param_name=p.name,
|
||||
param_type=p.type,
|
||||
required=p.required,
|
||||
)
|
||||
for p in new_ep.parameters
|
||||
],
|
||||
)
|
||||
# 3. 剩余未匹配的 removed → 删除接口
|
||||
for key, ep in unmatched_removed:
|
||||
if key not in matched_removed:
|
||||
reports.append(
|
||||
EndpointChangeReport(
|
||||
uri=ep.uri,
|
||||
http_method=ep.http_method,
|
||||
controller_class=ep.controller_class,
|
||||
method_name=ep.method_name,
|
||||
source_file=ep.source_file,
|
||||
is_removed_endpoint=True,
|
||||
)
|
||||
)
|
||||
|
||||
elif len(old_list) > 0 and len(new_list) == 0:
|
||||
# 删除接口
|
||||
for old_ep in old_list:
|
||||
reports.append(
|
||||
EndpointChangeReport(
|
||||
uri=old_ep.uri,
|
||||
http_method=old_ep.http_method,
|
||||
controller_class=old_ep.controller_class,
|
||||
method_name=old_ep.method_name,
|
||||
source_file=old_ep.source_file,
|
||||
is_removed_endpoint=True,
|
||||
)
|
||||
# 4. 剩余未匹配的 added → 新增接口
|
||||
for key, ep in unmatched_added:
|
||||
if key not in matched_added:
|
||||
reports.append(
|
||||
EndpointChangeReport(
|
||||
uri=ep.uri,
|
||||
http_method=ep.http_method,
|
||||
controller_class=ep.controller_class,
|
||||
method_name=ep.method_name,
|
||||
source_file=ep.source_file,
|
||||
is_new_endpoint=True,
|
||||
parameter_changes=[
|
||||
ParameterChange(
|
||||
change_type=ChangeType.ADDED,
|
||||
param_name=p.name,
|
||||
param_type=p.type,
|
||||
required=p.required,
|
||||
)
|
||||
for p in ep.parameters
|
||||
],
|
||||
)
|
||||
)
|
||||
|
||||
# 5. 共同 URI:对比参数变更
|
||||
for key in common_keys:
|
||||
old_ep = old_endpoints[key]
|
||||
new_ep = new_endpoints[key]
|
||||
param_changes = compare_parameters(old_ep.parameters, new_ep.parameters)
|
||||
if param_changes:
|
||||
reports.append(
|
||||
EndpointChangeReport(
|
||||
uri=new_ep.uri,
|
||||
http_method=new_ep.http_method,
|
||||
controller_class=new_ep.controller_class,
|
||||
method_name=new_ep.method_name,
|
||||
source_file=new_ep.source_file,
|
||||
parameter_changes=param_changes,
|
||||
)
|
||||
)
|
||||
|
||||
return [r for r in reports if r.has_changes]
|
||||
|
||||
Reference in New Issue
Block a user