feat: passport images for user
This commit is contained in:
@@ -107,6 +107,13 @@ class User(BaseModel):
|
|||||||
uselist=True,
|
uselist=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
passport_images = relationship(
|
||||||
|
'PassportImage',
|
||||||
|
back_populates='user',
|
||||||
|
lazy='selectin',
|
||||||
|
cascade="all, delete-orphan"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Position(BaseModel):
|
class Position(BaseModel):
|
||||||
__tablename__ = 'positions'
|
__tablename__ = 'positions'
|
||||||
@@ -119,3 +126,12 @@ class Position(BaseModel):
|
|||||||
uselist=False,
|
uselist=False,
|
||||||
back_populates='position'
|
back_populates='position'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class PassportImage(BaseModel):
|
||||||
|
__tablename__ = 'passport_images'
|
||||||
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
|
user_id = mapped_column(ForeignKey('users.id'), nullable=False)
|
||||||
|
user: Mapped["User"] = relationship(back_populates='passport_images')
|
||||||
|
|
||||||
|
image_url: Mapped[str] = mapped_column(nullable=False)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from fastapi import APIRouter, Depends
|
from fastapi import APIRouter, Depends, UploadFile
|
||||||
|
|
||||||
from backend.dependecies import SessionDependency
|
from backend.dependecies import SessionDependency
|
||||||
from schemas.user import *
|
from schemas.user import *
|
||||||
@@ -56,3 +56,17 @@ async def get_managers(
|
|||||||
session: SessionDependency,
|
session: SessionDependency,
|
||||||
):
|
):
|
||||||
return await UserService(session).get_managers()
|
return await UserService(session).get_managers()
|
||||||
|
|
||||||
|
|
||||||
|
@user_router.post(
|
||||||
|
'/passport-images/upload/{user_id}',
|
||||||
|
response_model=UploadPassportImageResponse,
|
||||||
|
operation_id='upload_passport_image'
|
||||||
|
)
|
||||||
|
async def upload_passport_image(
|
||||||
|
user_id: int,
|
||||||
|
upload_file: UploadFile,
|
||||||
|
session: SessionDependency,
|
||||||
|
):
|
||||||
|
file_bytes = upload_file.file.read()
|
||||||
|
return await UserService(session).upload_passport_image(user_id, file_bytes)
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
|
from pydantic import model_validator
|
||||||
|
|
||||||
from schemas.base import BaseSchema, OkMessageSchema
|
from schemas.base import BaseSchema, OkMessageSchema
|
||||||
from schemas.payrate import PayRateSchema
|
from schemas.payrate import PayRateSchema
|
||||||
from schemas.position import PositionSchema
|
from schemas.position import PositionSchema
|
||||||
@@ -8,6 +10,12 @@ from schemas.role import RoleSchema
|
|||||||
|
|
||||||
# region Entities
|
# region Entities
|
||||||
|
|
||||||
|
class PassportImageSchema(BaseSchema):
|
||||||
|
id: int
|
||||||
|
user_id: int
|
||||||
|
image_url: str
|
||||||
|
|
||||||
|
|
||||||
class BasicUser(BaseSchema):
|
class BasicUser(BaseSchema):
|
||||||
telegram_id: int
|
telegram_id: int
|
||||||
phone_number: str | None = None
|
phone_number: str | None = None
|
||||||
@@ -23,6 +31,18 @@ class BasicUser(BaseSchema):
|
|||||||
role_key: str
|
role_key: str
|
||||||
pay_rate: Optional[PayRateSchema] = None
|
pay_rate: Optional[PayRateSchema] = None
|
||||||
|
|
||||||
|
passport_image_url: str | None = None
|
||||||
|
passport_images: list[PassportImageSchema] | None = []
|
||||||
|
|
||||||
|
@model_validator(mode="after")
|
||||||
|
def image_url_to_list(cls, values):
|
||||||
|
passport_images = values.passport_images
|
||||||
|
if not passport_images:
|
||||||
|
return values
|
||||||
|
latest_image = passport_images[-1]
|
||||||
|
values.passport_image_url = latest_image.image_url
|
||||||
|
return values
|
||||||
|
|
||||||
|
|
||||||
class BaseUser(BasicUser):
|
class BaseUser(BasicUser):
|
||||||
id: int
|
id: int
|
||||||
@@ -70,4 +90,8 @@ class CreateUserResponse(OkMessageSchema):
|
|||||||
class GetManagersResponse(BaseSchema):
|
class GetManagersResponse(BaseSchema):
|
||||||
managers: List[UserSchema]
|
managers: List[UserSchema]
|
||||||
|
|
||||||
|
|
||||||
|
class UploadPassportImageResponse(OkMessageSchema):
|
||||||
|
image_url: str | None = None
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
from sqlalchemy import select, update, delete, insert, and_
|
from sqlalchemy import select, update, delete, insert, and_
|
||||||
|
|
||||||
from models import User, user_position, user_pay_rate
|
from backend import config
|
||||||
|
from external.s3_uploader.uploader import S3Uploader
|
||||||
|
from models import User, user_position, user_pay_rate, PassportImage
|
||||||
from services.base import BaseService
|
from services.base import BaseService
|
||||||
from schemas.user import *
|
from schemas.user import *
|
||||||
|
|
||||||
@@ -34,6 +36,7 @@ class UserService(BaseService):
|
|||||||
try:
|
try:
|
||||||
base_fields = request.data.model_dump_parent()
|
base_fields = request.data.model_dump_parent()
|
||||||
del base_fields['pay_rate']
|
del base_fields['pay_rate']
|
||||||
|
del base_fields['passport_image_url']
|
||||||
user = User(**base_fields)
|
user = User(**base_fields)
|
||||||
self.session.add(user)
|
self.session.add(user)
|
||||||
await self.session.flush()
|
await self.session.flush()
|
||||||
@@ -62,6 +65,8 @@ class UserService(BaseService):
|
|||||||
return UpdateUserResponse(ok=False, message='Указанный пользователь не найден')
|
return UpdateUserResponse(ok=False, message='Указанный пользователь не найден')
|
||||||
base_fields = request.data.model_dump_parent()
|
base_fields = request.data.model_dump_parent()
|
||||||
del base_fields['pay_rate']
|
del base_fields['pay_rate']
|
||||||
|
del base_fields['passport_image_url']
|
||||||
|
del base_fields['passport_images']
|
||||||
stmt = update(User).values(**base_fields).where(User.id == request.data.id)
|
stmt = update(User).values(**base_fields).where(User.id == request.data.id)
|
||||||
await self.session.execute(stmt)
|
await self.session.execute(stmt)
|
||||||
await self.session.flush()
|
await self.session.flush()
|
||||||
@@ -93,3 +98,30 @@ class UserService(BaseService):
|
|||||||
return UpdateUserResponse(ok=True, message='Пользователь успешно обновлен')
|
return UpdateUserResponse(ok=True, message='Пользователь успешно обновлен')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return UpdateUserResponse(ok=False, message=str(e))
|
return UpdateUserResponse(ok=False, message=str(e))
|
||||||
|
|
||||||
|
async def upload_passport_image(self, user_id: int, file_bytes: bytes) -> UploadPassportImageResponse:
|
||||||
|
try:
|
||||||
|
user: Optional[User] = await self.session.get(User, user_id)
|
||||||
|
if not user:
|
||||||
|
raise Exception("Не удалось пользователя с указанным ID")
|
||||||
|
# removing previous images
|
||||||
|
for image in user.passport_images:
|
||||||
|
await self.session.delete(image)
|
||||||
|
s3_uploader = S3Uploader(config.S3_API_KEY)
|
||||||
|
response = await s3_uploader.upload(file_bytes)
|
||||||
|
response_url = response.get('link')
|
||||||
|
if not response_url:
|
||||||
|
raise Exception("Не удалось загрузить изображение")
|
||||||
|
passport_image = PassportImage(
|
||||||
|
user_id=user_id,
|
||||||
|
image_url=response_url,
|
||||||
|
)
|
||||||
|
self.session.add(passport_image)
|
||||||
|
await self.session.commit()
|
||||||
|
return UploadPassportImageResponse(
|
||||||
|
ok=True,
|
||||||
|
message='Изображение успешно загружено',
|
||||||
|
image_url=response_url
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
return UploadPassportImageResponse(ok=False, message=str(e))
|
||||||
|
|||||||
Reference in New Issue
Block a user