368 lines
13 KiB
Python
368 lines
13 KiB
Python
import graphene
|
||
from django.utils import timezone
|
||
from datetime import timedelta
|
||
from graphene_django import DjangoObjectType
|
||
from django.contrib.auth import get_user_model
|
||
from ..models import Team as TeamModel, TeamMember as TeamMemberModel, TeamAddress as TeamAddressModel
|
||
from ..permissions import require_scopes
|
||
|
||
UserModel = get_user_model()
|
||
|
||
class User(DjangoObjectType):
|
||
id = graphene.String()
|
||
firstName = graphene.String()
|
||
lastName = graphene.String()
|
||
phone = graphene.String()
|
||
avatarId = graphene.String()
|
||
createdAt = graphene.String()
|
||
|
||
class Meta:
|
||
model = UserModel
|
||
fields = ('username', 'first_name', 'last_name', 'email')
|
||
|
||
def resolve_id(self, info):
|
||
if hasattr(self, 'profile') and self.profile:
|
||
return self.profile.logto_id
|
||
return self.username
|
||
|
||
def resolve_firstName(self, info):
|
||
return self.first_name
|
||
|
||
def resolve_lastName(self, info):
|
||
return self.last_name
|
||
|
||
def resolve_phone(self, info):
|
||
return getattr(self.profile, 'phone', None)
|
||
|
||
def resolve_avatarId(self, info):
|
||
return getattr(self.profile, 'avatar_id', None)
|
||
|
||
def resolve_createdAt(self, info):
|
||
return self.date_joined.isoformat() if self.date_joined else None
|
||
|
||
class TeamMember(DjangoObjectType):
|
||
user = graphene.Field(User)
|
||
joinedAt = graphene.String()
|
||
|
||
class Meta:
|
||
model = TeamMemberModel
|
||
fields = ('role', 'joined_at')
|
||
|
||
def resolve_user(self, info):
|
||
return self.user
|
||
|
||
def resolve_joinedAt(self, info):
|
||
return self.joined_at.isoformat() if self.joined_at else None
|
||
|
||
class TeamAddress(DjangoObjectType):
|
||
isDefault = graphene.Boolean()
|
||
createdAt = graphene.String()
|
||
graphNodeId = graphene.String()
|
||
processedAt = graphene.String()
|
||
countryCode = graphene.String()
|
||
|
||
class Meta:
|
||
model = TeamAddressModel
|
||
fields = ('uuid', 'name', 'address', 'latitude', 'longitude', 'is_default', 'created_at', 'country_code')
|
||
|
||
def resolve_isDefault(self, info):
|
||
return self.is_default
|
||
|
||
def resolve_createdAt(self, info):
|
||
return self.created_at.isoformat() if self.created_at else None
|
||
|
||
def resolve_graphNodeId(self, info):
|
||
return self.graph_node_id
|
||
|
||
def resolve_processedAt(self, info):
|
||
return self.processed_at.isoformat() if self.processed_at else None
|
||
|
||
def resolve_countryCode(self, info):
|
||
return self.country_code
|
||
|
||
|
||
class SelectedLocation(graphene.ObjectType):
|
||
type = graphene.String()
|
||
uuid = graphene.String()
|
||
name = graphene.String()
|
||
latitude = graphene.Float()
|
||
longitude = graphene.Float()
|
||
|
||
|
||
class Team(DjangoObjectType):
|
||
id = graphene.String()
|
||
ownerId = graphene.String()
|
||
members = graphene.List(TeamMember)
|
||
addresses = graphene.List(lambda: TeamAddress)
|
||
selectedLocation = graphene.Field(SelectedLocation)
|
||
|
||
class Meta:
|
||
model = TeamModel
|
||
fields = ('uuid', 'name', 'logto_org_id', 'owner', 'created_at', 'updated_at')
|
||
|
||
def resolve_id(self, info):
|
||
return self.uuid
|
||
|
||
def resolve_ownerId(self, info):
|
||
if self.owner and hasattr(self.owner, 'profile'):
|
||
return self.owner.profile.logto_id
|
||
return self.owner.username if self.owner else None
|
||
|
||
def resolve_members(self, info):
|
||
return self.members.all()
|
||
|
||
def resolve_addresses(self, info):
|
||
return self.addresses.all()
|
||
|
||
def resolve_selectedLocation(self, info):
|
||
loc_type = getattr(self, 'selected_location_type', None)
|
||
loc_uuid = getattr(self, 'selected_location_uuid', None)
|
||
if loc_type and loc_uuid:
|
||
return SelectedLocation(
|
||
type=loc_type,
|
||
uuid=loc_uuid,
|
||
name=getattr(self, 'selected_location_name', None),
|
||
latitude=getattr(self, 'selected_location_latitude', None),
|
||
longitude=getattr(self, 'selected_location_longitude', None)
|
||
)
|
||
return None
|
||
|
||
|
||
class TeamQuery(graphene.ObjectType):
|
||
team = graphene.Field(Team)
|
||
getTeam = graphene.Field(Team, teamId=graphene.String(required=True))
|
||
team_members = graphene.List(TeamMember)
|
||
team_addresses = graphene.List(TeamAddress)
|
||
|
||
@require_scopes("teams:member")
|
||
def resolve_team(self, info):
|
||
team_uuid = getattr(info.context, 'team_uuid', None)
|
||
if not team_uuid:
|
||
return None
|
||
|
||
try:
|
||
return TeamModel.objects.get(uuid=team_uuid)
|
||
except TeamModel.DoesNotExist:
|
||
return None
|
||
|
||
@require_scopes("teams:member")
|
||
def resolve_getTeam(self, info, teamId):
|
||
# Получаем конкретную команду по ID
|
||
try:
|
||
return TeamModel.objects.get(uuid=teamId)
|
||
except TeamModel.DoesNotExist:
|
||
return None
|
||
|
||
@require_scopes("teams:member")
|
||
def resolve_team_members(self, info):
|
||
# Получаем участников команды
|
||
team_uuid = getattr(info.context, 'team_uuid', None)
|
||
if not team_uuid:
|
||
return []
|
||
|
||
try:
|
||
team = TeamModel.objects.get(uuid=team_uuid)
|
||
return team.members.all()
|
||
except TeamModel.DoesNotExist:
|
||
return []
|
||
|
||
@require_scopes("teams:member")
|
||
def resolve_team_addresses(self, info):
|
||
team_uuid = getattr(info.context, 'team_uuid', None)
|
||
if not team_uuid:
|
||
return []
|
||
|
||
try:
|
||
team = TeamModel.objects.get(uuid=team_uuid)
|
||
return team.addresses.all()
|
||
except TeamModel.DoesNotExist:
|
||
return []
|
||
|
||
|
||
class InviteMemberInput(graphene.InputObjectType):
|
||
email = graphene.String(required=True)
|
||
role = graphene.String()
|
||
|
||
class InviteMemberMutation(graphene.Mutation):
|
||
class Arguments:
|
||
input = InviteMemberInput(required=True)
|
||
|
||
success = graphene.Boolean()
|
||
message = graphene.String()
|
||
|
||
@require_scopes("teams:member")
|
||
def mutate(self, info, input):
|
||
from ..temporal_client import start_invite_workflow
|
||
|
||
# Проверяем права - только owner может приглашать
|
||
team_uuid = getattr(info.context, 'team_uuid', None)
|
||
user_id = getattr(info.context, 'user_id', None)
|
||
|
||
if not team_uuid or not user_id:
|
||
return InviteMemberMutation(success=False, message="Недостаточно прав")
|
||
|
||
try:
|
||
team = TeamModel.objects.get(uuid=team_uuid)
|
||
|
||
# Проверяем что пользователь - owner команды
|
||
if not team.owner:
|
||
return InviteMemberMutation(success=False, message="Только owner может приглашать")
|
||
owner_identifier = team.owner.profile.logto_id if hasattr(team.owner, 'profile') and team.owner.profile else team.owner.username
|
||
if owner_identifier != user_id:
|
||
return InviteMemberMutation(success=False, message="Только owner может приглашать")
|
||
|
||
expires_at = timezone.now() + timedelta(days=7)
|
||
start_invite_workflow(
|
||
team_uuid=str(team.uuid),
|
||
email=input.email,
|
||
role=input.role or 'MEMBER',
|
||
invited_by=owner_identifier,
|
||
expires_at=expires_at.isoformat(),
|
||
)
|
||
|
||
return InviteMemberMutation(success=True, message="Приглашение отправлено")
|
||
|
||
except TeamModel.DoesNotExist:
|
||
return InviteMemberMutation(success=False, message="Команда не найдена")
|
||
|
||
class CreateTeamAddressInput(graphene.InputObjectType):
|
||
name = graphene.String(required=True)
|
||
address = graphene.String(required=True)
|
||
latitude = graphene.Float()
|
||
longitude = graphene.Float()
|
||
countryCode = graphene.String()
|
||
isDefault = graphene.Boolean()
|
||
|
||
|
||
class CreateTeamAddressMutation(graphene.Mutation):
|
||
class Arguments:
|
||
input = CreateTeamAddressInput(required=True)
|
||
|
||
success = graphene.Boolean()
|
||
message = graphene.String()
|
||
workflowId = graphene.String()
|
||
|
||
@require_scopes("teams:member")
|
||
def mutate(self, info, input):
|
||
from ..temporal_client import start_address_workflow
|
||
|
||
team_uuid = getattr(info.context, 'team_uuid', None)
|
||
if not team_uuid:
|
||
return CreateTeamAddressMutation(success=False, message="Не авторизован")
|
||
|
||
try:
|
||
team = TeamModel.objects.get(uuid=team_uuid)
|
||
|
||
# Запускаем workflow - он сам создаст адрес через M2M мутацию
|
||
workflow_id, _ = start_address_workflow(
|
||
team_uuid=str(team.uuid),
|
||
name=input.name,
|
||
address=input.address,
|
||
latitude=input.get('latitude'),
|
||
longitude=input.get('longitude'),
|
||
country_code=input.get('countryCode'),
|
||
is_default=input.get('isDefault', False),
|
||
)
|
||
|
||
return CreateTeamAddressMutation(
|
||
success=True,
|
||
message="Адрес создается",
|
||
workflowId=workflow_id,
|
||
)
|
||
|
||
except TeamModel.DoesNotExist:
|
||
return CreateTeamAddressMutation(success=False, message="Команда не найдена")
|
||
except Exception as e:
|
||
return CreateTeamAddressMutation(success=False, message=str(e))
|
||
|
||
|
||
class DeleteTeamAddressMutation(graphene.Mutation):
|
||
class Arguments:
|
||
uuid = graphene.String(required=True)
|
||
|
||
success = graphene.Boolean()
|
||
message = graphene.String()
|
||
|
||
@require_scopes("teams:member")
|
||
def mutate(self, info, uuid):
|
||
team_uuid = getattr(info.context, 'team_uuid', None)
|
||
if not team_uuid:
|
||
return DeleteTeamAddressMutation(success=False, message="Не авторизован")
|
||
|
||
try:
|
||
team = TeamModel.objects.get(uuid=team_uuid)
|
||
address = team.addresses.get(uuid=uuid)
|
||
address.delete()
|
||
return DeleteTeamAddressMutation(success=True, message="Адрес удален")
|
||
|
||
except TeamModel.DoesNotExist:
|
||
return DeleteTeamAddressMutation(success=False, message="Команда не найдена")
|
||
except TeamAddressModel.DoesNotExist:
|
||
return DeleteTeamAddressMutation(success=False, message="Адрес не найден")
|
||
|
||
|
||
class SetSelectedLocationInput(graphene.InputObjectType):
|
||
type = graphene.String(required=True) # 'address' или 'hub'
|
||
uuid = graphene.String(required=True)
|
||
name = graphene.String(required=True)
|
||
latitude = graphene.Float(required=True)
|
||
longitude = graphene.Float(required=True)
|
||
|
||
|
||
class SetSelectedLocationMutation(graphene.Mutation):
|
||
class Arguments:
|
||
input = SetSelectedLocationInput(required=True)
|
||
|
||
success = graphene.Boolean()
|
||
message = graphene.String()
|
||
selectedLocation = graphene.Field(SelectedLocation)
|
||
|
||
@require_scopes("teams:member")
|
||
def mutate(self, info, input):
|
||
team_uuid = getattr(info.context, 'team_uuid', None)
|
||
if not team_uuid:
|
||
return SetSelectedLocationMutation(success=False, message="Не авторизован")
|
||
|
||
location_type = input.type
|
||
if location_type not in ('address', 'hub'):
|
||
return SetSelectedLocationMutation(success=False, message="Неверный тип локации")
|
||
|
||
try:
|
||
team = TeamModel.objects.get(uuid=team_uuid)
|
||
team.selected_location_type = location_type
|
||
team.selected_location_uuid = input.uuid
|
||
team.selected_location_name = input.name
|
||
team.selected_location_latitude = input.latitude
|
||
team.selected_location_longitude = input.longitude
|
||
team.save(update_fields=[
|
||
'selected_location_type',
|
||
'selected_location_uuid',
|
||
'selected_location_name',
|
||
'selected_location_latitude',
|
||
'selected_location_longitude'
|
||
])
|
||
|
||
return SetSelectedLocationMutation(
|
||
success=True,
|
||
message="Локация выбрана",
|
||
selectedLocation=SelectedLocation(
|
||
type=location_type,
|
||
uuid=input.uuid,
|
||
name=input.name,
|
||
latitude=input.latitude,
|
||
longitude=input.longitude
|
||
)
|
||
)
|
||
|
||
except TeamModel.DoesNotExist:
|
||
return SetSelectedLocationMutation(success=False, message="Команда не найдена")
|
||
|
||
|
||
class TeamMutation(graphene.ObjectType):
|
||
invite_member = InviteMemberMutation.Field()
|
||
create_team_address = CreateTeamAddressMutation.Field()
|
||
delete_team_address = DeleteTeamAddressMutation.Field()
|
||
set_selected_location = SetSelectedLocationMutation.Field()
|
||
|
||
team_schema = graphene.Schema(query=TeamQuery, mutation=TeamMutation)
|