refactor: add rule engine, risk service, quote state machine, and replay tests

This commit is contained in:
2026-03-01 14:30:14 +08:00
parent dc2565b8f3
commit 3c825547cf
9 changed files with 590 additions and 137 deletions

89
utils/observability.py Normal file
View File

@@ -0,0 +1,89 @@
from __future__ import annotations
import hashlib
import json
import logging
import time
from typing import Any, Dict, Optional
def build_trace_id(*parts: str) -> str:
raw = "|".join(str(p or "") for p in parts)
digest = hashlib.md5(raw.encode("utf-8")).hexdigest()
return digest[:16]
def emit_activity(
logger: logging.Logger,
*,
event: str,
trace_id: str = "",
customer_id: str = "",
result: str = "ok",
latency_ms: Optional[int] = None,
**fields: Any,
) -> None:
payload: Dict[str, Any] = {
"trace_id": trace_id or "-",
"customer_id": customer_id or "-",
"event": event,
"result": result,
}
if latency_ms is not None:
payload["latency_ms"] = int(max(0, latency_ms))
for k, v in (fields or {}).items():
if isinstance(v, str):
payload[k] = v[:400]
else:
payload[k] = v
try:
logger.info(f"[ACTIVITY] {json.dumps(payload, ensure_ascii=False)}")
except Exception:
logger.info(f"[ACTIVITY] {payload}")
class ActivityTimer:
def __init__(
self,
*,
logger: logging.Logger,
event: str,
trace_id: str = "",
customer_id: str = "",
**fields: Any,
):
self.logger = logger
self.event = event
self.trace_id = trace_id
self.customer_id = customer_id
self.fields = fields
self.start = time.monotonic()
def ok(self, **fields: Any) -> None:
elapsed_ms = int((time.monotonic() - self.start) * 1000)
merged = dict(self.fields)
merged.update(fields)
emit_activity(
self.logger,
event=self.event,
trace_id=self.trace_id,
customer_id=self.customer_id,
result="ok",
latency_ms=elapsed_ms,
**merged,
)
def fail(self, error: str, **fields: Any) -> None:
elapsed_ms = int((time.monotonic() - self.start) * 1000)
merged = dict(self.fields)
merged.update(fields)
emit_activity(
self.logger,
event=self.event,
trace_id=self.trace_id,
customer_id=self.customer_id,
result="error",
latency_ms=elapsed_ms,
error=error,
**merged,
)