This commit is contained in:
165
.gitea/checker/change_logger.py
Normal file
165
.gitea/checker/change_logger.py
Normal file
@@ -0,0 +1,165 @@
|
||||
"""
|
||||
变更日志持久化模块。
|
||||
受 log.enabled 开关控制,默认关闭;仅记录接口参数变更及 LLM 审核结果。
|
||||
"""
|
||||
|
||||
import json
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from comparator import EndpointChangeReport
|
||||
from git_utils import CommitInfo
|
||||
|
||||
|
||||
def is_log_enabled(config: Dict[str, Any]) -> bool:
|
||||
"""判断日志总开关是否开启。"""
|
||||
return config.get("log", {}).get("enabled", False)
|
||||
|
||||
|
||||
def _serialize_reports(reports: List[EndpointChangeReport]) -> List[dict]:
|
||||
"""将参数变更报告序列化为 JSON 结构。"""
|
||||
result = []
|
||||
for r in reports:
|
||||
result.append(
|
||||
{
|
||||
"uri": r.uri,
|
||||
"http_method": r.http_method,
|
||||
"controller_class": r.controller_class,
|
||||
"method_name": r.method_name,
|
||||
"is_new_endpoint": r.is_new_endpoint,
|
||||
"is_removed_endpoint": r.is_removed_endpoint,
|
||||
"parameter_changes": [
|
||||
{
|
||||
"change_type": c.change_type.value,
|
||||
"param_name": c.param_name,
|
||||
"param_type": c.param_type,
|
||||
"old_name": c.old_name,
|
||||
"old_type": c.old_type,
|
||||
"required": c.required,
|
||||
"detail": c.detail,
|
||||
}
|
||||
for c in r.parameter_changes
|
||||
],
|
||||
}
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
def save_to_file(
|
||||
reports: List[EndpointChangeReport],
|
||||
commit_info: CommitInfo,
|
||||
log_dir: str,
|
||||
llm_review: Optional[str] = None,
|
||||
) -> Path:
|
||||
"""写入 JSON 日志文件。"""
|
||||
log_path = Path(log_dir)
|
||||
log_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
short_sha = commit_info.sha[:8]
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
output = log_path / f"{timestamp}_{short_sha}.json"
|
||||
|
||||
record = {
|
||||
"commit_sha": commit_info.sha,
|
||||
"author": commit_info.author,
|
||||
"commit_time": commit_info.commit_time,
|
||||
"message": commit_info.message,
|
||||
"detected_at": datetime.now().isoformat(),
|
||||
"change_count": len(reports),
|
||||
"parameter_changes": _serialize_reports(reports),
|
||||
"llm_review": llm_review,
|
||||
}
|
||||
|
||||
with open(output, "w", encoding="utf-8") as f:
|
||||
json.dump(record, f, ensure_ascii=False, indent=2)
|
||||
|
||||
print(f"[日志] 参数变更记录已写入: {output}")
|
||||
return output
|
||||
|
||||
|
||||
def save_to_mysql(
|
||||
reports: List[EndpointChangeReport],
|
||||
commit_info: CommitInfo,
|
||||
mysql_config: Dict[str, Any],
|
||||
llm_review: Optional[str] = None,
|
||||
) -> bool:
|
||||
"""写入 MySQL。"""
|
||||
try:
|
||||
import pymysql
|
||||
except ImportError:
|
||||
print("[错误] MySQL 模式需要: pip install pymysql")
|
||||
return False
|
||||
|
||||
host = mysql_config.get("host", "")
|
||||
if not host or host == "YOUR_MYSQL_HOST":
|
||||
print("[警告] 未配置 MySQL,跳过写入。")
|
||||
return False
|
||||
|
||||
try:
|
||||
conn = pymysql.connect(
|
||||
host=host,
|
||||
port=int(mysql_config.get("port", 3306)),
|
||||
user=mysql_config.get("user"),
|
||||
password=mysql_config.get("password"),
|
||||
database=mysql_config.get("database"),
|
||||
charset="utf8mb4",
|
||||
)
|
||||
table = mysql_config.get("table", "api_change_logs")
|
||||
payload = json.dumps(_serialize_reports(reports), ensure_ascii=False)
|
||||
|
||||
with conn.cursor() as cursor:
|
||||
sql = f"""
|
||||
INSERT INTO `{table}`
|
||||
(commit_sha, author, commit_time, commit_message, change_count, reports_json, llm_review, created_at)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s, NOW())
|
||||
"""
|
||||
cursor.execute(
|
||||
sql,
|
||||
(
|
||||
commit_info.sha,
|
||||
commit_info.author,
|
||||
commit_info.commit_time,
|
||||
commit_info.message,
|
||||
len(reports),
|
||||
payload,
|
||||
llm_review,
|
||||
),
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
print(f"[日志] 已写入 MySQL: {table}")
|
||||
return True
|
||||
except Exception as exc:
|
||||
print(f"[错误] MySQL 写入失败: {exc}")
|
||||
return False
|
||||
|
||||
|
||||
def persist_change_log(
|
||||
reports: List[EndpointChangeReport],
|
||||
commit_info: CommitInfo,
|
||||
config: Dict[str, Any],
|
||||
llm_review: Optional[str] = None,
|
||||
) -> None:
|
||||
"""
|
||||
根据 log.enabled 决定是否持久化接口参数变更日志。
|
||||
|
||||
:param reports: 参数变更报告
|
||||
:param commit_info: 提交信息
|
||||
:param config: 完整配置
|
||||
:param llm_review: LLM 参数变更审核结论
|
||||
"""
|
||||
if not is_log_enabled(config):
|
||||
print("[日志] 日志开关已关闭(log.enabled=false),跳过写入。")
|
||||
return
|
||||
|
||||
log_cfg = config.get("log", {})
|
||||
if log_cfg.get("storage") == "mysql":
|
||||
save_to_mysql(reports, commit_info, log_cfg.get("mysql", {}), llm_review)
|
||||
else:
|
||||
save_to_file(
|
||||
reports,
|
||||
commit_info,
|
||||
log_cfg.get("file_dir", ".gitea/logs/api-changes"),
|
||||
llm_review,
|
||||
)
|
||||
Reference in New Issue
Block a user