fix(points): @points_gate decorator filter internal kwargs to avoid TypeError on routes without **kwargs
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 3s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m58s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 29s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 1m32s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 4m28s
AI Code Review / AI Code Review (pull_request) Successful in 6m46s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 9m6s
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 17h52m20s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 17h51m38s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 17h51m38s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 17h53m10s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 17h55m40s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 17h53m39s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 17h53m40s

This commit is contained in:
xiaoxia-agent
2026-09-15 20:11:34 +08:00
parent 8b192b7c74
commit fc8edf1e27
+22 -8
View File
@@ -69,7 +69,7 @@ def points_gate(
@functools.wraps(func)
def sync_wrapper(*args: Any, **kwargs: Any) -> Any:
if not _points_gate_enabled():
return func(*args, **kwargs)
return func(*args, **_filter_kwargs(func, kwargs))
return _execute_with_gate(
func, args, kwargs, scene_key, per_unit, unit_field, quantity_field, is_async=False
)
@@ -81,6 +81,20 @@ def points_gate(
return decorator
def _filter_kwargs(func: Callable, kwargs: dict) -> dict:
"""过滤掉目标函数签名不接受的 kwargs(避免 TypeError)。"""
import inspect
try:
sig = inspect.signature(func)
params = sig.parameters
has_var_keyword = any(p.kind == inspect.Parameter.VAR_KEYWORD for p in params.values())
if has_var_keyword:
return kwargs
return {k: v for k, v in kwargs.items() if k in params}
except (ValueError, TypeError):
return kwargs
def _extract_kwargs(func: Callable, args: tuple, kwargs: dict) -> dict:
"""将位置参数映射到函数签名中的参数名,便于统一按 kwargs 提取。"""
sig = inspect.signature(func)
@@ -134,8 +148,8 @@ def _execute_with_gate(
kwargs["_points_deducted"] = 0
kwargs["_is_free_quota"] = True
if is_async:
return _run_async(func, args, kwargs)
return func(*args, **kwargs)
return _run_async(func, args, _filter_kwargs(func, kwargs))
return func(*args, **_filter_kwargs(func, kwargs))
# ── 计算积分消耗 ──
if per_unit is not None:
@@ -163,8 +177,8 @@ def _execute_with_gate(
if total_points == 0:
kwargs["_points_deducted"] = 0
if is_async:
return _run_async(func, args, kwargs)
return func(*args, **kwargs)
return _run_async(func, args, _filter_kwargs(func, kwargs))
return func(*args, **_filter_kwargs(func, kwargs))
# ── 扣减积分 ──
from packages.domain.points_service import PointsService
@@ -190,8 +204,8 @@ def _execute_with_gate(
# ── 执行业务函数,失败则退还积分 ──
try:
if is_async:
return _run_async(func, args, kwargs)
return func(*args, **kwargs)
return _run_async(func, args, _filter_kwargs(func, kwargs))
return func(*args, **_filter_kwargs(func, kwargs))
except Exception:
svc.refund_points(user.id, total_points, scene_key, db, ref_id=str(job_id))
raise
@@ -199,4 +213,4 @@ def _execute_with_gate(
def _run_async(func: Callable, args: tuple, kwargs: dict):
"""在 async wrapper 中 await 原始 async 函数。"""
return func(*args, **kwargs)
return func(*args, **_filter_kwargs(func, kwargs))