feat: 迁移奶龙风控业务模块与演示文档

This commit is contained in:
zhangshy
2026-09-10 21:03:44 +08:00
parent 6516ccb385
commit a94d5c754d
62 changed files with 10815 additions and 0 deletions
+115
View File
@@ -0,0 +1,115 @@
"""风控查询所需的补充只读 ORM 映射。
第二版 `app/model/fund.py` 已覆盖多数 `fin_*` 表;本模块只补充风控查询需要、
但当前尚未映射的三张已有表,不负责建表、迁移或写入。
安全边界:
1. 只声明列映射,不提供写入方法。
2. `sys_user.password_hash` 和 `email` 不进入风控只读模型。
3. 不声明 ForeignKey,避免 ORM 写入时产生级联语义。
"""
from datetime import datetime
from decimal import Decimal
from typing import Any
from sqlalchemy import JSON, Numeric, String, Text
from sqlalchemy.dialects.mysql import BIGINT, DATETIME, TINYINT
from sqlalchemy.orm import Mapped, mapped_column
from app.model.base import Base
__all__ = ["RiskLoginRecord", "RiskUser", "RiskWorkOrder"]
class RiskUser(Base):
"""统一身份表中的风控只读字段(sys_user)。"""
__tablename__ = "sys_user"
id: Mapped[int] = mapped_column(BIGINT(unsigned=True), primary_key=True)
user_no: Mapped[str] = mapped_column(String(32), nullable=False)
username: Mapped[str] = mapped_column(String(64), nullable=False)
user_type: Mapped[str] = mapped_column(String(16), nullable=False)
employee_role: Mapped[str | None] = mapped_column(String(32), nullable=True)
customer_tier: Mapped[str | None] = mapped_column(String(16), nullable=True)
investor_type: Mapped[str | None] = mapped_column(String(8), nullable=True)
investor_type_assessed_at: Mapped[datetime | None] = mapped_column(
DATETIME(fsp=0), nullable=True
)
is_professional_investor: Mapped[int] = mapped_column(
TINYINT(display_width=1), nullable=False
)
professional_investor_status: Mapped[str] = mapped_column(String(16), nullable=False)
professional_investor_certified_at: Mapped[datetime | None] = mapped_column(
DATETIME(fsp=0), nullable=True
)
fund_account_status: Mapped[str] = mapped_column(String(16), nullable=False)
fund_account_opened_at: Mapped[datetime | None] = mapped_column(
DATETIME(fsp=0), nullable=True
)
status: Mapped[str] = mapped_column(String(16), nullable=False)
created_at: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False)
updated_at: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False)
class RiskLoginRecord(Base):
"""登录记录只读映射(sys_login_record)。"""
__tablename__ = "sys_login_record"
id: Mapped[int] = mapped_column(BIGINT(unsigned=True), primary_key=True)
user_id: Mapped[int] = mapped_column(BIGINT(unsigned=True), nullable=False)
login_at: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False)
login_result: Mapped[str] = mapped_column(String(16), nullable=False)
ip_region: Mapped[str | None] = mapped_column(String(64), nullable=True)
device_id: Mapped[str | None] = mapped_column(String(128), nullable=True)
is_common_device: Mapped[int] = mapped_column(TINYINT(display_width=1), nullable=False)
failure_reason: Mapped[str | None] = mapped_column(String(128), nullable=True)
created_at: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False)
class RiskWorkOrder(Base):
"""风险处置及交易申请兼容工单只读映射(biz_work_order)。"""
__tablename__ = "biz_work_order"
id: Mapped[int] = mapped_column(BIGINT(unsigned=True), primary_key=True)
work_order_no: Mapped[str] = mapped_column(String(64), nullable=False)
customer_id: Mapped[int] = mapped_column(BIGINT(unsigned=True), nullable=False)
order_type: Mapped[str | None] = mapped_column(String(16), nullable=True)
product_id: Mapped[int | None] = mapped_column(BIGINT(unsigned=True), nullable=True)
amount: Mapped[Decimal | None] = mapped_column(Numeric(18, 2), nullable=True)
channel: Mapped[str | None] = mapped_column(String(32), nullable=True)
advisor_id: Mapped[int | None] = mapped_column(BIGINT(unsigned=True), nullable=True)
risk_rule_hits: Mapped[Any | None] = mapped_column(JSON, nullable=True)
risk_disclosure_ack_at: Mapped[datetime | None] = mapped_column(
DATETIME(fsp=0), nullable=True
)
second_confirmation_at: Mapped[datetime | None] = mapped_column(
DATETIME(fsp=0), nullable=True
)
recording_reference: Mapped[str | None] = mapped_column(String(64), nullable=True)
ops_handler_id: Mapped[int | None] = mapped_column(BIGINT(unsigned=True), nullable=True)
ops_handled_at: Mapped[datetime | None] = mapped_column(DATETIME(fsp=0), nullable=True)
compliance_handler_id: Mapped[int | None] = mapped_column(
BIGINT(unsigned=True), nullable=True
)
compliance_handled_at: Mapped[datetime | None] = mapped_column(
DATETIME(fsp=0), nullable=True
)
reject_reason: Mapped[str | None] = mapped_column(Text, nullable=True)
alert_id: Mapped[int | None] = mapped_column(BIGINT(unsigned=True), nullable=True)
work_order_type: Mapped[str | None] = mapped_column(String(32), nullable=True)
submitter_id: Mapped[int | None] = mapped_column(BIGINT(unsigned=True), nullable=True)
handler_id: Mapped[int | None] = mapped_column(BIGINT(unsigned=True), nullable=True)
priority: Mapped[str | None] = mapped_column(String(8), nullable=True)
status: Mapped[str] = mapped_column(String(24), nullable=False)
request_detail: Mapped[dict[str, Any] | None] = mapped_column(JSON, nullable=True)
handle_result: Mapped[dict[str, Any] | None] = mapped_column(JSON, nullable=True)
submitted_at: Mapped[datetime | None] = mapped_column(DATETIME(fsp=0), nullable=True)
accepted_at: Mapped[datetime | None] = mapped_column(DATETIME(fsp=0), nullable=True)
completed_at: Mapped[datetime | None] = mapped_column(DATETIME(fsp=0), nullable=True)
created_at: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False)
updated_at: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False)