root 6f61d0660c feat: implement 5 high-value Phase 2 features
Phase 2 features:
1. Contract Risk Analysis - AI-powered risk detection with suggestions
2. Case Prediction Engine - Win probability and outcome prediction
3. Legal Knowledge Graph - Entity and relation management
4. Multi-language Translation - Legal document translation
5. Lawyer Matching - Intelligent lawyer recommendation

All 63 unit tests passing.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-01 03:42:37 +08:00

254 lines
8.9 KiB
Python

"""Phase 2 models: Risk, Prediction, Knowledge Graph, Translation, Lawyer."""
import enum
from datetime import datetime
from typing import Optional, List
from sqlalchemy import String, Text, DateTime, Enum as SQLEnum, Integer, ForeignKey, Float, JSON
from sqlalchemy.orm import Mapped, mapped_column
from app.core.database import Base
# ============ Feature 1: Contract Risk ============
class RiskLevel(str, enum.Enum):
HIGH = "high"
MEDIUM = "medium"
LOW = "low"
class RiskType(str, enum.Enum):
VAGUE = "vague" # 模糊条款
UNFAIR = "unfair" # 不公平条款
ILLEGAL = "illegal" # 违法条款
MISSING = "missing" # 缺失条款
CONFLICT = "conflict" # 冲突条款
AMBIGUOUS = "ambiguous" # 歧义条款
class ContractRisk(Base):
"""Contract risk analysis result."""
__tablename__ = "contract_risks"
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
contract_id: Mapped[int] = mapped_column(Integer, ForeignKey("contracts.id"), index=True)
clause_text: Mapped[str] = mapped_column(Text)
risk_type: Mapped[RiskType] = mapped_column(SQLEnum(RiskType))
risk_level: Mapped[RiskLevel] = mapped_column(SQLEnum(RiskLevel))
description: Mapped[str] = mapped_column(Text)
suggestion: Mapped[str] = mapped_column(Text)
position: Mapped[Optional[dict]] = mapped_column(JSON, nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
def __init__(
self,
contract_id: int,
clause_text: str,
risk_type: RiskType,
risk_level: RiskLevel,
description: str,
suggestion: str,
position: Optional[dict] = None,
**kwargs
):
self.contract_id = contract_id
self.clause_text = clause_text
self.risk_type = risk_type
self.risk_level = risk_level
self.description = description
self.suggestion = suggestion
self.position = position
self.created_at = datetime.utcnow()
# ============ Feature 2: Case Prediction ============
class CasePrediction(Base):
"""Case prediction analysis."""
__tablename__ = "case_predictions"
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
user_id: Mapped[int] = mapped_column(Integer, ForeignKey("users.id"), index=True)
case_description: Mapped[str] = mapped_column(Text)
predicted_outcome: Mapped[str] = mapped_column(Text)
win_probability: Mapped[float] = mapped_column(Float)
similar_cases: Mapped[Optional[list]] = mapped_column(JSON, nullable=True)
key_factors: Mapped[Optional[list]] = mapped_column(JSON, nullable=True)
confidence: Mapped[float] = mapped_column(Float)
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
def __init__(
self,
user_id: int,
case_description: str,
predicted_outcome: str,
win_probability: float,
confidence: float,
similar_cases: Optional[list] = None,
key_factors: Optional[list] = None,
**kwargs
):
self.user_id = user_id
self.case_description = case_description
self.predicted_outcome = predicted_outcome
self.win_probability = win_probability
self.confidence = confidence
self.similar_cases = similar_cases
self.key_factors = key_factors
self.created_at = datetime.utcnow()
# ============ Feature 3: Knowledge Graph ============
class EntityType(str, enum.Enum):
LAW = "law"
ARTICLE = "article"
CASE = "case"
CONCEPT = "concept"
ORGANIZATION = "organization"
class RelationType(str, enum.Enum):
REFERENCES = "references"
AMENDS = "amends"
REPLACES = "replaces"
INTERPRETS = "interprets"
APPLIES = "applies"
DEFINES = "defines"
class LegalEntity(Base):
"""Legal knowledge graph entity."""
__tablename__ = "legal_entities"
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
name: Mapped[str] = mapped_column(String(200), index=True)
entity_type: Mapped[EntityType] = mapped_column(SQLEnum(EntityType))
properties: Mapped[Optional[dict]] = mapped_column(JSON, nullable=True)
source_id: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
def __init__(
self,
name: str,
entity_type: EntityType,
properties: Optional[dict] = None,
source_id: Optional[int] = None,
**kwargs
):
self.name = name
self.entity_type = entity_type
self.properties = properties
self.source_id = source_id
self.created_at = datetime.utcnow()
class LegalRelation(Base):
"""Legal knowledge graph relation."""
__tablename__ = "legal_relations"
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
source_id: Mapped[int] = mapped_column(Integer, ForeignKey("legal_entities.id"), index=True)
target_id: Mapped[int] = mapped_column(Integer, ForeignKey("legal_entities.id"), index=True)
relation_type: Mapped[RelationType] = mapped_column(SQLEnum(RelationType))
weight: Mapped[float] = mapped_column(Float, default=1.0)
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
def __init__(
self,
source_id: int,
target_id: int,
relation_type: RelationType,
weight: float = 1.0,
**kwargs
):
self.source_id = source_id
self.target_id = target_id
self.relation_type = relation_type
self.weight = weight
self.created_at = datetime.utcnow()
# ============ Feature 4: Translation ============
class TranslationRecord(Base):
"""Translation record."""
__tablename__ = "translation_records"
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
document_id: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
source_text: Mapped[str] = mapped_column(Text)
source_lang: Mapped[str] = mapped_column(String(10))
target_text: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
target_lang: Mapped[str] = mapped_column(String(10))
status: Mapped[str] = mapped_column(String(20), default="pending")
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
completed_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
def __init__(
self,
source_text: str,
source_lang: str,
target_lang: str,
document_id: Optional[int] = None,
status: str = "pending",
**kwargs
):
self.source_text = source_text
self.source_lang = source_lang
self.target_lang = target_lang
self.document_id = document_id
self.status = status
self.created_at = datetime.utcnow()
# ============ Feature 5: Lawyer Matching ============
class Lawyer(Base):
"""Lawyer profile."""
__tablename__ = "lawyers"
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
name: Mapped[str] = mapped_column(String(100))
specialties: Mapped[Optional[list]] = mapped_column(JSON, nullable=True)
experience_years: Mapped[int] = mapped_column(Integer, default=0)
success_rate: Mapped[float] = mapped_column(Float, default=0.0)
cases_count: Mapped[int] = mapped_column(Integer, default=0)
rating: Mapped[float] = mapped_column(Float, default=0.0)
bio: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
contact: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
def __init__(
self,
name: str,
specialties: Optional[list] = None,
experience_years: int = 0,
**kwargs
):
self.name = name
self.specialties = specialties or []
self.experience_years = experience_years
self.created_at = datetime.utcnow()
class LawyerRecommendation(Base):
"""Lawyer recommendation for a case."""
__tablename__ = "lawyer_recommendations"
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
case_description: Mapped[str] = mapped_column(Text)
lawyer_id: Mapped[int] = mapped_column(Integer, ForeignKey("lawyers.id"), index=True)
match_score: Mapped[float] = mapped_column(Float)
match_reasons: Mapped[Optional[list]] = mapped_column(JSON, nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
def __init__(
self,
case_description: str,
lawyer_id: int,
match_score: float,
match_reasons: Optional[list] = None,
**kwargs
):
self.case_description = case_description
self.lawyer_id = lawyer_id
self.match_score = match_score
self.match_reasons = match_reasons
self.created_at = datetime.utcnow()