diff --git a/inventory-backend/app/utils/decorators.py b/inventory-backend/app/utils/decorators.py index 0d5dc5a..867f9d6 100644 --- a/inventory-backend/app/utils/decorators.py +++ b/inventory-backend/app/utils/decorators.py @@ -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(