feat: 升级审计装饰器,支持自动抓取并记录 API 请求体作为变更明细
This commit is contained in:
@ -95,7 +95,45 @@ def audit_log(module: str, action: str = None, get_target_id_fn=None, get_target
|
||||
审计日志装饰器
|
||||
用法: @audit_log(module='inbound_buy', action='create')
|
||||
@audit_log(module='bom', action='update', get_target_id_fn=lambda: ..., get_details_fn=lambda req, resp: ...)
|
||||
|
||||
升级特性:
|
||||
- 自动捕获请求 Payload 作为变更明细
|
||||
- 自动过滤过长的 Base64 图片数据
|
||||
- 支持自定义 get_details_fn 覆盖默认行为
|
||||
"""
|
||||
# 需要过滤的图片字段
|
||||
IMAGE_FIELDS = {'arrival_photo', 'product_photo', 'photo', 'image', 'signature', 'borrow_signature', 'return_signature'}
|
||||
|
||||
def _filter_payload(payload):
|
||||
"""过滤 Payload 中的大字段,防止数据库膨胀"""
|
||||
if not payload or not isinstance(payload, dict):
|
||||
return payload
|
||||
filtered = {}
|
||||
for key, value in payload.items():
|
||||
if key.lower() in IMAGE_FIELDS and isinstance(value, str) and len(value) > 100:
|
||||
filtered[key] = '[图片数据已省略]'
|
||||
elif isinstance(value, dict):
|
||||
filtered[key] = _filter_payload(value)
|
||||
elif isinstance(value, list):
|
||||
filtered[key] = [
|
||||
_filter_payload(item) if isinstance(item, dict) else item
|
||||
for item in value
|
||||
]
|
||||
else:
|
||||
filtered[key] = value
|
||||
return filtered
|
||||
|
||||
def _get_payload():
|
||||
"""自动获取请求 Payload"""
|
||||
# 尝试 JSON
|
||||
payload = request.get_json(silent=True)
|
||||
if payload:
|
||||
return payload
|
||||
# 尝试 Form Data
|
||||
if request.form:
|
||||
return request.form.to_dict()
|
||||
return None
|
||||
|
||||
def wrapper(fn):
|
||||
@wraps(fn)
|
||||
def decorator(*args, **kwargs):
|
||||
@ -109,7 +147,7 @@ def audit_log(module: str, action: str = None, get_target_id_fn=None, get_target
|
||||
ip_address = request.headers.get('X-Forwarded-For') or request.remote_addr or ''
|
||||
if ip_address and ',' in ip_address:
|
||||
ip_address = ip_address.split(',')[0].strip()
|
||||
|
||||
|
||||
# 获取请求信息
|
||||
http_method = request.method
|
||||
url = request.url
|
||||
@ -120,6 +158,10 @@ def audit_log(module: str, action: str = None, get_target_id_fn=None, get_target
|
||||
if callable(action):
|
||||
final_action = action()
|
||||
|
||||
# 预先获取 Payload(用于后续 details 记录)
|
||||
raw_payload = _get_payload()
|
||||
filtered_payload = _filter_payload(raw_payload) if raw_payload else None
|
||||
|
||||
# 执行原函数
|
||||
response = fn(*args, **kwargs)
|
||||
|
||||
@ -157,10 +199,14 @@ def audit_log(module: str, action: str = None, get_target_id_fn=None, get_target
|
||||
# 获取 details
|
||||
details = None
|
||||
if get_details_fn:
|
||||
# 优先使用自定义差异对比函数
|
||||
try:
|
||||
details = get_details_fn(request, response)
|
||||
except Exception:
|
||||
pass
|
||||
elif filtered_payload:
|
||||
# 默认:记录请求 Payload
|
||||
details = {'payload': filtered_payload}
|
||||
|
||||
# 保存日志
|
||||
log_entry = AuditLog(
|
||||
|
||||
Reference in New Issue
Block a user