Notify moderators about service center applications
This commit is contained in:
@@ -1,12 +1,10 @@
|
|||||||
from datetime import UTC, datetime
|
from datetime import UTC, datetime
|
||||||
|
|
||||||
import httpx
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException
|
from fastapi import APIRouter, Depends, HTTPException
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
from app.api.deps import get_current_telegram_user, log_audit, require_platform_role
|
from app.api.deps import get_current_telegram_user, log_audit, require_platform_role
|
||||||
from app.core.config import settings
|
|
||||||
from app.db.session import get_session
|
from app.db.session import get_session
|
||||||
from app.models.car import (
|
from app.models.car import (
|
||||||
AuditLog,
|
AuditLog,
|
||||||
@@ -17,6 +15,7 @@ from app.models.car import (
|
|||||||
)
|
)
|
||||||
from app.models.user import User
|
from app.models.user import User
|
||||||
from app.schemas.service_center import AdminModerationDecision, ServiceCenterRead, ServiceVisitRead
|
from app.schemas.service_center import AdminModerationDecision, ServiceCenterRead, ServiceVisitRead
|
||||||
|
from app.services.notifications import notify_user
|
||||||
|
|
||||||
router = APIRouter(prefix="/admin", tags=["admin"])
|
router = APIRouter(prefix="/admin", tags=["admin"])
|
||||||
|
|
||||||
@@ -261,16 +260,3 @@ async def ensure_owner_employee(session: AsyncSession, service_center_id: int, o
|
|||||||
else:
|
else:
|
||||||
employee.role = "owner"
|
employee.role = "owner"
|
||||||
employee.status = "active"
|
employee.status = "active"
|
||||||
|
|
||||||
|
|
||||||
async def notify_user(user: User, text: str) -> None:
|
|
||||||
if not settings.bot_token:
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
async with httpx.AsyncClient(timeout=5) as client:
|
|
||||||
await client.post(
|
|
||||||
f"https://api.telegram.org/bot{settings.bot_token}/sendMessage",
|
|
||||||
data={"chat_id": str(user.telegram_id), "text": text},
|
|
||||||
)
|
|
||||||
except Exception:
|
|
||||||
return
|
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ from app.schemas.service_center import (
|
|||||||
VehicleSearchRequest,
|
VehicleSearchRequest,
|
||||||
VehicleSearchResult,
|
VehicleSearchResult,
|
||||||
)
|
)
|
||||||
|
from app.services.notifications import notify_platform_moderators
|
||||||
from app.services.odometer import validate_odometer_change
|
from app.services.odometer import validate_odometer_change
|
||||||
from app.services.vehicle_identity import mask_license_plate, mask_vin
|
from app.services.vehicle_identity import mask_license_plate, mask_vin
|
||||||
|
|
||||||
@@ -101,9 +102,19 @@ async def create_service_center(
|
|||||||
status="active",
|
status="active",
|
||||||
)
|
)
|
||||||
session.add(employee)
|
session.add(employee)
|
||||||
await log_audit(session, actor=current_user, action="service_center.create", target_type="service_center", target_id=center.id)
|
await log_audit(
|
||||||
|
session,
|
||||||
|
actor=current_user,
|
||||||
|
action="service_center.create",
|
||||||
|
target_type="service_center",
|
||||||
|
target_id=center.id,
|
||||||
|
)
|
||||||
await session.commit()
|
await session.commit()
|
||||||
await session.refresh(center)
|
await session.refresh(center)
|
||||||
|
await notify_platform_moderators(
|
||||||
|
session,
|
||||||
|
f"Новая заявка СТО #{center.id}: {center.display_name or center.name}. Откройте /admin_sto_pending для модерации.",
|
||||||
|
)
|
||||||
return center
|
return center
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
27
app/services/notifications.py
Normal file
27
app/services/notifications.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import httpx
|
||||||
|
from sqlalchemy import select
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
|
from app.core.config import settings
|
||||||
|
from app.models.user import User
|
||||||
|
|
||||||
|
MODERATOR_ROLES = {"admin", "verifier", "moderator"}
|
||||||
|
|
||||||
|
|
||||||
|
async def notify_user(user: User, text: str) -> None:
|
||||||
|
if not settings.bot_token or settings.app_env == "test":
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
async with httpx.AsyncClient(timeout=5) as client:
|
||||||
|
await client.post(
|
||||||
|
f"https://api.telegram.org/bot{settings.bot_token}/sendMessage",
|
||||||
|
data={"chat_id": str(user.telegram_id), "text": text},
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
async def notify_platform_moderators(session: AsyncSession, text: str) -> None:
|
||||||
|
result = await session.execute(select(User).where(User.platform_role.in_(MODERATOR_ROLES)))
|
||||||
|
for user in result.scalars():
|
||||||
|
await notify_user(user, text)
|
||||||
@@ -14,11 +14,12 @@ async def test_license_plate_can_be_saved_and_edited(client, auth_headers) -> No
|
|||||||
updated = await client.patch(
|
updated = await client.patch(
|
||||||
f"/api/cars/{car['id']}",
|
f"/api/cars/{car['id']}",
|
||||||
headers=auth_headers,
|
headers=auth_headers,
|
||||||
json={"plate_number": "34 나 7890"},
|
json={"plate_number": "34 나 7890", "body_type": "SUV"},
|
||||||
)
|
)
|
||||||
|
|
||||||
assert updated.status_code == 200
|
assert updated.status_code == 200
|
||||||
assert updated.json()["plate_number"] == "34 나 7890"
|
assert updated.json()["plate_number"] == "34 나 7890"
|
||||||
|
assert updated.json()["body_type"] == "SUV"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@@ -312,3 +313,26 @@ async def test_admin_request_changes_keeps_application_visible_to_moderation(
|
|||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert response.json()["verification_status"] == "needs_changes"
|
assert response.json()["verification_status"] == "needs_changes"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_service_center_application_is_visible_to_moderators(
|
||||||
|
client, auth_headers, admin_auth_headers, internal_headers
|
||||||
|
) -> None:
|
||||||
|
center = (
|
||||||
|
await client.post(
|
||||||
|
"/api/service-centers",
|
||||||
|
headers=auth_headers,
|
||||||
|
json={"display_name": "Pending Review Service", "country": "KR"},
|
||||||
|
)
|
||||||
|
).json()
|
||||||
|
await client.post(
|
||||||
|
"/api/users",
|
||||||
|
headers=internal_headers,
|
||||||
|
json={"telegram_id": 9001, "platform_role": "moderator"},
|
||||||
|
)
|
||||||
|
|
||||||
|
pending = await client.get("/api/admin/service-centers/pending", headers=admin_auth_headers)
|
||||||
|
|
||||||
|
assert pending.status_code == 200
|
||||||
|
assert center["id"] in [item["id"] for item in pending.json()]
|
||||||
|
|||||||
Reference in New Issue
Block a user