szolgáltatók beálltásai, szerkesztése , létrehozása
This commit is contained in:
650
backend/app/services/provider_service.py
Normal file
650
backend/app/services/provider_service.py
Normal file
@@ -0,0 +1,650 @@
|
||||
"""
|
||||
Provider Service – Szolgáltató keresés, gyors felvétel és szerkesztés logikája.
|
||||
|
||||
Gamification Integration (2026-06-16):
|
||||
=======================================
|
||||
A Vezető Tervező utasítása szerint a crowdsourcing funkciókba
|
||||
dinamikus Gamification horgok kerültek beépítésre.
|
||||
|
||||
ALAPELV: A pontértékek SOHA nincsenek beégetve (hardcoded) a kódba!
|
||||
Minden pontszámot a gamification.point_rules táblából olvasunk ki
|
||||
action_key alapján futásidőben.
|
||||
|
||||
Jelenlegi horgok:
|
||||
- ADD_NEW_PROVIDER: 500 pont (adatbázisból) + providers_added_count növelés
|
||||
|
||||
P1 CRITICAL ALIGN (2026-06-17): Atomizált címmezők.
|
||||
=====================================================
|
||||
- quick_add_provider: data.street -> data.address_street_name, address_street_type, address_house_number
|
||||
- update_provider: data.street -> data.address_street_name, address_street_type, address_house_number
|
||||
- search_providers: visszaadja az address_street_name, address_street_type, address_house_number mezőket
|
||||
- A kapcsolatfelvételi adatok (contact_phone, contact_email, website, tags) a ServiceProfile-ba kerülnek.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import uuid
|
||||
import hashlib
|
||||
from datetime import datetime, timezone
|
||||
from typing import Optional, List, Tuple
|
||||
|
||||
from sqlalchemy import select, func, or_, literal, union_all, cast, String, Text, null, and_, case
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import joinedload
|
||||
|
||||
from app.models.marketplace.organization import Organization, OrgType, Branch, OrganizationMember, OrgUserRole
|
||||
from app.models.marketplace.service import ServiceProfile, ExpertiseTag, ServiceExpertise, ServiceStaging
|
||||
from app.models.identity.social import ServiceProvider
|
||||
from app.models.gamification.gamification import PointRule, UserStats
|
||||
from app.schemas.provider import (
|
||||
ProviderSearchResult,
|
||||
ProviderSearchResponse,
|
||||
ProviderQuickAddIn,
|
||||
ProviderQuickAddResponse,
|
||||
ProviderUpdateIn,
|
||||
ProviderUpdateResponse,
|
||||
)
|
||||
from app.services.gamification_service import gamification_service
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def _award_provider_points(
|
||||
db: AsyncSession,
|
||||
user_id: int,
|
||||
action_key: str,
|
||||
) -> int:
|
||||
"""
|
||||
Dinamikus pontjóváírás a gamification.point_rules tábla alapján.
|
||||
|
||||
Ez a függvény az adatbázisból olvassa ki a pontértéket az action_key
|
||||
alapján, így a pontok NINCSENEK beégetve a kódba. Az admin felületről
|
||||
bármikor módosíthatók a point_rules táblában.
|
||||
|
||||
Args:
|
||||
db: AsyncSession
|
||||
user_id: A felhasználó ID-ja
|
||||
action_key: A pontszabály azonosítója (pl. 'ADD_NEW_PROVIDER')
|
||||
|
||||
Returns:
|
||||
int: A jóváírt pontok száma (0 ha a szabály nem található vagy inaktív)
|
||||
"""
|
||||
# 1. Pontszabály lekérdezése az adatbázisból (dinamikus!)
|
||||
rule_stmt = select(PointRule).where(
|
||||
PointRule.action_key == action_key,
|
||||
PointRule.is_active == True,
|
||||
)
|
||||
rule_result = await db.execute(rule_stmt)
|
||||
rule = rule_result.scalar_one_or_none()
|
||||
|
||||
if not rule:
|
||||
logger.warning(
|
||||
f"Pontszabály '{action_key}' nem található vagy inaktív. "
|
||||
f"Pontjóváírás kihagyva user_id={user_id}."
|
||||
)
|
||||
return 0
|
||||
|
||||
points_to_award = rule.points
|
||||
logger.info(
|
||||
f"Dinamikus pont lekérés: action_key='{action_key}', "
|
||||
f"points={points_to_award} (forrás: gamification.point_rules tábla)"
|
||||
)
|
||||
|
||||
# 2. Pontok jóváírása a Gamification Service-en keresztül
|
||||
# A GamificationService.award_points() kezeli a szorzókat, szintlépést,
|
||||
# büntetés ledolgozást és a naplózást (PointsLedger).
|
||||
await gamification_service.award_points(
|
||||
db=db,
|
||||
user_id=user_id,
|
||||
amount=points_to_award,
|
||||
reason=f"ADD_NEW_PROVIDER: +{points_to_award} pont új szolgáltató rögzítéséért",
|
||||
commit=False, # A hívó (quick_add_provider) kezeli a commit-ot
|
||||
)
|
||||
|
||||
# 3. Statisztika növelése (providers_added_count)
|
||||
stats_stmt = select(UserStats).where(UserStats.user_id == user_id)
|
||||
stats = (await db.execute(stats_stmt)).scalar_one_or_none()
|
||||
|
||||
if stats:
|
||||
stats.providers_added_count = UserStats.providers_added_count + 1
|
||||
logger.info(
|
||||
f"Statisztika frissítve: user_id={user_id}, "
|
||||
f"providers_added_count={stats.providers_added_count}"
|
||||
)
|
||||
else:
|
||||
# Ha még nincs UserStats rekordja, létrehozzuk
|
||||
stats = UserStats(
|
||||
user_id=user_id,
|
||||
total_xp=points_to_award,
|
||||
current_level=1,
|
||||
providers_added_count=1,
|
||||
)
|
||||
db.add(stats)
|
||||
logger.info(
|
||||
f"Új UserStats rekord létrehozva: user_id={user_id}, "
|
||||
f"providers_added_count=1"
|
||||
)
|
||||
|
||||
return points_to_award
|
||||
|
||||
|
||||
async def search_providers(
|
||||
db: AsyncSession,
|
||||
q: Optional[str] = None,
|
||||
category: Optional[str] = None,
|
||||
city: Optional[str] = None,
|
||||
limit: int = 20,
|
||||
offset: int = 0,
|
||||
) -> ProviderSearchResponse:
|
||||
"""
|
||||
Egyesített szolgáltató keresés három forrásból:
|
||||
1. fleet.organizations (verified orgs with org_type='service_provider')
|
||||
2. marketplace.service_staging (robot által gyűjtött adatok)
|
||||
3. marketplace.service_providers (crowdsourced adatok)
|
||||
|
||||
P1 CRITICAL ALIGN (2026-06-17): Az org lekérdezés most már visszaadja
|
||||
az address_street_name, address_street_type, address_house_number mezőket is.
|
||||
|
||||
Args:
|
||||
db: AsyncSession
|
||||
q: Keresőszó (tokenizálva, AND kapcsolat) — opcionális
|
||||
category: Kategória szűrő
|
||||
city: Város szűrő — AND kapcsolat a q-val
|
||||
limit: Lapozási limit
|
||||
offset: Lapozási offset
|
||||
|
||||
Returns:
|
||||
ProviderSearchResponse lapozott találatokkal
|
||||
"""
|
||||
results = []
|
||||
total = 0
|
||||
|
||||
# Tokenizáljuk a q paramétert: szóközök mentén feldaraboljuk
|
||||
q_tokens: list[str] = []
|
||||
if q:
|
||||
q_tokens = [token.strip() for token in q.split() if token.strip()]
|
||||
|
||||
# --- 1. LEKÉRDEZÉS: Szervezetek (fleet.organizations) ---
|
||||
org_conditions = [
|
||||
Organization.org_type == OrgType.service_provider,
|
||||
Organization.is_deleted == False,
|
||||
]
|
||||
if q_tokens:
|
||||
token_conditions = []
|
||||
for token in q_tokens:
|
||||
token_conditions.append(
|
||||
or_(
|
||||
Organization.name.ilike(f"%{token}%"),
|
||||
cast(Organization.aliases, Text).ilike(f"%{token}%"),
|
||||
cast(Organization.tags, Text).ilike(f"%{token}%"),
|
||||
)
|
||||
)
|
||||
org_conditions.append(and_(*token_conditions))
|
||||
if city:
|
||||
org_conditions.append(
|
||||
or_(
|
||||
Organization.address_city.ilike(f"%{city}%"),
|
||||
Organization.address_zip.ilike(f"%{city}%"),
|
||||
)
|
||||
)
|
||||
|
||||
# P1 CRITICAL ALIGN: Az org lekérdezés most már tartalmazza az atomizált
|
||||
# címmezőket (address_street_name, address_street_type, address_house_number).
|
||||
# Az 'address' mezőt SQL összefűzéssé alakítjuk, hogy a frontend kártyán
|
||||
# ne duplikálódjon a városnév (pl. "Dunakeszi, Dunakeszi").
|
||||
org_stmt = select(
|
||||
Organization.id.label("id"),
|
||||
Organization.name.label("name"),
|
||||
Organization.address_city.label("city"),
|
||||
func.concat(
|
||||
Organization.address_zip,
|
||||
literal(' '),
|
||||
Organization.address_city,
|
||||
literal(', '),
|
||||
Organization.address_street_name,
|
||||
literal(' '),
|
||||
Organization.address_street_type,
|
||||
literal(' '),
|
||||
Organization.address_house_number,
|
||||
).label("address"),
|
||||
Organization.address_zip.label("address_zip"),
|
||||
Organization.address_street_name.label("address_street_name"),
|
||||
Organization.address_street_type.label("address_street_type"),
|
||||
Organization.address_house_number.label("address_house_number"),
|
||||
Organization.is_verified.label("is_verified"),
|
||||
ServiceProfile.contact_phone.label("contact_phone"),
|
||||
ServiceProfile.contact_email.label("contact_email"),
|
||||
ServiceProfile.website.label("website"),
|
||||
ServiceProfile.specialization_tags.label("specialization_tags"),
|
||||
case(
|
||||
(cast(Organization.external_integration_config["source"], String) == "crowdsourced", literal("crowd_added")),
|
||||
else_=literal("verified_org"),
|
||||
).label("source"),
|
||||
).outerjoin(
|
||||
ServiceProfile,
|
||||
ServiceProfile.organization_id == Organization.id,
|
||||
).where(*org_conditions)
|
||||
|
||||
# --- 2. LEKÉRDEZÉS: Robot által gyűjtött adatok (marketplace.service_staging) ---
|
||||
staging_conditions = []
|
||||
if q_tokens:
|
||||
token_conditions = []
|
||||
for token in q_tokens:
|
||||
token_conditions.append(
|
||||
ServiceStaging.name.ilike(f"%{token}%")
|
||||
)
|
||||
staging_conditions.append(and_(*token_conditions))
|
||||
if city:
|
||||
staging_conditions.append(
|
||||
or_(
|
||||
ServiceStaging.city.ilike(f"%{city}%"),
|
||||
ServiceStaging.postal_code.ilike(f"%{city}%"),
|
||||
)
|
||||
)
|
||||
|
||||
staging_stmt = select(
|
||||
ServiceStaging.id.label("id"),
|
||||
ServiceStaging.name.label("name"),
|
||||
ServiceStaging.city.label("city"),
|
||||
ServiceStaging.full_address.label("address"),
|
||||
literal(None).label("address_zip"),
|
||||
literal(None).label("address_street_name"),
|
||||
literal(None).label("address_street_type"),
|
||||
literal(None).label("address_house_number"),
|
||||
literal(False).label("is_verified"),
|
||||
literal(None).label("contact_phone"),
|
||||
literal(None).label("contact_email"),
|
||||
literal(None).label("website"),
|
||||
literal(None).label("specialization_tags"),
|
||||
literal("staged_data").label("source"),
|
||||
)
|
||||
if staging_conditions:
|
||||
staging_stmt = staging_stmt.where(*staging_conditions)
|
||||
|
||||
# --- 3. LEKÉRDEZÉS: Crowdsourced adatok (marketplace.service_providers) ---
|
||||
crowd_conditions = []
|
||||
if q_tokens:
|
||||
token_conditions = []
|
||||
for token in q_tokens:
|
||||
token_conditions.append(
|
||||
ServiceProvider.name.ilike(f"%{token}%")
|
||||
)
|
||||
crowd_conditions.append(and_(*token_conditions))
|
||||
if city:
|
||||
crowd_conditions.append(
|
||||
ServiceProvider.address.ilike(f"%{city}%")
|
||||
)
|
||||
|
||||
crowd_stmt = select(
|
||||
ServiceProvider.id.label("id"),
|
||||
ServiceProvider.name.label("name"),
|
||||
literal(None).label("city"),
|
||||
ServiceProvider.address.label("address"),
|
||||
literal(None).label("address_zip"),
|
||||
literal(None).label("address_street_name"),
|
||||
literal(None).label("address_street_type"),
|
||||
literal(None).label("address_house_number"),
|
||||
literal(False).label("is_verified"),
|
||||
literal(None).label("contact_phone"),
|
||||
literal(None).label("contact_email"),
|
||||
literal(None).label("website"),
|
||||
literal(None).label("specialization_tags"),
|
||||
literal("crowd_added").label("source"),
|
||||
)
|
||||
if crowd_conditions:
|
||||
crowd_stmt = crowd_stmt.where(*crowd_conditions)
|
||||
|
||||
# --- UNION ---
|
||||
union_parts = [org_stmt]
|
||||
union_parts.append(staging_stmt)
|
||||
union_parts.append(crowd_stmt)
|
||||
|
||||
# UNION végrehajtása
|
||||
if len(union_parts) == 1:
|
||||
base_query = union_parts[0]
|
||||
else:
|
||||
base_query = union_all(*union_parts)
|
||||
|
||||
# Teljes darabszám
|
||||
count_subquery = base_query.subquery()
|
||||
count_stmt = select(func.count()).select_from(count_subquery)
|
||||
total_result = await db.execute(count_stmt)
|
||||
total = total_result.scalar() or 0
|
||||
|
||||
# Lapozott lekérdezés
|
||||
paginated_query = base_query.order_by("source").offset(offset).limit(limit)
|
||||
result = await db.execute(paginated_query)
|
||||
rows = result.fetchall()
|
||||
|
||||
for row in rows:
|
||||
tags_list: list = []
|
||||
spec_tags = getattr(row, "specialization_tags", None)
|
||||
if spec_tags and isinstance(spec_tags, dict):
|
||||
user_tags = spec_tags.get("user_tags", [])
|
||||
if user_tags:
|
||||
tags_list = list(user_tags)
|
||||
|
||||
results.append(
|
||||
ProviderSearchResult(
|
||||
id=row.id,
|
||||
name=row.name,
|
||||
city=row.city,
|
||||
address=row.address,
|
||||
address_zip=getattr(row, "address_zip", None),
|
||||
address_street_name=getattr(row, "address_street_name", None),
|
||||
address_street_type=getattr(row, "address_street_type", None),
|
||||
address_house_number=getattr(row, "address_house_number", None),
|
||||
contact_phone=getattr(row, "contact_phone", None),
|
||||
contact_email=getattr(row, "contact_email", None),
|
||||
website=getattr(row, "website", None),
|
||||
tags=tags_list,
|
||||
source=row.source,
|
||||
is_verified=row.is_verified,
|
||||
)
|
||||
)
|
||||
|
||||
page = (offset // limit) + 1 if limit > 0 else 1
|
||||
|
||||
return ProviderSearchResponse(
|
||||
results=results,
|
||||
total=total,
|
||||
page=page,
|
||||
per_page=limit,
|
||||
)
|
||||
|
||||
|
||||
async def quick_add_provider(
|
||||
db: AsyncSession,
|
||||
data: ProviderQuickAddIn,
|
||||
user_id: int,
|
||||
) -> ProviderQuickAddResponse:
|
||||
"""
|
||||
Gyors szolgáltató felvétel crowdsourcingból.
|
||||
|
||||
P1 CRITICAL ALIGN (2026-06-17): Atomizált címmezők.
|
||||
- data.street -> data.address_street_name, data.address_street_type, data.address_house_number
|
||||
- A kapcsolatfelvételi adatok a ServiceProfile-ba kerülnek.
|
||||
|
||||
Folyamat:
|
||||
1. Kategória ellenőrzése (expertise_tags)
|
||||
2. Organization létrehozása (is_verified=False, org_type='service_provider')
|
||||
3. ServiceProfile létrehozása
|
||||
4. ServiceExpertise kapcsolat létrehozása
|
||||
5. Branch létrehozása a címmel
|
||||
6. OrganizationMember létrehozása a felhasználóhoz
|
||||
|
||||
Args:
|
||||
db: AsyncSession
|
||||
data: ProviderQuickAddIn
|
||||
user_id: A felhasználó ID-ja
|
||||
|
||||
Returns:
|
||||
ProviderQuickAddResponse
|
||||
|
||||
Raises:
|
||||
ValueError: Ha a kategória nem létezik
|
||||
"""
|
||||
# 1. Kategória ellenőrzése
|
||||
category = await db.get(ExpertiseTag, data.category_id)
|
||||
if not category:
|
||||
raise ValueError(f"Category with id={data.category_id} not found")
|
||||
|
||||
# 2. Organization létrehozása
|
||||
folder_slug = hashlib.md5(
|
||||
f"{data.name}-{uuid.uuid4()}".encode()
|
||||
).hexdigest()[:12]
|
||||
|
||||
# P1 CRITICAL ALIGN: address_street_name, address_street_type, address_house_number
|
||||
# használata a régi street helyett.
|
||||
org = Organization(
|
||||
name=data.name[:100],
|
||||
full_name=data.name,
|
||||
display_name=data.name[:50],
|
||||
folder_slug=folder_slug,
|
||||
org_type=OrgType.service_provider,
|
||||
is_verified=False,
|
||||
status="pending_verification",
|
||||
subscription_plan="FREE",
|
||||
base_asset_limit=1,
|
||||
purchased_extra_slots=0,
|
||||
notification_settings={"notify_owner": True, "alert_days_before": [30, 15, 7, 1]},
|
||||
external_integration_config={"source": "crowdsourced"},
|
||||
is_ownership_transferable=True,
|
||||
address_city=data.city,
|
||||
address_zip=data.address_zip,
|
||||
address_street_name=data.address_street_name,
|
||||
address_street_type=data.address_street_type,
|
||||
address_house_number=data.address_house_number,
|
||||
owner_id=None,
|
||||
first_registered_at=datetime.now(timezone.utc),
|
||||
current_lifecycle_started_at=datetime.now(timezone.utc),
|
||||
created_at=datetime.now(timezone.utc),
|
||||
)
|
||||
db.add(org)
|
||||
await db.flush()
|
||||
|
||||
# 3. ServiceProfile létrehozása
|
||||
fingerprint = hashlib.sha256(
|
||||
f"quick-add-{org.id}-{datetime.now().isoformat()}".encode()
|
||||
).hexdigest()[:64]
|
||||
|
||||
# specialization_tags összeállítása: primary_category + user-supplied tags
|
||||
spec_tags = {"primary_category": category.key}
|
||||
if data.tags:
|
||||
spec_tags["user_tags"] = data.tags
|
||||
|
||||
profile = ServiceProfile(
|
||||
organization_id=org.id,
|
||||
fingerprint=fingerprint,
|
||||
status="ghost",
|
||||
location=func.ST_SetSRID(func.ST_MakePoint(19.040236, 47.497913), 4326),
|
||||
specialization_tags=spec_tags,
|
||||
contact_phone=data.contact_phone,
|
||||
contact_email=data.contact_email,
|
||||
website=data.website,
|
||||
)
|
||||
db.add(profile)
|
||||
await db.flush()
|
||||
|
||||
# 4. ServiceExpertise kapcsolat
|
||||
expertise = ServiceExpertise(
|
||||
service_id=profile.id,
|
||||
expertise_id=data.category_id,
|
||||
confidence_level=50, # Közepes bizonyosság (crowdsourced)
|
||||
)
|
||||
db.add(expertise)
|
||||
|
||||
# 5. Branch létrehozása atomizált címmezőkkel
|
||||
branch = Branch(
|
||||
organization_id=org.id,
|
||||
name=data.name[:100],
|
||||
is_main=True,
|
||||
city=data.city,
|
||||
postal_code=data.address_zip,
|
||||
street_name=data.address_street_name,
|
||||
street_type=data.address_street_type,
|
||||
house_number=data.address_house_number,
|
||||
status="active",
|
||||
)
|
||||
db.add(branch)
|
||||
|
||||
# =====================================================================
|
||||
# 7. 🎮 GAMIFICATION INTEGRÁCIÓ (Dinamikus pontozás)
|
||||
# =====================================================================
|
||||
earned_points = await _award_provider_points(
|
||||
db=db,
|
||||
user_id=user_id,
|
||||
action_key="ADD_NEW_PROVIDER",
|
||||
)
|
||||
# =====================================================================
|
||||
|
||||
await db.commit()
|
||||
await db.refresh(org)
|
||||
|
||||
logger.info(
|
||||
f"Quick-add provider created: org_id={org.id}, name={data.name}, "
|
||||
f"user_id={user_id}, earned_points={earned_points}"
|
||||
)
|
||||
|
||||
return ProviderQuickAddResponse(
|
||||
id=org.id,
|
||||
name=data.name,
|
||||
status="pending_verification",
|
||||
message="Szolgáltató sikeresen rögzítve. Ellenőrzés alatt.",
|
||||
earned_points=earned_points,
|
||||
)
|
||||
|
||||
|
||||
async def update_provider(
|
||||
db: AsyncSession,
|
||||
provider_id: int,
|
||||
data: ProviderUpdateIn,
|
||||
user_id: int,
|
||||
) -> ProviderUpdateResponse:
|
||||
"""
|
||||
Szolgáltató adatainak szerkesztése.
|
||||
|
||||
P1 CRITICAL ALIGN (2026-06-17): Atomizált címmezők.
|
||||
- data.street -> data.address_street_name, data.address_street_type, data.address_house_number
|
||||
- A kapcsolatfelvételi adatok a ServiceProfile-ba kerülnek.
|
||||
|
||||
Multi-source update (2026-06-17): Támogatja mindhárom forrást:
|
||||
1. fleet.organizations (verified orgs) - közvetlen frissítés
|
||||
2. marketplace.service_staging (robot adatok) - migrálás Organization-be
|
||||
3. marketplace.service_providers (crowdsourced) - migrálás Organization-be
|
||||
|
||||
Frissíti az Organization és a ServiceProfile rekordokat a megadott
|
||||
adatokkal. A forrást (source) a backend állítja be, a frontend SOHA
|
||||
nem küldheti azt!
|
||||
|
||||
Args:
|
||||
db: AsyncSession
|
||||
provider_id: A szolgáltató ID-ja
|
||||
data: ProviderUpdateIn séma
|
||||
user_id: A szerkesztő felhasználó ID-ja
|
||||
|
||||
Returns:
|
||||
ProviderUpdateResponse
|
||||
|
||||
Raises:
|
||||
ValueError: Ha a szolgáltató nem található
|
||||
"""
|
||||
# 1. Organization lekérdezése
|
||||
org = await db.get(Organization, provider_id)
|
||||
|
||||
if not org:
|
||||
# 1b. Ha nincs Organization-ben, ServiceStaging-ben keresünk
|
||||
staging = await db.get(ServiceStaging, provider_id)
|
||||
if staging:
|
||||
# Migráljuk Organization-be
|
||||
org = Organization(
|
||||
id=staging.id,
|
||||
name=staging.name[:100],
|
||||
full_name=staging.name,
|
||||
display_name=staging.name[:50],
|
||||
org_type=OrgType.service_provider,
|
||||
address_city=staging.city,
|
||||
address_zip=data.address_zip,
|
||||
address_street_name=data.address_street_name,
|
||||
address_street_type=data.address_street_type,
|
||||
address_house_number=data.address_house_number,
|
||||
status="pending_verification",
|
||||
is_verified=False,
|
||||
folder_slug=f"sp-{staging.id}-{uuid.uuid4().hex[:6]}",
|
||||
)
|
||||
db.add(org)
|
||||
await db.flush()
|
||||
logger.info(
|
||||
f"Provider migrated from ServiceStaging: staging_id={provider_id}, "
|
||||
f"new org_id={org.id}, user_id={user_id}"
|
||||
)
|
||||
else:
|
||||
# 1c. Ha nincs staging-ben sem, ServiceProvider-ben keresünk
|
||||
crowd = await db.get(ServiceProvider, provider_id)
|
||||
if crowd:
|
||||
# Migráljuk Organization-be
|
||||
org = Organization(
|
||||
id=crowd.id,
|
||||
name=crowd.name[:100],
|
||||
full_name=crowd.name,
|
||||
display_name=crowd.name[:50],
|
||||
org_type=OrgType.service_provider,
|
||||
address_city=data.city,
|
||||
address_zip=data.address_zip,
|
||||
address_street_name=data.address_street_name,
|
||||
address_street_type=data.address_street_type,
|
||||
address_house_number=data.address_house_number,
|
||||
status="pending_verification",
|
||||
is_verified=False,
|
||||
folder_slug=f"cr-{crowd.id}-{uuid.uuid4().hex[:6]}",
|
||||
)
|
||||
db.add(org)
|
||||
await db.flush()
|
||||
logger.info(
|
||||
f"Provider migrated from ServiceProvider: crowd_id={provider_id}, "
|
||||
f"new org_id={org.id}, user_id={user_id}"
|
||||
)
|
||||
else:
|
||||
# 1d. Ha egyikben sem található
|
||||
raise ValueError(f"Provider with id={provider_id} not found")
|
||||
|
||||
# 2. Organization mezők frissítése atomizált címmezőkkel
|
||||
# ADATVÉDELMI SZABÁLY (2026-06-17): Csak akkor írjuk felül a mezőt,
|
||||
# ha a frontend explicit értéket küldött (nem None). Ez megakadályozza,
|
||||
# hogy a meglévő címadatok véletlenül null-ra állítódjanak, amikor
|
||||
# a felhasználó csak más mezőket szerkeszt.
|
||||
org.name = data.name[:100]
|
||||
org.full_name = data.name
|
||||
org.display_name = data.name[:50]
|
||||
if data.city is not None:
|
||||
org.address_city = data.city
|
||||
if data.address_zip is not None:
|
||||
org.address_zip = data.address_zip
|
||||
if data.address_street_name is not None:
|
||||
org.address_street_name = data.address_street_name
|
||||
if data.address_street_type is not None:
|
||||
org.address_street_type = data.address_street_type
|
||||
if data.address_house_number is not None:
|
||||
org.address_house_number = data.address_house_number
|
||||
|
||||
# 3. ServiceProfile lekérdezése (ha létezik)
|
||||
profile_stmt = select(ServiceProfile).where(
|
||||
ServiceProfile.organization_id == org.id
|
||||
)
|
||||
profile_result = await db.execute(profile_stmt)
|
||||
profile = profile_result.scalar_one_or_none()
|
||||
|
||||
if profile:
|
||||
# 4. ServiceProfile mezők frissítése
|
||||
profile.contact_phone = data.contact_phone
|
||||
profile.contact_email = data.contact_email
|
||||
profile.website = data.website
|
||||
|
||||
# specialization_tags frissítése
|
||||
if data.tags is not None:
|
||||
spec_tags = profile.specialization_tags or {}
|
||||
spec_tags["user_tags"] = data.tags
|
||||
profile.specialization_tags = spec_tags
|
||||
|
||||
logger.info(
|
||||
f"Provider update: org_id={org.id}, profile_id={profile.id}, "
|
||||
f"contact_phone={data.contact_phone}, contact_email={data.contact_email}, "
|
||||
f"website={data.website}, tags={data.tags}"
|
||||
)
|
||||
else:
|
||||
logger.warning(
|
||||
f"Provider update: org_id={org.id} has no ServiceProfile. "
|
||||
f"Only Organization fields updated."
|
||||
)
|
||||
|
||||
await db.commit()
|
||||
|
||||
logger.info(
|
||||
f"Provider updated successfully: org_id={org.id}, "
|
||||
f"name={data.name}, user_id={user_id}"
|
||||
)
|
||||
|
||||
return ProviderUpdateResponse(
|
||||
id=org.id,
|
||||
name=data.name,
|
||||
message="Szolgáltató adatai sikeresen frissítve.",
|
||||
)
|
||||
Reference in New Issue
Block a user