Files
group_xinghuo_jinrong/app/model/suitability.py
T

66 lines
2.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""适当性判定 → risk_suitability_log 行映射(R-02 P0 契约)。"""
from __future__ import annotations
from typing import Any, Literal
CheckSource = Literal["r02_trade", "r02_chat", "c11_inquiry", "manual"]
def compute_rule_refs(check: dict[str, Any]) -> list[str]:
"""由 check 结果计算 rule_refs(JR-AST/FM 规则编号体系)。
独立公共函数(AL-05 抽取,main 逻辑不变):供 build_suitability_log_row
落库与 service 层 SuitabilityResult.rule_refs 共用,避免两处漂移。
"""
rule_refs: list[str] = []
mismatch_type = check.get("mismatch_type") or "not_found"
if mismatch_type == "risk_level":
rule_refs.append("JR-AST-012")
if check.get("risk_is_expired") or mismatch_type == "risk_expired":
rule_refs.append("FM-03")
if check.get("needs_branch_confirm") or mismatch_type == "age_branch_confirm":
rule_refs.append("FM-01")
if mismatch_type == "professional_exempt":
rule_refs.append("JR-AST-PRO")
return rule_refs
def build_suitability_log_row(
check: dict[str, Any],
*,
trace_id: str,
actor_id: str,
check_source: CheckSource = "r02_trade",
request_ref: str | None = None,
profile_l1_version: int | None = None,
) -> dict[str, Any]:
"""将 CoreReadOnlyRepository.check_suitability 结果转为 INSERT 用 dict。"""
rule_refs = compute_rule_refs(check)
match_result = check.get("match_result") or "forbidden"
mismatch_type = check.get("mismatch_type") or "not_found"
return {
"trace_id": trace_id,
"customer_id": check.get("customer_id") or "",
"product_id": check.get("product_id") or "",
"product_name": check.get("product_name"),
"customer_risk_level": check.get("customer_risk_code") or "C1",
"product_risk_level": check.get("product_risk_code") or "R1",
"investor_category": check.get("investor_category") or "ordinary",
"match_result": match_result,
"mismatch_type": mismatch_type,
"is_matched": 1 if check.get("matched") else 0,
"is_blocked": 1 if check.get("blocked") else 0,
"requires_disclosure": 1 if check.get("requires_disclosure") else 0,
"needs_branch_confirm": 1 if check.get("needs_branch_confirm") else 0,
"risk_was_expired": 1 if check.get("risk_is_expired") else 0,
"block_reason": check.get("reason"),
"block_response_code": check.get("block_response_code"),
"check_source": check_source,
"actor_id": actor_id,
"request_ref": request_ref,
"profile_l1_version": profile_l1_version,
"rule_refs": rule_refs or None,
}