root 656f596d7e feat: implement AI legal assistant system MVP
Core modules:
- Laws: CRUD, search, AI-powered QA
- Analysis: legal research and case management
- Contracts: lifecycle management with templates
- Signatures: electronic signature workflow

Infrastructure:
- FastAPI + SQLite + async SQLAlchemy
- Docker deployment support
- 54 unit tests passing

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

120 lines
4.2 KiB
Python

"""Signature model for electronic signatures."""
import enum
from datetime import datetime
from typing import Optional
from sqlalchemy import String, Text, DateTime, Enum as SQLEnum, Integer, ForeignKey, JSON
from sqlalchemy.orm import Mapped, mapped_column
from app.core.database import Base
class SignatureStatus(str, enum.Enum):
"""Signature status enumeration."""
PENDING = "pending"
SIGNED = "signed"
REJECTED = "rejected"
EXPIRED = "expired"
class SignatureRequest(Base):
"""Signature request model."""
__tablename__ = "signature_requests"
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
contract_id: Mapped[int] = mapped_column(Integer, ForeignKey("contracts.id"), index=True)
requester_id: Mapped[int] = mapped_column(Integer, ForeignKey("users.id"), index=True)
signer_name: Mapped[str] = mapped_column(String(100))
signer_email: Mapped[str] = mapped_column(String(100))
status: Mapped[SignatureStatus] = mapped_column(
SQLEnum(SignatureStatus),
default=SignatureStatus.PENDING
)
token: Mapped[str] = mapped_column(String(64), unique=True, index=True)
expires_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
def __init__(
self,
contract_id: int,
requester_id: int,
signer_name: str,
signer_email: str,
token: str,
expires_at: datetime,
status: SignatureStatus = SignatureStatus.PENDING,
**kwargs
):
self.contract_id = contract_id
self.requester_id = requester_id
self.signer_name = signer_name
self.signer_email = signer_email
self.token = token
self.expires_at = expires_at
self.status = status
self.created_at = datetime.utcnow()
class Signature(Base):
"""Signature record model."""
__tablename__ = "signatures"
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
request_id: Mapped[int] = mapped_column(Integer, ForeignKey("signature_requests.id"), index=True)
signer_name: Mapped[str] = mapped_column(String(100))
signature_data: Mapped[str] = mapped_column(Text) # Base64 image or coordinates
ip_address: Mapped[Optional[str]] = mapped_column(String(50), nullable=True)
user_agent: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
verification_hash: Mapped[str] = mapped_column(String(64)) # Document fingerprint
signed_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
def __init__(
self,
request_id: int,
signer_name: str,
signature_data: str,
verification_hash: str,
ip_address: Optional[str] = None,
user_agent: Optional[str] = None,
**kwargs
):
self.request_id = request_id
self.signer_name = signer_name
self.signature_data = signature_data
self.verification_hash = verification_hash
self.ip_address = ip_address
self.user_agent = user_agent
self.signed_at = datetime.utcnow()
class SignatureAudit(Base):
"""Signature audit log model."""
__tablename__ = "signature_audits"
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
signature_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("signatures.id"), nullable=True)
request_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("signature_requests.id"), nullable=True)
action: Mapped[str] = mapped_column(String(50))
details: Mapped[Optional[dict]] = mapped_column(JSON, nullable=True)
ip_address: Mapped[Optional[str]] = mapped_column(String(50), nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
def __init__(
self,
action: str,
signature_id: Optional[int] = None,
request_id: Optional[int] = None,
details: Optional[dict] = None,
ip_address: Optional[str] = None,
**kwargs
):
self.action = action
self.signature_id = signature_id
self.request_id = request_id
self.details = details
self.ip_address = ip_address
self.created_at = datetime.utcnow()