438 lines
20 KiB
Python
438 lines
20 KiB
Python
import uuid
|
|
from collections.abc import AsyncIterator
|
|
from datetime import datetime
|
|
from typing import Any
|
|
|
|
from sqlalchemy import (
|
|
JSON,
|
|
BigInteger,
|
|
Boolean,
|
|
CheckConstraint,
|
|
DateTime,
|
|
ForeignKey,
|
|
Index,
|
|
Integer,
|
|
String,
|
|
Text,
|
|
UniqueConstraint,
|
|
func,
|
|
)
|
|
from sqlalchemy.dialects.postgresql import INET, JSONB, UUID
|
|
from sqlalchemy.ext.asyncio import (
|
|
AsyncEngine,
|
|
AsyncSession,
|
|
async_sessionmaker,
|
|
)
|
|
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
|
|
|
|
from app.postgres import create_postgres_engine
|
|
|
|
SCHEMA = "han_app"
|
|
|
|
|
|
class Base(DeclarativeBase):
|
|
type_annotation_map = {dict[str, Any]: JSON}
|
|
|
|
|
|
class BitrixBase(DeclarativeBase):
|
|
"""Models owned by bitrix-sync, excluded from han_app create_all."""
|
|
|
|
|
|
class Common:
|
|
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
record_status: Mapped[str] = mapped_column(String(1), default="A", server_default="A")
|
|
status_changed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
|
status_change_reason: Mapped[str | None] = mapped_column(String(255))
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
updated_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
|
|
)
|
|
updater_user_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True))
|
|
|
|
|
|
class UserIdentity(Common, Base):
|
|
__tablename__ = "user_identities"
|
|
__table_args__ = (Index("ix_user_identities_phone", "phone_number"), {"schema": SCHEMA})
|
|
keycloak_sub: Mapped[str] = mapped_column(String(255), unique=True)
|
|
phone_number: Mapped[str] = mapped_column(String(32))
|
|
last_login_at: Mapped[datetime] = mapped_column(DateTime(timezone=True))
|
|
|
|
|
|
class ClientProfile(Common, Base):
|
|
__tablename__ = "client_profiles"
|
|
__table_args__ = ({"schema": SCHEMA},)
|
|
user_id: Mapped[uuid.UUID] = mapped_column(
|
|
ForeignKey(f"{SCHEMA}.user_identities.id", ondelete="RESTRICT"), unique=True
|
|
)
|
|
bitrix_contact_id: Mapped[str | None] = mapped_column(String(64))
|
|
full_name: Mapped[str | None] = mapped_column(String(255))
|
|
citizenship: Mapped[str | None] = mapped_column(String(128))
|
|
russian_phone: Mapped[str | None] = mapped_column(String(32))
|
|
foreign_phone: Mapped[str | None] = mapped_column(String(32))
|
|
email: Mapped[str | None] = mapped_column(String(320))
|
|
source_updated_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
|
|
|
|
|
class UserConsent(Common, Base):
|
|
__tablename__ = "user_consents"
|
|
__table_args__ = (
|
|
UniqueConstraint("user_id", "consent_type", "document_version"),
|
|
CheckConstraint("consent_type IN ('personal_data','user_agreement','marketing')"),
|
|
{"schema": SCHEMA},
|
|
)
|
|
user_id: Mapped[uuid.UUID] = mapped_column(
|
|
ForeignKey(f"{SCHEMA}.user_identities.id", ondelete="RESTRICT")
|
|
)
|
|
ux_session_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True))
|
|
consent_type: Mapped[str] = mapped_column(String(32))
|
|
document_version: Mapped[str] = mapped_column(String(64))
|
|
accepted: Mapped[bool] = mapped_column(Boolean)
|
|
accepted_at: Mapped[datetime] = mapped_column(DateTime(timezone=True))
|
|
client_ip: Mapped[str | None] = mapped_column(INET)
|
|
user_agent_hash: Mapped[str | None] = mapped_column(String(64))
|
|
device_json: Mapped[dict[str, Any]] = mapped_column(JSONB, default=dict)
|
|
|
|
|
|
class UxSession(Common, Base):
|
|
__tablename__ = "ux_sessions"
|
|
__table_args__ = (
|
|
CheckConstraint("start_reason IN ('first_launch','cold_start','idle_timeout')"),
|
|
Index("ix_ux_sessions_user_started", "user_id", "started_at"),
|
|
{"schema": SCHEMA},
|
|
)
|
|
user_id: Mapped[uuid.UUID] = mapped_column(
|
|
ForeignKey(f"{SCHEMA}.user_identities.id", ondelete="RESTRICT")
|
|
)
|
|
start_reason: Mapped[str] = mapped_column(String(32))
|
|
platform: Mapped[str] = mapped_column(String(32))
|
|
app_version: Mapped[str] = mapped_column(String(64))
|
|
device_id_hash: Mapped[str | None] = mapped_column(String(64))
|
|
device_id: Mapped[str | None] = mapped_column(String(255))
|
|
started_at: Mapped[datetime] = mapped_column(DateTime(timezone=True))
|
|
|
|
|
|
class Dialog(Common, Base):
|
|
__tablename__ = "dialogs"
|
|
__table_args__ = (
|
|
CheckConstraint("status IN ('open','waiting_for_company','waiting_for_client','closed')"),
|
|
Index("ix_dialogs_user_updated", "user_id", "updated_at", "id"),
|
|
{"schema": SCHEMA},
|
|
)
|
|
user_id: Mapped[uuid.UUID] = mapped_column(
|
|
ForeignKey(f"{SCHEMA}.user_identities.id", ondelete="RESTRICT")
|
|
)
|
|
status: Mapped[str] = mapped_column(String(32), default="open")
|
|
last_message_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
|
closed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
|
|
|
|
|
class Message(Common, Base):
|
|
__tablename__ = "messages"
|
|
__table_args__ = (
|
|
CheckConstraint("sender_type IN ('client','company')"),
|
|
CheckConstraint("content_kind IN ('text','file')"),
|
|
CheckConstraint("safety_status IN ('pending','allowed','blocked','needs_review')"),
|
|
CheckConstraint(
|
|
"delivery_status IN ('accepted','processing','delivered','rejected','failed')"
|
|
),
|
|
Index("ix_messages_dialog_created", "dialog_id", "created_at", "id"),
|
|
UniqueConstraint("dialog_id", "client_idempotency_key"),
|
|
{"schema": SCHEMA},
|
|
)
|
|
dialog_id: Mapped[uuid.UUID] = mapped_column(
|
|
ForeignKey(f"{SCHEMA}.dialogs.id", ondelete="RESTRICT")
|
|
)
|
|
sender_type: Mapped[str] = mapped_column(String(16))
|
|
content_kind: Mapped[str] = mapped_column(String(16))
|
|
text: Mapped[str] = mapped_column(Text, default="")
|
|
safety_status: Mapped[str] = mapped_column(String(16))
|
|
safety_processing_mode: Mapped[str | None] = mapped_column(String(16))
|
|
safety_config_version: Mapped[int | None] = mapped_column(BigInteger)
|
|
safety_rules_version: Mapped[str | None] = mapped_column(String(128))
|
|
delivery_status: Mapped[str] = mapped_column(String(16))
|
|
external_message_id: Mapped[str | None] = mapped_column(String(255))
|
|
client_idempotency_key: Mapped[str | None] = mapped_column(String(128))
|
|
occurred_at: Mapped[datetime] = mapped_column(DateTime(timezone=True))
|
|
|
|
|
|
class MessageAttachment(Common, Base):
|
|
__tablename__ = "message_attachments"
|
|
__table_args__ = (
|
|
CheckConstraint("direction IN ('client_upload','company_inbound')"),
|
|
CheckConstraint("scan_status IN ('pending','clean','bypassed','infected','failed')"),
|
|
CheckConstraint("size_bytes > 0"),
|
|
{"schema": SCHEMA},
|
|
)
|
|
dialog_id: Mapped[uuid.UUID] = mapped_column(ForeignKey(f"{SCHEMA}.dialogs.id"))
|
|
message_id: Mapped[uuid.UUID | None] = mapped_column(
|
|
ForeignKey(f"{SCHEMA}.messages.id"), unique=True
|
|
)
|
|
owner_user_id: Mapped[uuid.UUID] = mapped_column(ForeignKey(f"{SCHEMA}.user_identities.id"))
|
|
direction: Mapped[str] = mapped_column(String(32))
|
|
original_file_name: Mapped[str] = mapped_column(String(255))
|
|
safe_file_name: Mapped[str] = mapped_column(String(255))
|
|
mime_type: Mapped[str] = mapped_column(String(128))
|
|
size_bytes: Mapped[int] = mapped_column(BigInteger)
|
|
checksum_sha256: Mapped[str | None] = mapped_column(String(64))
|
|
scan_status: Mapped[str] = mapped_column(String(16), default="pending")
|
|
storage_bucket: Mapped[str] = mapped_column(String(255))
|
|
object_key: Mapped[str] = mapped_column(String(1024))
|
|
quarantine_object_key: Mapped[str | None] = mapped_column(String(1024))
|
|
quarantine_version_id: Mapped[str | None] = mapped_column(String(1024))
|
|
quarantine_etag: Mapped[str | None] = mapped_column(String(1024))
|
|
upload_expires_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
|
completed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
|
|
|
|
|
class Document(Common, Base):
|
|
__tablename__ = "documents"
|
|
__table_args__ = (UniqueConstraint("storage_bucket", "object_key"), {"schema": SCHEMA})
|
|
user_id: Mapped[uuid.UUID] = mapped_column(ForeignKey(f"{SCHEMA}.user_identities.id"))
|
|
name: Mapped[str] = mapped_column(String(255))
|
|
mime_type: Mapped[str] = mapped_column(String(128))
|
|
size_bytes: Mapped[int] = mapped_column(BigInteger)
|
|
checksum_sha256: Mapped[str] = mapped_column(String(64))
|
|
storage_bucket: Mapped[str] = mapped_column(String(255))
|
|
object_key: Mapped[str] = mapped_column(String(1024))
|
|
sent_at: Mapped[datetime] = mapped_column(DateTime(timezone=True))
|
|
|
|
|
|
class SafetyTask(Base):
|
|
__tablename__ = "safety_tasks"
|
|
__table_args__ = ({"schema": SCHEMA},)
|
|
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
task_id: Mapped[str] = mapped_column(String(255), unique=True)
|
|
poll_location: Mapped[str] = mapped_column(String(1024))
|
|
message_id: Mapped[uuid.UUID] = mapped_column(ForeignKey(f"{SCHEMA}.messages.id"), unique=True)
|
|
attachment_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True))
|
|
quarantine_object_key: Mapped[str | None] = mapped_column(String(1024))
|
|
traceparent: Mapped[str | None] = mapped_column(String(55))
|
|
status: Mapped[str] = mapped_column(String(16))
|
|
processing_mode: Mapped[str | None] = mapped_column(String(16))
|
|
config_version: Mapped[int | None] = mapped_column(BigInteger)
|
|
rules_version: Mapped[str | None] = mapped_column(String(128))
|
|
expires_at: Mapped[datetime] = mapped_column(DateTime(timezone=True))
|
|
deadline_at: Mapped[datetime] = mapped_column(DateTime(timezone=True))
|
|
next_poll_at: Mapped[datetime] = mapped_column(DateTime(timezone=True))
|
|
attempt_count: Mapped[int] = mapped_column(Integer, default=0)
|
|
last_error_code: Mapped[str | None] = mapped_column(String(64))
|
|
locked_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
|
locked_by: Mapped[str | None] = mapped_column(String(128))
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
|
|
class DeliveryOutbox(Base):
|
|
__tablename__ = "delivery_outbox"
|
|
__table_args__ = ({"schema": SCHEMA},)
|
|
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
message_id: Mapped[uuid.UUID] = mapped_column(ForeignKey(f"{SCHEMA}.messages.id"), unique=True)
|
|
external_chat_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True))
|
|
payload_json: Mapped[dict[str, Any]] = mapped_column(JSON)
|
|
traceparent: Mapped[str | None] = mapped_column(String(55))
|
|
status: Mapped[str] = mapped_column(String(16), default="pending")
|
|
attempt_count: Mapped[int] = mapped_column(Integer, default=0)
|
|
next_attempt_at: Mapped[datetime] = mapped_column(DateTime(timezone=True))
|
|
last_error_code: Mapped[str | None] = mapped_column(String(64))
|
|
locked_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
|
locked_by: Mapped[str | None] = mapped_column(String(128))
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
|
|
class IdempotencyRecord(Base):
|
|
__tablename__ = "idempotency_records"
|
|
__table_args__ = (
|
|
UniqueConstraint("scope", "user_id", "idempotency_key"),
|
|
{"schema": SCHEMA},
|
|
)
|
|
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
scope: Mapped[str] = mapped_column(String(128))
|
|
user_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True))
|
|
idempotency_key: Mapped[str] = mapped_column(String(128))
|
|
request_fingerprint: Mapped[str] = mapped_column(String(64))
|
|
status: Mapped[str] = mapped_column(String(16))
|
|
response_status: Mapped[int | None] = mapped_column(Integer)
|
|
response_body_json: Mapped[dict[str, Any] | None] = mapped_column(JSON)
|
|
resource_type: Mapped[str | None] = mapped_column(String(64))
|
|
resource_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True))
|
|
expires_at: Mapped[datetime] = mapped_column(DateTime(timezone=True))
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
|
|
class OpenLinesInboxReceipt(Base):
|
|
__tablename__ = "openlines_inbox_receipts"
|
|
__table_args__ = (
|
|
UniqueConstraint("event_id"),
|
|
UniqueConstraint("external_chat_id", "bitrix_message_id"),
|
|
{"schema": SCHEMA},
|
|
)
|
|
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
event_id: Mapped[str] = mapped_column(String(255))
|
|
external_chat_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True))
|
|
bitrix_message_id: Mapped[str | None] = mapped_column(String(255))
|
|
event_type: Mapped[str] = mapped_column(String(32))
|
|
payload_fingerprint: Mapped[str] = mapped_column(String(64))
|
|
status: Mapped[str] = mapped_column(String(16))
|
|
message_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True))
|
|
last_error_code: Mapped[str | None] = mapped_column(String(64))
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
|
|
class AuditEvent(Base):
|
|
__tablename__ = "audit_events"
|
|
__table_args__ = ({"schema": SCHEMA},)
|
|
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
event_type: Mapped[str] = mapped_column(String(128))
|
|
actor_type: Mapped[str] = mapped_column(String(32))
|
|
user_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True))
|
|
ux_session_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True))
|
|
request_id: Mapped[str] = mapped_column(String(64))
|
|
trace_id: Mapped[str | None] = mapped_column(String(64))
|
|
resource_type: Mapped[str | None] = mapped_column(String(64))
|
|
resource_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True))
|
|
user_agent_hash: Mapped[str | None] = mapped_column(String(64))
|
|
outcome: Mapped[str] = mapped_column(String(32))
|
|
metadata_json: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
|
|
class AppSetting(Base):
|
|
__tablename__ = "app_settings"
|
|
__table_args__ = ({"schema": SCHEMA},)
|
|
setting_key: Mapped[str] = mapped_column(String(255), primary_key=True)
|
|
setting_value: Mapped[str] = mapped_column(Text)
|
|
value_type: Mapped[str] = mapped_column(String(32))
|
|
is_public: Mapped[bool] = mapped_column(Boolean, default=False)
|
|
description: Mapped[str | None] = mapped_column(Text)
|
|
record_status: Mapped[str] = mapped_column(String(1), default="A")
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
|
|
class TextResource(Common, Base):
|
|
__tablename__ = "text_resources"
|
|
__table_args__ = (
|
|
UniqueConstraint("mnemonic", "locale"),
|
|
{"schema": SCHEMA},
|
|
)
|
|
mnemonic: Mapped[str] = mapped_column(String(255))
|
|
locale: Mapped[str] = mapped_column(String(16), default="ru")
|
|
text_value: Mapped[str] = mapped_column(Text)
|
|
sort_order: Mapped[int] = mapped_column(Integer, default=0)
|
|
|
|
|
|
class PopularQuestion(Common, Base):
|
|
__tablename__ = "popular_questions"
|
|
__table_args__ = (
|
|
UniqueConstraint("mnemonic", "locale"),
|
|
{"schema": SCHEMA},
|
|
)
|
|
mnemonic: Mapped[str] = mapped_column(String(255))
|
|
locale: Mapped[str] = mapped_column(String(16), default="ru")
|
|
question_text: Mapped[str] = mapped_column(Text)
|
|
sort_order: Mapped[int] = mapped_column(Integer, default=0)
|
|
|
|
|
|
class SyncQueue(Base):
|
|
__tablename__ = "sync_queue"
|
|
__table_args__ = (
|
|
CheckConstraint(
|
|
"status IN ('pending','leased','processed','retry_wait','dead_letter','cancelled')"
|
|
),
|
|
Index("ix_sync_queue_claim", "status", "next_attempt_at", "created_at"),
|
|
Index("ix_sync_queue_entity_history", "entity_type", "entity_id", "created_at"),
|
|
{"schema": SCHEMA},
|
|
)
|
|
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
task_type: Mapped[str] = mapped_column(String(64))
|
|
entity_type: Mapped[str] = mapped_column(String(64))
|
|
entity_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True))
|
|
dedup_key: Mapped[str] = mapped_column(String(255))
|
|
payload_json: Mapped[dict[str, Any]] = mapped_column(JSON)
|
|
status: Mapped[str] = mapped_column(String(16), default="pending")
|
|
attempt_count: Mapped[int] = mapped_column(Integer, default=0)
|
|
next_attempt_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), server_default=func.now()
|
|
)
|
|
locked_by: Mapped[str | None] = mapped_column(String(128))
|
|
locked_until: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
|
lease_token: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True))
|
|
last_error_code: Mapped[str | None] = mapped_column(String(64))
|
|
last_error_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
|
completed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
|
cancel_reason: Mapped[str | None] = mapped_column(String(255))
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
|
|
class EntityExternalMapping(Base):
|
|
__tablename__ = "entity_external_mapping"
|
|
__table_args__ = (UniqueConstraint("entity_type", "entity_id"), {"schema": SCHEMA})
|
|
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
entity_type: Mapped[str] = mapped_column(String(64))
|
|
entity_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True))
|
|
external_id: Mapped[str] = mapped_column(String(128))
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
|
|
class BitrixEntityExternalMapping(BitrixBase):
|
|
__tablename__ = "entity_external_mapping"
|
|
__table_args__ = ({"schema": "bitrix_sync"},)
|
|
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
entity_type: Mapped[str] = mapped_column(String(64))
|
|
entity_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True))
|
|
external_system: Mapped[str] = mapped_column(String(32), default="bitrix24")
|
|
external_entity_type: Mapped[str] = mapped_column(String(32), default="contact")
|
|
external_id: Mapped[str] = mapped_column(String(128))
|
|
status: Mapped[str] = mapped_column(String(16), default="active")
|
|
opened_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
closed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
|
close_reason: Mapped[str | None] = mapped_column(String(64))
|
|
workflow_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True))
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
|
|
Index(
|
|
"uq_sync_queue_active_dedup",
|
|
SyncQueue.dedup_key,
|
|
unique=True,
|
|
postgresql_where=SyncQueue.status.in_(["pending", "leased", "retry_wait"]),
|
|
)
|
|
Index(
|
|
"ix_sync_queue_expired_lease",
|
|
SyncQueue.locked_until,
|
|
postgresql_where=SyncQueue.status == "leased",
|
|
)
|
|
Index(
|
|
"uq_external_mapping_active_entity",
|
|
BitrixEntityExternalMapping.external_system,
|
|
BitrixEntityExternalMapping.entity_type,
|
|
BitrixEntityExternalMapping.entity_id,
|
|
unique=True,
|
|
postgresql_where=BitrixEntityExternalMapping.status == "active",
|
|
)
|
|
Index(
|
|
"uq_external_mapping_active_external",
|
|
BitrixEntityExternalMapping.external_system,
|
|
BitrixEntityExternalMapping.external_entity_type,
|
|
BitrixEntityExternalMapping.external_id,
|
|
unique=True,
|
|
postgresql_where=BitrixEntityExternalMapping.status == "active",
|
|
)
|
|
|
|
|
|
class Database:
|
|
def __init__(self, url: str) -> None:
|
|
self.engine: AsyncEngine = create_postgres_engine(url, pool_pre_ping=True)
|
|
self.sessions = async_sessionmaker(self.engine, expire_on_commit=False)
|
|
|
|
async def session(self) -> AsyncIterator[AsyncSession]:
|
|
async with self.sessions() as session:
|
|
yield session
|
|
|
|
async def close(self) -> None:
|
|
await self.engine.dispose()
|