58 lines
2.3 KiB
Python
58 lines
2.3 KiB
Python
"""Rule-based fraud amount assessment.
|
||
|
||
Classifies each transaction into high / medium / low confidence fraud,
|
||
and generates initial reason text.
|
||
"""
|
||
from app.models.transaction import TransactionRecord
|
||
from app.models.assessment import ConfidenceLevel
|
||
|
||
FRAUD_KEYWORDS = ["投资", "保证金", "手续费", "解冻", "税费", "充值", "提币", "提现"]
|
||
|
||
|
||
def classify_transaction(tx: TransactionRecord) -> tuple[ConfidenceLevel, str, str]:
|
||
"""Return (confidence_level, reason, exclude_reason)."""
|
||
if tx.is_transit:
|
||
return (
|
||
ConfidenceLevel.low,
|
||
f"该笔为本人账户间中转({tx.source_app.value} -> {tx.counterparty_name}),不直接计入被骗损失。",
|
||
"本人账户间互转,仅作为资金路径展示。",
|
||
)
|
||
|
||
if tx.direction.value == "in":
|
||
return (
|
||
ConfidenceLevel.low,
|
||
f"该笔为收入方向交易(+¥{float(tx.amount):,.2f}),通常不属于被骗损失。",
|
||
"收入交易不计入损失。",
|
||
)
|
||
|
||
remark = tx.remark or ""
|
||
counterparty = tx.counterparty_name or ""
|
||
confidence = tx.confidence
|
||
|
||
has_fraud_keyword = any(kw in remark or kw in counterparty for kw in FRAUD_KEYWORDS)
|
||
|
||
if confidence >= 0.9 and has_fraud_keyword:
|
||
return (
|
||
ConfidenceLevel.high,
|
||
f"受害人经{tx.source_app.value}向「{counterparty}」转账¥{float(tx.amount):,.2f},"
|
||
f"备注为「{remark}」,与诈骗常见话术吻合,OCR置信度{confidence:.0%}。",
|
||
"",
|
||
)
|
||
|
||
if confidence >= 0.85:
|
||
reason = (
|
||
f"受害人经{tx.source_app.value}向「{counterparty}」转账¥{float(tx.amount):,.2f}。"
|
||
)
|
||
if has_fraud_keyword:
|
||
reason += f"备注「{remark}」含涉诈关键词。"
|
||
return ConfidenceLevel.high, reason, ""
|
||
reason += "建议结合笔录确认是否受诱导操作。"
|
||
return ConfidenceLevel.medium, reason, "如经核实该笔为受害人主动日常消费,应排除。"
|
||
|
||
return (
|
||
ConfidenceLevel.medium,
|
||
f"受害人经{tx.source_app.value}向「{counterparty}」转账¥{float(tx.amount):,.2f},"
|
||
f"OCR置信度较低({confidence:.0%}),需人工复核。",
|
||
"OCR置信度不足,可能存在识别误差。",
|
||
)
|