Финальная стабильная реализация

This commit is contained in:
mi
2026-08-21 11:48:24 +03:00
parent 6e24278b10
commit d2415fcfeb
17 changed files with 662 additions and 57 deletions
@@ -12,6 +12,8 @@ from typing import Any
from sqlalchemy import text
from sqlalchemy.ext.asyncio import AsyncConnection, AsyncEngine, create_async_engine
from app.domain import safe_hash
def postgres_ssl_context() -> ssl.SSLContext:
ca_file = os.environ.get("PG_CA_FILE")
@@ -83,9 +85,12 @@ class Repository:
WITH candidates AS (
SELECT id
FROM han_app.sync_queue
WHERE status IN ('pending','retry_wait')
AND next_attempt_at <= now()
AND (locked_until IS NULL OR locked_until < now())
WHERE (
status IN ('pending','retry_wait') AND next_attempt_at <= now()
)
OR (
status='leased' AND locked_until < now()
)
ORDER BY next_attempt_at, created_at
FOR UPDATE SKIP LOCKED
LIMIT :limit
@@ -156,7 +161,7 @@ class Repository:
"""
)
async with self.engine.begin() as connection:
return (
workflow_id = (
await connection.execute(
sql,
{
@@ -167,6 +172,17 @@ class Repository:
},
)
).scalar_one()
await connection.execute(
text(
"""
UPDATE bitrix_sync.workflow_instances
SET state='running',current_step='load_profile',updated_at=now()
WHERE id=:id AND state IN ('created','running')
"""
),
{"id": workflow_id},
)
return workflow_id
async def complete_task(self, task: LeasedTask, workflow_id: uuid.UUID) -> bool:
async with self.engine.begin() as connection:
@@ -280,8 +296,12 @@ class Repository:
"""
WITH candidates AS (
SELECT id FROM bitrix_sync.webhook_inbox
WHERE status IN ('received','retry_wait') AND next_attempt_at<=now()
AND (locked_until IS NULL OR locked_until<now())
WHERE (
status IN ('received','retry_wait') AND next_attempt_at<=now()
)
OR (
status='processing' AND locked_until<now()
)
ORDER BY next_attempt_at,received_at
FOR UPDATE SKIP LOCKED LIMIT :limit
)
@@ -345,7 +365,8 @@ class Repository:
text(
"""
UPDATE han_app.client_profiles
SET full_name=:full_name,citizenship=:citizenship,email=:email,updated_at=now()
SET full_name=:full_name,citizenship=:citizenship,email=:email,
source_updated_at=:source_updated_at,updated_at=now()
WHERE user_id=:user_id AND record_status='A'
"""
),
@@ -354,6 +375,7 @@ class Repository:
"full_name": full_name,
"citizenship": citizenship,
"email": email,
"source_updated_at": source_updated_at,
},
)
await connection.execute(
@@ -363,14 +385,20 @@ class Repository:
(id,mapping_id,user_id,external_id,full_name_hash,email_hash,
citizenship_hash,source_updated_at,last_applied_source,
last_webhook_received_at,created_at,updated_at)
SELECT gen_random_uuid(),m.id,:user_id,:external_id,
encode(digest(coalesce(:full_name,''),'sha256'),'hex'),
encode(digest(coalesce(:email,''),'sha256'),'hex'),
encode(digest(coalesce(:citizenship,''),'sha256'),'hex'),
:source_updated_at,:source,
CASE WHEN :source='webhook' THEN now() END,now(),now()
SELECT gen_random_uuid(),m.id,:user_id,
CAST(:external_id AS varchar(128)),
CAST(:full_name_hash AS varchar(64)),
CAST(:email_hash AS varchar(64)),
CAST(:citizenship_hash AS varchar(64)),
CAST(:source_updated_at AS timestamptz),
CAST(:source AS varchar(24)),
CASE WHEN CAST(:source AS varchar(24))='webhook'
THEN now() END,
now(),now()
FROM bitrix_sync.entity_external_mapping m
WHERE m.entity_id=:user_id AND m.external_id=:external_id AND m.status='active'
WHERE m.entity_id=:user_id
AND m.external_id=CAST(:external_id AS varchar(128))
AND m.status='active'
ON CONFLICT (mapping_id) DO UPDATE
SET full_name_hash=excluded.full_name_hash,email_hash=excluded.email_hash,
citizenship_hash=excluded.citizenship_hash,
@@ -385,9 +413,9 @@ class Repository:
{
"user_id": user_id,
"external_id": external_id,
"full_name": full_name,
"citizenship": citizenship,
"email": email,
"full_name_hash": safe_hash(full_name or ""),
"citizenship_hash": safe_hash(citizenship or ""),
"email_hash": safe_hash(email or ""),
"source_updated_at": source_updated_at,
"source": source,
},