Косметические правки по фронту, раскатка приложения в продакшн версии, исправление бага с созданием контакта

This commit is contained in:
mi
2026-09-03 11:34:25 +03:00
parent f989097484
commit 465a70d488
18 changed files with 528 additions and 139 deletions
@@ -117,6 +117,67 @@ async def ensure_delivery_outbox(
return outbox
OPENLINES_FALLBACK_DISPLAY_NAME = "Новый клиент HAN"
def openlines_display_name(full_name: str | None) -> str:
name = (full_name or "").strip()
return name or OPENLINES_FALLBACK_DISPLAY_NAME
def openlines_user_payload(user: UserIdentity, profile: ClientProfile | None) -> dict[str, str]:
return {
"id": str(user.id),
"display_name": openlines_display_name(profile.full_name if profile else None),
"phone": user.phone_number,
}
async def load_active_client_profile(
session: AsyncSession, user_id: uuid.UUID
) -> ClientProfile | None:
return (
await session.execute(
select(ClientProfile).where(
ClientProfile.user_id == user_id, ClientProfile.record_status == "A"
)
)
).scalar_one_or_none()
def openlines_delivery_payload(
*,
message: Message,
dialog_id: uuid.UUID,
user: UserIdentity,
profile: ClientProfile | None,
attachment: MessageAttachment | None,
) -> dict[str, Any]:
files: list[dict[str, Any]] = []
if attachment:
files.append(
{
"attachment_id": str(attachment.id),
"name": attachment.safe_file_name,
"mime_type": attachment.mime_type,
"size_bytes": attachment.size_bytes,
"_storage_bucket": attachment.storage_bucket,
"_object_key": attachment.object_key,
}
)
return {
"message_id": str(message.id),
"external_chat_id": str(dialog_id),
"occurred_at": message.occurred_at.isoformat(),
"user": openlines_user_payload(user, profile),
"message": {
"content_kind": message.content_kind,
"text": message.text,
"files": files,
},
}
class DomainError(Exception):
def __init__(self, code: str, status: int, message: str, details: dict[str, Any] | None = None):
self.code, self.status, self.message = code, status, message
@@ -1032,34 +1093,18 @@ async def send_message(
"bypassed" if verdict["processing_mode"] == "mock" else "clean"
)
message.safety_status = "allowed"
profile = await load_active_client_profile(session, user.id)
outbox = await ensure_delivery_outbox(
session,
message_id=message.id,
external_chat_id=dialog_id,
payload_json={
"message_id": str(message.id),
"external_chat_id": str(dialog_id),
"occurred_at": message.occurred_at.isoformat(),
"user": {"id": str(user.id), "display_name": user.phone_number},
"message": {
"content_kind": message.content_kind,
"text": message.text,
"files": (
[
{
"attachment_id": str(attachment.id),
"name": attachment.safe_file_name,
"mime_type": attachment.mime_type,
"size_bytes": attachment.size_bytes,
"_storage_bucket": attachment.storage_bucket,
"_object_key": attachment.object_key,
}
]
if attachment
else []
),
},
},
payload_json=openlines_delivery_payload(
message=message,
dialog_id=dialog_id,
user=user,
profile=profile,
attachment=attachment,
),
# Keep the row recoverable after a process crash, but do not let the
# delivery worker race the synchronous first attempt.
next_attempt_at=datetime.now(UTC)
@@ -30,7 +30,9 @@ from app.notification_service import expire_notifications
from app.realtime import RealtimeFanout
from app.services import (
ensure_delivery_outbox,
load_active_client_profile,
load_settings,
openlines_delivery_payload,
publish_dialog_status,
publish_message_status,
)
@@ -189,37 +191,18 @@ async def safety_once(
else None
)
if dialog and user:
profile = await load_active_client_profile(session, user.id)
await ensure_delivery_outbox(
session,
message_id=message.id,
external_chat_id=dialog.id,
payload_json={
"message_id": str(message.id),
"external_chat_id": str(dialog.id),
"occurred_at": message.occurred_at.isoformat(),
"user": {
"id": str(user.id),
"display_name": user.phone_number,
},
"message": {
"content_kind": message.content_kind,
"text": message.text,
"files": (
[
{
"attachment_id": str(attachment.id),
"name": attachment.safe_file_name,
"mime_type": attachment.mime_type,
"size_bytes": attachment.size_bytes,
"_storage_bucket": attachment.storage_bucket,
"_object_key": attachment.object_key,
}
]
if attachment
else []
),
},
},
payload_json=openlines_delivery_payload(
message=message,
dialog_id=dialog.id,
user=user,
profile=profile,
attachment=attachment,
),
next_attempt_at=datetime.now(UTC),
)
elif verdict["_status"] == 403 and message:
@@ -304,7 +304,11 @@ async def test_openlines_payload_gets_fresh_download_url_without_storage_fields(
"message_id": str(uuid.uuid4()),
"external_chat_id": str(uuid.uuid4()),
"occurred_at": "2026-07-10T12:00:00+00:00",
"user": {"id": str(uuid.uuid4()), "display_name": "+79990000000"},
"user": {
"id": str(uuid.uuid4()),
"display_name": "Новый клиент HAN",
"phone": "+79990000000",
},
"message": {
"content_kind": "file",
"text": "",
@@ -20,6 +20,7 @@ from app.schemas import (
from app.services import (
MESSAGE_SAFETY_REPLIES,
ensure_delivery_outbox,
openlines_user_payload,
safety_reply_message,
safety_task_recovery_at,
)
@@ -85,6 +86,20 @@ def test_safety_recovery_starts_after_synchronous_polling_window() -> None:
assert (safety_task_recovery_at(now, settings) - now).total_seconds() == 307
def test_openlines_user_payload_uses_profile_name_and_phone() -> None:
user = SimpleNamespace(id=uuid.uuid4(), phone_number="+79991234567")
named = openlines_user_payload(user, SimpleNamespace(full_name="Иван Иванов"))
assert named == {
"id": str(user.id),
"display_name": "Иван Иванов",
"phone": "+79991234567",
}
assert openlines_user_payload(user, SimpleNamespace(full_name=" "))["display_name"] == (
"Новый клиент HAN"
)
assert openlines_user_payload(user, None)["display_name"] == "Новый клиент HAN"
async def test_delivery_outbox_returns_concurrent_insert_winner() -> None:
existing = object()
session = SimpleNamespace(