ApplyAttendanceOutsideDto
All checks were successful
CodeChecker 变更检测 / code-check (push) Successful in 25s
All checks were successful
CodeChecker 变更检测 / code-check (push) Successful in 25s
This commit is contained in:
102
.gitea/1.md
102
.gitea/1.md
@@ -1,102 +0,0 @@
|
|||||||
---
|
|
||||||
|
|
||||||
#### 需求拆解:
|
|
||||||
**[类变更类通知]** Vo、Dto、Model、Entity **目前只针对****<u>修改 </u>****<u><font style="background-color:#FBDE28;">删除也需要</font></u>**
|
|
||||||
|
|
||||||
**需要展示的内容:**
|
|
||||||
|
|
||||||
修改人、修改时间 (方便后续前端对接)
|
|
||||||
|
|
||||||
对象变更细节:变更了(增删改查)哪些字段 + 字段说明
|
|
||||||
|
|
||||||
影响范围:类的变更影响了哪些接口的使用(展示出影响的接口List)
|
|
||||||
|
|
||||||
* **入参影响** --> dto改变 --> 展示出影响的接口List
|
|
||||||
* **类转换影响** --> dto到entity的转换(这种类型需要 配置开关 判断是否需要检测) --> 展示出Entity类?
|
|
||||||
* **对前端的影响** --> Vo的变动 --> 展示出影响的接口List
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### 一、请求链路
|
|
||||||
Push
|
|
||||||
|
|
||||||
↓
|
|
||||||
|
|
||||||
Gitea Runner
|
|
||||||
|
|
||||||
↓
|
|
||||||
|
|
||||||
Pipeline
|
|
||||||
|
|
||||||
↓
|
|
||||||
|
|
||||||
GitDiff获取变更
|
|
||||||
|
|
||||||
↓
|
|
||||||
|
|
||||||
JavaParser解析(AST Controller变化\DTO变化\VO变化\ENTITY变化)
|
|
||||||
|
|
||||||
↓
|
|
||||||
|
|
||||||
企业微信通知
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### 二、架构&技术选型
|
|
||||||
| 组件 | 方案 |
|
|
||||||
| --- | --- |
|
|
||||||
| Git Diff | Git |
|
|
||||||
| 源码解析 | JavaParser |
|
|
||||||
| 引用分析 | JavaParser Symbol Solver |
|
|
||||||
| Spring Endpoint扫描 | Spring Mapping AST |
|
|
||||||
| 通知 | 企业微信机器人 webhook |
|
|
||||||
| CI集成 | Jenkins/GitLab CI |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### 三、分层说明
|
|
||||||
| **<font style="color:rgba(20, 20, 20, 0.92);">层级</font>** | **<font style="color:rgba(20, 20, 20, 0.92);">组件</font>** | **<font style="color:rgba(20, 20, 20, 0.92);">职责</font>** |
|
|
||||||
| :--- | :--- | :--- |
|
|
||||||
| **<font style="color:rgba(20, 20, 20, 0.92);">触发层</font>** | <font style="color:rgba(20, 20, 20, 0.92);">Git Push</font> | <font style="color:rgba(20, 20, 20, 0.92);">开发者提交代码,触发 CI 流程</font> |
|
|
||||||
| **<font style="color:rgba(20, 20, 20, 0.92);">CI/CD 层</font>** | <font style="color:rgba(20, 20, 20, 0.92);">Gitea Runner + Pipeline</font> | <font style="color:rgba(20, 20, 20, 0.92);">监听 Push 事件,编排流水线任务</font> |
|
|
||||||
| **<font style="color:rgba(20, 20, 20, 0.92);">解析层</font>** | <font style="color:rgba(20, 20, 20, 0.92);">GitDiff + JavaParser</font> | <font style="color:rgba(20, 20, 20, 0.92);">获取 diff,按 AST 解析 Controller/DTO/VO/Entity 变更</font> |
|
|
||||||
| **<font style="color:rgba(20, 20, 20, 0.92);">通知层</font>** | <font style="color:rgba(20, 20, 20, 0.92);">企业微信</font> | <font style="color:rgba(20, 20, 20, 0.92);">将分析结果推送给相关开发人员</font> |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### 四、通知模版
|
|
||||||
【类变更通知】
|
|
||||||
|
|
||||||
■ 变更对象:{ClassName}({类类型:Dto/Vo/Entity/Model})
|
|
||||||
■ 修改人:{Modifier}
|
|
||||||
■ 修改时间:{ModifyTime}
|
|
||||||
|
|
||||||
────────────────────────────────
|
|
||||||
▶ 对象变更细节
|
|
||||||
────────────────────────────────
|
|
||||||
字段变更列表:
|
|
||||||
[新增] 字段名: fieldA 说明: {字段说明}
|
|
||||||
[删除] 字段名: fieldB 说明: {字段说明}
|
|
||||||
[修改] 字段名: fieldC 说明: {原说明 → 新说明 / 类型变更等}
|
|
||||||
|
|
||||||
────────────────────────────────
|
|
||||||
▶ 影响范围
|
|
||||||
────────────────────────────────
|
|
||||||
① 入参影响(Dto变更导致接口参数变化):
|
|
||||||
影响接口列表:
|
|
||||||
- GET /api/attendance/dayStaList
|
|
||||||
- POST /api/attendance/export
|
|
||||||
(若无影响则显示:无)
|
|
||||||
|
|
||||||
② 类转换影响(Dto → Entity 转换,已开启检测):
|
|
||||||
涉及Entity类:{EntityClassName}
|
|
||||||
(若开关关闭或未检测到影响则显示:无 / 未开启检测)
|
|
||||||
|
|
||||||
③ 前端影响(Vo变更导致返回结构变化):
|
|
||||||
影响接口列表:
|
|
||||||
- GET /api/attendance/detail
|
|
||||||
- GET /api/attendance/summary
|
|
||||||
(若无影响则显示:无)
|
|
||||||
|
|
||||||
@@ -1,525 +0,0 @@
|
|||||||
"""
|
|
||||||
企业微信 Markdown 通知模块。
|
|
||||||
支持加粗、颜色(info/comment/warning),新增接口与变更接口使用不同展示模板。
|
|
||||||
"""
|
|
||||||
|
|
||||||
import json
|
|
||||||
import re
|
|
||||||
from collections import OrderedDict
|
|
||||||
from typing import List, Optional, Tuple
|
|
||||||
|
|
||||||
import requests
|
|
||||||
|
|
||||||
from comparator import EndpointChangeReport, ParameterChange
|
|
||||||
|
|
||||||
# 企微 Markdown 单条上限 4096 字符,留余量
|
|
||||||
MAX_MD_LENGTH = 3800
|
|
||||||
|
|
||||||
|
|
||||||
def truncate_text(text: str, max_length: int = MAX_MD_LENGTH) -> str:
|
|
||||||
"""截断超长消息。"""
|
|
||||||
if len(text) <= max_length:
|
|
||||||
return text
|
|
||||||
return text[:max_length] + "\n\n<font color=\"comment\">... 消息过长,已截断</font>"
|
|
||||||
|
|
||||||
|
|
||||||
def _format_param_change_list(changes: List[ParameterChange]) -> List[str]:
|
|
||||||
"""生成企微友好的普通参数变更列表(卡片式)。"""
|
|
||||||
if not changes:
|
|
||||||
return ['<font color="comment">无</font>']
|
|
||||||
lines = ["", f"共 **{len(changes)}** 项变更", ""]
|
|
||||||
for i, change in enumerate(changes, 1):
|
|
||||||
lines.append(change.to_markdown_block(i))
|
|
||||||
if i < len(changes):
|
|
||||||
lines.append("")
|
|
||||||
return lines
|
|
||||||
|
|
||||||
|
|
||||||
def _body_dto_group_key(change: ParameterChange) -> Tuple[str, str]:
|
|
||||||
"""类对象变更分组键:(body 参数名, DTO 类名)。"""
|
|
||||||
return (change.body_param_name or "body", change.parent_dto or "")
|
|
||||||
|
|
||||||
|
|
||||||
def _format_body_field_line(change: ParameterChange, *, is_last: bool) -> List[str]:
|
|
||||||
"""格式化 DTO 一级字段变更行。"""
|
|
||||||
branch = "└─" if is_last else "├─"
|
|
||||||
desc = change.description or change.old_description
|
|
||||||
type_part = f" · `{change.param_type}`" if change.param_type else ""
|
|
||||||
req_part = f" · {change._required_tag()}" if change._required_tag() else ""
|
|
||||||
lines = [f"{branch} `{change.param_name}`{type_part}{req_part} {change._change_tag()}"]
|
|
||||||
if desc:
|
|
||||||
lines.append(f"> 说明:{desc}")
|
|
||||||
if change.change_type.value == "modified" and change.detail:
|
|
||||||
lines.append(f"> 变更:{change.detail}")
|
|
||||||
if change.change_type.value == "renamed":
|
|
||||||
lines.append(f"> `{change.old_name}` → `{change.param_name}`")
|
|
||||||
return lines
|
|
||||||
|
|
||||||
|
|
||||||
def _format_body_dto_groups(changes: List[ParameterChange]) -> List[str]:
|
|
||||||
"""按 DTO 分组展示 @RequestBody 一级字段。"""
|
|
||||||
if not changes:
|
|
||||||
return ['<font color="comment">无</font>']
|
|
||||||
|
|
||||||
groups: OrderedDict[Tuple[str, str], List[ParameterChange]] = OrderedDict()
|
|
||||||
for change in changes:
|
|
||||||
key = _body_dto_group_key(change)
|
|
||||||
groups.setdefault(key, []).append(change)
|
|
||||||
|
|
||||||
lines: List[str] = ["", f"共 **{len(groups)}** 个类对象 · **{len(changes)}** 项字段变更", ""]
|
|
||||||
for (param_name, dto_name), group in groups.items():
|
|
||||||
label = param_name or "body"
|
|
||||||
dto_part = f" · `{dto_name}`" if dto_name else ""
|
|
||||||
lines.append(f"**{label}**{dto_part}")
|
|
||||||
lines.append("")
|
|
||||||
for i, change in enumerate(group):
|
|
||||||
lines.extend(_format_body_field_line(change, is_last=(i == len(group) - 1)))
|
|
||||||
lines.append("")
|
|
||||||
|
|
||||||
if lines and lines[-1] == "":
|
|
||||||
lines.pop()
|
|
||||||
return lines
|
|
||||||
|
|
||||||
|
|
||||||
def _format_param_details_section(report: EndpointChangeReport) -> List[str]:
|
|
||||||
"""生成接口参数变动详情区块。"""
|
|
||||||
body_changes = [c for c in report.parameter_changes if c.source == "body"]
|
|
||||||
regular_changes = [c for c in report.parameter_changes if c.source != "body"]
|
|
||||||
lines = ["", "---------------------------------------", "", "#### 【接口参数变动详情】", ""]
|
|
||||||
|
|
||||||
if body_changes:
|
|
||||||
lines.append("**类对象变更(一级字段)**")
|
|
||||||
lines.extend(_format_body_dto_groups(body_changes))
|
|
||||||
lines.append("")
|
|
||||||
|
|
||||||
if regular_changes:
|
|
||||||
lines.append("**普通参数变更**")
|
|
||||||
lines.extend(_format_param_change_list(regular_changes))
|
|
||||||
lines.append("")
|
|
||||||
|
|
||||||
if not body_changes and not regular_changes:
|
|
||||||
lines.append('<font color="comment">无</font>')
|
|
||||||
|
|
||||||
return lines
|
|
||||||
|
|
||||||
|
|
||||||
def _format_endpoint_block(report: EndpointChangeReport) -> str:
|
|
||||||
"""
|
|
||||||
格式化单个接口块,按模板匹配格式输出。
|
|
||||||
路径显示为 source_file(相对仓库根的完整 .java 路径)。
|
|
||||||
"""
|
|
||||||
change_type = "新增接口" if report.is_new_endpoint else ("删除接口" if report.is_removed_endpoint else "修改参数")
|
|
||||||
uri_line = f"**{report.http_method}** `{report.uri}`"
|
|
||||||
file_path = report.source_file or report.controller_class
|
|
||||||
class_line = f"- **路径:** <font color=\"info\">**{file_path}**</font>"
|
|
||||||
|
|
||||||
header = [
|
|
||||||
f"- **变更类型:** <font color=\"warning\">**{change_type}**</font>",
|
|
||||||
f"- **URI:** {uri_line}",
|
|
||||||
class_line,
|
|
||||||
]
|
|
||||||
|
|
||||||
if report.is_removed_endpoint:
|
|
||||||
return "\n".join(header + ["", f"<font color=\"warning\">**该接口已被移除**</font>"])
|
|
||||||
|
|
||||||
return "\n".join(header + _format_param_details_section(report))
|
|
||||||
|
|
||||||
|
|
||||||
def build_markdown_notification(
|
|
||||||
reports: List[EndpointChangeReport],
|
|
||||||
push_user: str,
|
|
||||||
push_time: str,
|
|
||||||
llm_summary: Optional[str] = None,
|
|
||||||
) -> str:
|
|
||||||
"""
|
|
||||||
构建完整 Markdown 通知正文。
|
|
||||||
|
|
||||||
:param reports: AST 变更报告
|
|
||||||
:param push_user: 推送人
|
|
||||||
:param push_time: 推送时间
|
|
||||||
:param llm_summary: LLM 兼容性摘要(可选,简短)
|
|
||||||
:return: Markdown 文本
|
|
||||||
"""
|
|
||||||
parts: List[str] = []
|
|
||||||
|
|
||||||
# 所有 API 级变更(新增、修改路径、修改请求方式、删除、参数变更)统一走 model1.md 路径变更通知
|
|
||||||
method_changed_reports = [r for r in reports if r.is_method_changed]
|
|
||||||
renamed_reports = [r for r in reports if r.is_renamed_endpoint]
|
|
||||||
new_reports = [r for r in reports if r.is_new_endpoint]
|
|
||||||
# 参数变更报告:只包含「URI/方法未变,仅参数变化」的报告
|
|
||||||
# 路径变更 + 参数变更、方法变更 + 参数变更 场景已在上层 comparator 中拆分为独立报告
|
|
||||||
changed_reports = [
|
|
||||||
r for r in reports
|
|
||||||
if not r.is_new_endpoint
|
|
||||||
and not r.is_removed_endpoint
|
|
||||||
and not r.is_renamed_endpoint
|
|
||||||
and not r.is_method_changed
|
|
||||||
]
|
|
||||||
removed_reports = [r for r in reports if r.is_removed_endpoint]
|
|
||||||
|
|
||||||
# 1. 新增接口 → 走 API路径变更通知
|
|
||||||
for report in new_reports:
|
|
||||||
path_md = build_path_change_markdown(
|
|
||||||
old_uri="-",
|
|
||||||
new_uri=report.uri,
|
|
||||||
change_type="新增接口",
|
|
||||||
push_user=push_user,
|
|
||||||
push_time=push_time,
|
|
||||||
file_name=report.source_file or report.controller_class,
|
|
||||||
)
|
|
||||||
parts.append(path_md)
|
|
||||||
parts.append("")
|
|
||||||
|
|
||||||
# 2. 修改请求方式 → 使用独立的新模板 【API请求方式变更通知】
|
|
||||||
for report in method_changed_reports:
|
|
||||||
method_md = build_method_change_markdown(
|
|
||||||
uri=report.uri,
|
|
||||||
old_method=report.old_http_method or "?",
|
|
||||||
new_method=report.http_method,
|
|
||||||
push_user=push_user,
|
|
||||||
push_time=push_time,
|
|
||||||
file_name=report.source_file or report.controller_class,
|
|
||||||
)
|
|
||||||
parts.append(method_md)
|
|
||||||
parts.append("")
|
|
||||||
|
|
||||||
# 3. 修改路径 → 走 API路径变更通知
|
|
||||||
for report in renamed_reports:
|
|
||||||
path_md = build_path_change_markdown(
|
|
||||||
old_uri=report.old_uri or "-",
|
|
||||||
new_uri=report.uri,
|
|
||||||
change_type="修改路径",
|
|
||||||
push_user=push_user,
|
|
||||||
push_time=push_time,
|
|
||||||
file_name=report.source_file or report.controller_class,
|
|
||||||
)
|
|
||||||
parts.append(path_md)
|
|
||||||
parts.append("")
|
|
||||||
|
|
||||||
# 4. 删除接口 → 走 API路径变更通知
|
|
||||||
for report in removed_reports:
|
|
||||||
path_md = build_path_change_markdown(
|
|
||||||
old_uri=report.uri,
|
|
||||||
new_uri="已删除",
|
|
||||||
change_type="删除接口",
|
|
||||||
push_user=push_user,
|
|
||||||
push_time=push_time,
|
|
||||||
file_name=report.source_file or report.controller_class,
|
|
||||||
)
|
|
||||||
parts.append(path_md)
|
|
||||||
parts.append("")
|
|
||||||
|
|
||||||
# 4. 普通参数变更(非路径变更)仍使用 model.md 格式
|
|
||||||
if changed_reports:
|
|
||||||
parts.append("# 【API参数变更通知】")
|
|
||||||
parts.append(f"- **修改人:** {push_user}")
|
|
||||||
parts.append(f"- **修改时间:** {push_time}")
|
|
||||||
parts.append("")
|
|
||||||
for report in changed_reports:
|
|
||||||
parts.append(_format_endpoint_block(report))
|
|
||||||
parts.append("")
|
|
||||||
|
|
||||||
if llm_summary:
|
|
||||||
cleaned = llm_summary.strip()
|
|
||||||
# 去掉 LLM 可能输出的「排除框架注入」类说明
|
|
||||||
cleaned = re.sub(
|
|
||||||
r"(排除Spring MVC框架自动注入的[^)]+)",
|
|
||||||
"",
|
|
||||||
cleaned,
|
|
||||||
)
|
|
||||||
cleaned = re.sub(
|
|
||||||
r"排除Spring MVC框架自动注入的[`\w/]+[`\w/、/]*[。\.]?",
|
|
||||||
"",
|
|
||||||
cleaned,
|
|
||||||
)
|
|
||||||
if cleaned:
|
|
||||||
parts.append("### <font color=\"comment\">【兼容性提示】</font>")
|
|
||||||
parts.append(cleaned)
|
|
||||||
|
|
||||||
return "\n".join(parts).strip()
|
|
||||||
|
|
||||||
|
|
||||||
def _split_markdown(text: str, max_len: int) -> List[str]:
|
|
||||||
"""按 ### 标题块拆分超长 Markdown。"""
|
|
||||||
if len(text) <= max_len:
|
|
||||||
return [text]
|
|
||||||
|
|
||||||
lines = text.split("\n")
|
|
||||||
chunks: List[str] = []
|
|
||||||
current: List[str] = []
|
|
||||||
|
|
||||||
for line in lines:
|
|
||||||
if line.startswith("### ") and current and len("\n".join(current)) > 200:
|
|
||||||
chunks.append("\n".join(current))
|
|
||||||
current = [line]
|
|
||||||
else:
|
|
||||||
current.append(line)
|
|
||||||
if len("\n".join(current)) >= max_len:
|
|
||||||
chunks.append("\n".join(current))
|
|
||||||
current = []
|
|
||||||
|
|
||||||
if current:
|
|
||||||
if chunks and len("\n".join(current)) < 200:
|
|
||||||
chunks[-1] = chunks[-1] + "\n" + "\n".join(current)
|
|
||||||
else:
|
|
||||||
chunks.append("\n".join(current))
|
|
||||||
|
|
||||||
return chunks or [truncate_text(text)]
|
|
||||||
|
|
||||||
|
|
||||||
def _post_wecom_markdown(webhook_url: str, content: str) -> bool:
|
|
||||||
"""发送企微 Markdown 消息。"""
|
|
||||||
if not webhook_url or "YOUR_WECOM_KEY" in webhook_url:
|
|
||||||
print("[警告] 未配置有效的企业微信 Webhook URL。")
|
|
||||||
print("--- 通知预览 ---")
|
|
||||||
print(content[:1000])
|
|
||||||
return False
|
|
||||||
|
|
||||||
payload = {
|
|
||||||
"msgtype": "markdown",
|
|
||||||
"markdown": {"content": truncate_text(content)},
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
resp = requests.post(
|
|
||||||
webhook_url,
|
|
||||||
headers={"Content-Type": "application/json"},
|
|
||||||
data=json.dumps(payload, ensure_ascii=False).encode("utf-8"),
|
|
||||||
timeout=10,
|
|
||||||
)
|
|
||||||
if resp.status_code == 200 and resp.json().get("errcode", 0) == 0:
|
|
||||||
return True
|
|
||||||
print(f"[错误] 企微返回异常: {resp.status_code} {resp.text}")
|
|
||||||
return False
|
|
||||||
except requests.RequestException as exc:
|
|
||||||
print(f"[错误] 发送企微消息失败: {exc}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def send_parameter_change_notification(
|
|
||||||
webhook_url: str,
|
|
||||||
reports: List[EndpointChangeReport],
|
|
||||||
push_user: str,
|
|
||||||
push_time: str,
|
|
||||||
llm_review: Optional[str] = None,
|
|
||||||
mentioned_users: Optional[List[str]] = None,
|
|
||||||
) -> int:
|
|
||||||
"""
|
|
||||||
发送 Markdown 格式的接口变更通知。
|
|
||||||
|
|
||||||
严格按变更类型拆分,各自独立构建和发送企微通知:
|
|
||||||
- 方法变更 → 独立调用 build_method_change_markdown
|
|
||||||
- 路径变更(新增/修改/删除) → 独立调用 build_path_change_markdown
|
|
||||||
- 参数变更 → 独立调用 _format_endpoint_block
|
|
||||||
|
|
||||||
不同类型之间完全互不干扰,各自走独立分支。
|
|
||||||
"""
|
|
||||||
if not reports and not llm_review:
|
|
||||||
print("无接口参数变更,不发送到企业微信")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
# 按类型严格分组(互不重叠)
|
|
||||||
method_changed_reports = [r for r in reports if r.is_method_changed]
|
|
||||||
renamed_reports = [r for r in reports if r.is_renamed_endpoint]
|
|
||||||
new_reports = [r for r in reports if r.is_new_endpoint]
|
|
||||||
removed_reports = [r for r in reports if r.is_removed_endpoint]
|
|
||||||
changed_reports = [
|
|
||||||
r for r in reports
|
|
||||||
if not r.is_new_endpoint
|
|
||||||
and not r.is_removed_endpoint
|
|
||||||
and not r.is_renamed_endpoint
|
|
||||||
and not r.is_method_changed
|
|
||||||
]
|
|
||||||
|
|
||||||
sent = 0
|
|
||||||
|
|
||||||
# ========== 1. 请求方式变更通知(独立分支) ==========
|
|
||||||
for report in method_changed_reports:
|
|
||||||
md = build_method_change_markdown(
|
|
||||||
uri=report.uri,
|
|
||||||
old_method=report.old_http_method or "?",
|
|
||||||
new_method=report.http_method,
|
|
||||||
push_user=push_user,
|
|
||||||
push_time=push_time,
|
|
||||||
file_name=report.source_file or report.controller_class,
|
|
||||||
)
|
|
||||||
if _post_wecom_markdown(webhook_url, md):
|
|
||||||
sent += 1
|
|
||||||
print(f"第 {sent} 条通知已发送到企业微信(请求方式变更)")
|
|
||||||
|
|
||||||
# ========== 2. 路径变更通知(新增/修改/删除) ==========
|
|
||||||
# 新增接口
|
|
||||||
for report in new_reports:
|
|
||||||
md = build_path_change_markdown(
|
|
||||||
old_uri="-",
|
|
||||||
new_uri=report.uri,
|
|
||||||
change_type="新增接口",
|
|
||||||
push_user=push_user,
|
|
||||||
push_time=push_time,
|
|
||||||
file_name=report.source_file or report.controller_class,
|
|
||||||
)
|
|
||||||
if report.parameter_changes:
|
|
||||||
param_section = "\n".join(_format_param_details_section(report)).strip()
|
|
||||||
md = f"{md}\n\n{param_section}"
|
|
||||||
if _post_wecom_markdown(webhook_url, md):
|
|
||||||
sent += 1
|
|
||||||
print(f"第 {sent} 条通知已发送到企业微信(新增接口)")
|
|
||||||
|
|
||||||
# 修改路径
|
|
||||||
for report in renamed_reports:
|
|
||||||
md = build_path_change_markdown(
|
|
||||||
old_uri=report.old_uri or "-",
|
|
||||||
new_uri=report.uri,
|
|
||||||
change_type="修改路径",
|
|
||||||
push_user=push_user,
|
|
||||||
push_time=push_time,
|
|
||||||
file_name=report.source_file or report.controller_class,
|
|
||||||
)
|
|
||||||
if _post_wecom_markdown(webhook_url, md):
|
|
||||||
sent += 1
|
|
||||||
print(f"第 {sent} 条通知已发送到企业微信(修改路径)")
|
|
||||||
|
|
||||||
# 删除接口
|
|
||||||
for report in removed_reports:
|
|
||||||
md = build_path_change_markdown(
|
|
||||||
old_uri=report.uri,
|
|
||||||
new_uri="已删除",
|
|
||||||
change_type="删除接口",
|
|
||||||
push_user=push_user,
|
|
||||||
push_time=push_time,
|
|
||||||
file_name=report.source_file or report.controller_class,
|
|
||||||
)
|
|
||||||
if _post_wecom_markdown(webhook_url, md):
|
|
||||||
sent += 1
|
|
||||||
print(f"第 {sent} 条通知已发送到企业微信(删除接口)")
|
|
||||||
|
|
||||||
# ========== 3. 参数变更通知(独立分支) ==========
|
|
||||||
if changed_reports:
|
|
||||||
# 构建参数变更通知(只包含参数变更报告,对齐 model.md)
|
|
||||||
parts: List[str] = []
|
|
||||||
parts.append("# 【API参数变更通知】")
|
|
||||||
parts.append(f"- **修改人:** {push_user}")
|
|
||||||
parts.append(f"- **修改时间:** {push_time}")
|
|
||||||
parts.append("")
|
|
||||||
for report in changed_reports:
|
|
||||||
parts.append(_format_endpoint_block(report))
|
|
||||||
parts.append("")
|
|
||||||
if llm_review:
|
|
||||||
parts.append("---")
|
|
||||||
parts.append("### <font color=\"comment\">兼容性提示</font>")
|
|
||||||
parts.append(llm_review.strip())
|
|
||||||
|
|
||||||
md = "\n".join(parts).strip()
|
|
||||||
if _post_wecom_markdown(webhook_url, md):
|
|
||||||
sent += 1
|
|
||||||
print(f"第 {sent} 条通知已发送到企业微信(参数变更)")
|
|
||||||
|
|
||||||
if sent > 0:
|
|
||||||
print(f"总共发送 {sent} 条通知到企业微信")
|
|
||||||
return sent
|
|
||||||
|
|
||||||
|
|
||||||
def build_path_change_markdown(
|
|
||||||
old_uri: str,
|
|
||||||
new_uri: str,
|
|
||||||
change_type: str,
|
|
||||||
push_user: str,
|
|
||||||
push_time: str,
|
|
||||||
file_name: str,
|
|
||||||
) -> str:
|
|
||||||
"""构建 API路径变更通知,完全匹配 model1.md 模板,并加强视觉区分。
|
|
||||||
|
|
||||||
支持的 change_type:
|
|
||||||
- 新增接口 / 删除接口 / 修改路径 / 修改请求方式
|
|
||||||
|
|
||||||
改进点:
|
|
||||||
- 标题使用【】风格
|
|
||||||
- 头部信息缩进 + 颜色高亮
|
|
||||||
- URI 详情使用列表(更直观)
|
|
||||||
- 「修改请求方式」额外展示方法变更
|
|
||||||
"""
|
|
||||||
# 变更类型高亮
|
|
||||||
type_highlight = f"<font color=\"warning\">**{change_type}**</font>"
|
|
||||||
|
|
||||||
# 路径高亮
|
|
||||||
class_highlight = f"<font color=\"info\">**{file_name}**</font>"
|
|
||||||
|
|
||||||
# 根据变更类型优化 URI 展示
|
|
||||||
if change_type == "新增接口":
|
|
||||||
old_display = "`-`"
|
|
||||||
new_display = f"<font color=\"info\">**`{new_uri}`**</font> ← <font color=\"info\">**新增**</font>"
|
|
||||||
elif change_type == "删除接口":
|
|
||||||
old_display = f"<font color=\"warning\">**`{old_uri}`**</font> ← <font color=\"warning\">**已删除**</font>"
|
|
||||||
new_display = "`已删除`"
|
|
||||||
else: # 修改路径
|
|
||||||
old_display = f"<font color=\"warning\">~~`{old_uri}`~~</font> ← <font color=\"warning\">**旧路径**</font>"
|
|
||||||
new_display = f"<font color=\"info\">**`{new_uri}`**</font> ← <font color=\"info\">**新路径**</font>"
|
|
||||||
|
|
||||||
parts = [
|
|
||||||
"# 【API路径变更通知】",
|
|
||||||
"",
|
|
||||||
f" 变更类型: {type_highlight}",
|
|
||||||
f" 路径: {class_highlight}",
|
|
||||||
f" 修改人: {push_user}",
|
|
||||||
f" 修改时间: {push_time}",
|
|
||||||
"",
|
|
||||||
"---------------------------------------",
|
|
||||||
"",
|
|
||||||
"#### 【URI变更详情】",
|
|
||||||
f"- **原路径:** {old_display}",
|
|
||||||
f"- **新路径:** {new_display}",
|
|
||||||
"",
|
|
||||||
]
|
|
||||||
return "\n".join(parts).strip()
|
|
||||||
|
|
||||||
|
|
||||||
def build_method_change_markdown(
|
|
||||||
uri: str,
|
|
||||||
old_method: str,
|
|
||||||
new_method: str,
|
|
||||||
push_user: str,
|
|
||||||
push_time: str,
|
|
||||||
file_name: str,
|
|
||||||
) -> str:
|
|
||||||
"""构建【API请求方式变更通知】独立模板。
|
|
||||||
|
|
||||||
格式参考 model1.md,但专门针对 HTTP 方法变更场景设计,
|
|
||||||
突出「原请求方式 → 新请求方式」的对比。
|
|
||||||
"""
|
|
||||||
type_highlight = '<font color="warning">**修改请求方式**</font>'
|
|
||||||
class_highlight = f'<font color="info">**{file_name}**</font>'
|
|
||||||
uri_highlight = f'<font color="info">**`{uri}`**</font>'
|
|
||||||
old_m = f'<font color="warning">**{old_method}**</font>'
|
|
||||||
new_m = f'<font color="info">**{new_method}**</font>'
|
|
||||||
|
|
||||||
parts = [
|
|
||||||
"# 【API请求方式变更通知】",
|
|
||||||
"",
|
|
||||||
f" 变更类型: {type_highlight}",
|
|
||||||
f" 路径: {class_highlight}",
|
|
||||||
f" 修改人: {push_user}",
|
|
||||||
f" 修改时间: {push_time}",
|
|
||||||
"",
|
|
||||||
"---------------------------------------",
|
|
||||||
"",
|
|
||||||
"#### 【请求方式变更详情】",
|
|
||||||
f"- **URI:** {uri_highlight}",
|
|
||||||
f"- **原请求方式:** {old_m}",
|
|
||||||
f"- **新请求方式:** {new_m} ← <font color=\"info\">**请求方式已变更**</font>",
|
|
||||||
"",
|
|
||||||
]
|
|
||||||
return "\n".join(parts).strip()
|
|
||||||
|
|
||||||
|
|
||||||
def send_path_change_notification(
|
|
||||||
webhook_url: str,
|
|
||||||
old_uri: str,
|
|
||||||
new_uri: str,
|
|
||||||
change_type: str,
|
|
||||||
push_user: str,
|
|
||||||
push_time: str,
|
|
||||||
file_name: str,
|
|
||||||
) -> bool:
|
|
||||||
"""发送路径变更通知。"""
|
|
||||||
md = build_path_change_markdown(old_uri, new_uri, change_type, push_user, push_time, file_name)
|
|
||||||
return _post_wecom_markdown(webhook_url, md)
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
---
|
|
||||||
|
|
||||||
#### 需求拆解:
|
|
||||||
**[类变更类通知]** Vo、Dto、Model、Entity **目前只针对****<u>修改 </u>****<u><font style="background-color:#FBDE28;">删除也需要</font></u>**
|
|
||||||
|
|
||||||
**需要展示的内容:**
|
|
||||||
|
|
||||||
修改人、修改时间 (方便后续前端对接)
|
|
||||||
|
|
||||||
对象变更细节:变更了(增删改查)哪些字段 + 字段说明
|
|
||||||
|
|
||||||
影响范围:类的变更影响了哪些接口的使用(展示出影响的接口List)
|
|
||||||
|
|
||||||
* **入参影响** --> dto改变 --> 展示出影响的接口List
|
|
||||||
* **类转换影响** --> dto到entity的转换(这种类型需要 配置开关 判断是否需要检测) --> 展示出Entity类?
|
|
||||||
* **对前端的影响** --> Vo的变动 --> 展示出影响的接口List
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### 一、请求链路
|
|
||||||
Push
|
|
||||||
|
|
||||||
↓
|
|
||||||
|
|
||||||
Gitea + act_runner
|
|
||||||
|
|
||||||
↓
|
|
||||||
|
|
||||||
Pipeline
|
|
||||||
|
|
||||||
↓
|
|
||||||
|
|
||||||
GitDiff获取变更
|
|
||||||
|
|
||||||
↓
|
|
||||||
|
|
||||||
JavaParser解析(AST Controller变化\DTO变化\VO变化\ENTITY变化)
|
|
||||||
|
|
||||||
↓
|
|
||||||
|
|
||||||
企业微信通知
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### 二、架构&技术选型
|
|
||||||
| 组件 | 方案 |
|
|
||||||
| --- | --- |
|
|
||||||
| Git Diff | Git |
|
|
||||||
| 源码解析 | JavaParser |
|
|
||||||
| 引用分析 | JavaParser Symbol Solver |
|
|
||||||
| Spring Endpoint扫描 | Spring Mapping AST |
|
|
||||||
| 通知 | 企业微信机器人 webhook |
|
|
||||||
| CI集成 | Gitea Actions |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### 三、分层说明
|
|
||||||
| **<font style="color:rgba(20, 20, 20, 0.92);">层级</font>** | **<font style="color:rgba(20, 20, 20, 0.92);">组件</font>** | **<font style="color:rgba(20, 20, 20, 0.92);">职责</font>** |
|
|
||||||
| :--- | :--- | :--- |
|
|
||||||
| **<font style="color:rgba(20, 20, 20, 0.92);">触发层</font>** | <font style="color:rgba(20, 20, 20, 0.92);">Git Push</font> | <font style="color:rgba(20, 20, 20, 0.92);">开发者提交代码,触发 CI 流程</font> |
|
|
||||||
| **<font style="color:rgba(20, 20, 20, 0.92);">CI/CD 层</font>** | <font style="color:rgba(20, 20, 20, 0.92);">本地 Gitea + act_runner</font> | <font style="color:rgba(20, 20, 20, 0.92);">监听 Push 事件,编排流水线任务</font> |
|
|
||||||
| **<font style="color:rgba(20, 20, 20, 0.92);">解析层</font>** | <font style="color:rgba(20, 20, 20, 0.92);">GitDiff + JavaParser</font> | <font style="color:rgba(20, 20, 20, 0.92);">获取 diff,按 AST 解析 Controller/DTO/VO/Entity 变更</font> |
|
|
||||||
| **<font style="color:rgba(20, 20, 20, 0.92);">通知层</font>** | <font style="color:rgba(20, 20, 20, 0.92);">企业微信</font> | <font style="color:rgba(20, 20, 20, 0.92);">将分析结果推送给相关开发人员</font> |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### 四、通知模版
|
|
||||||
|
|
||||||
模版已按类类型拆分至 [notify-templates/](./notify-templates/) 目录:
|
|
||||||
|
|
||||||
| 类类型 | 模版文件 | 影响范围段落 |
|
|
||||||
|--------|----------|--------------|
|
|
||||||
| Dto | [dto.md](./notify-templates/dto.md) | request + 类转换 |
|
|
||||||
| Vo | [vo.md](./notify-templates/vo.md) | response + 类转换 |
|
|
||||||
| Entity | [entity.md](./notify-templates/entity.md) | 类转换 |
|
|
||||||
| Model | [model.md](./notify-templates/model.md) | 类转换 |
|
|
||||||
|
|
||||||
详见 [notify-templates/README.md](./notify-templates/README.md)(含企微颜色样式、路径、字段说明规则)。
|
|
||||||
|
|
||||||
@@ -1,53 +1 @@
|
|||||||
name: CodeChecker 变更检测
|
name: CodeChecker 变更检测
|
||||||
|
|
||||||
run-name: ${{ gitea.actor }}的CodeChecker变更检测
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
on:
|
|
||||||
|
|
||||||
push:
|
|
||||||
|
|
||||||
branches:
|
|
||||||
|
|
||||||
- class-check
|
|
||||||
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
|
|
||||||
code-check:
|
|
||||||
|
|
||||||
if: ${{ gitea.ref != 'refs/heads/pre' && gitea.ref != 'refs/heads/dev' && gitea.ref != 'refs/heads/master-2.0' }}
|
|
||||||
|
|
||||||
runs-on: jdk11
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
steps:
|
|
||||||
|
|
||||||
- name: 检出代码
|
|
||||||
|
|
||||||
run: |
|
|
||||||
|
|
||||||
git config --global http.sslVerify false
|
|
||||||
|
|
||||||
git clone "https://${{ gitea.token }}@git.niujiekeji.com/${{ gitea.repository }}.git" .
|
|
||||||
|
|
||||||
git checkout ${{ gitea.sha }}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
- name: 检查配置文件与预编译 jar
|
|
||||||
|
|
||||||
run: |
|
|
||||||
|
|
||||||
if [ ! -f .gitea/config.yaml ]; then
|
|
||||||
|
|
||||||
echo "错误: 缺少 .gitea/config.yaml"
|
|
||||||
|
|
||||||
exit 1
|
|
||||||
|
|
||||||
fi
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
log:
|
|
||||||
level: info
|
|
||||||
|
|
||||||
runner:
|
|
||||||
file: .runner
|
|
||||||
capacity: 2
|
|
||||||
timeout: 3h
|
|
||||||
labels:
|
|
||||||
- "ubuntu-latest:docker://ai-check-gitea-job:latest"
|
|
||||||
- "linux:docker://ai-check-gitea-job:latest"
|
|
||||||
@@ -17,7 +17,7 @@ import java.util.List;
|
|||||||
public class ApplyAttendanceOutsideDto {
|
public class ApplyAttendanceOutsideDto {
|
||||||
|
|
||||||
/** 审批id */
|
/** 审批id */
|
||||||
private String taskId;
|
private Integer taskId;
|
||||||
/** 流程标题 */
|
/** 流程标题 */
|
||||||
private String flowTitle;
|
private String flowTitle;
|
||||||
/** 申请人id */
|
/** 申请人id */
|
||||||
|
|||||||
Reference in New Issue
Block a user