From d56e2922766560ef360457362e03e7fee6975cb4 Mon Sep 17 00:00:00 2001 From: AlexSserb Date: Wed, 4 Dec 2024 20:20:03 +0400 Subject: [PATCH] feat: passport images for user --- models/auth.py | 16 ++++++++++++++++ routers/user.py | 16 +++++++++++++++- schemas/user.py | 24 ++++++++++++++++++++++++ services/user.py | 34 +++++++++++++++++++++++++++++++++- 4 files changed, 88 insertions(+), 2 deletions(-) diff --git a/models/auth.py b/models/auth.py index b3019c2..8a56d67 100644 --- a/models/auth.py +++ b/models/auth.py @@ -107,6 +107,13 @@ class User(BaseModel): uselist=True, ) + passport_images = relationship( + 'PassportImage', + back_populates='user', + lazy='selectin', + cascade="all, delete-orphan" + ) + class Position(BaseModel): __tablename__ = 'positions' @@ -119,3 +126,12 @@ class Position(BaseModel): uselist=False, 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) diff --git a/routers/user.py b/routers/user.py index 8593a5c..d5672ae 100644 --- a/routers/user.py +++ b/routers/user.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Depends +from fastapi import APIRouter, Depends, UploadFile from backend.dependecies import SessionDependency from schemas.user import * @@ -56,3 +56,17 @@ async def get_managers( session: SessionDependency, ): 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) diff --git a/schemas/user.py b/schemas/user.py index d6379a6..12b9941 100644 --- a/schemas/user.py +++ b/schemas/user.py @@ -1,5 +1,7 @@ from typing import List, Optional +from pydantic import model_validator + from schemas.base import BaseSchema, OkMessageSchema from schemas.payrate import PayRateSchema from schemas.position import PositionSchema @@ -8,6 +10,12 @@ from schemas.role import RoleSchema # region Entities +class PassportImageSchema(BaseSchema): + id: int + user_id: int + image_url: str + + class BasicUser(BaseSchema): telegram_id: int phone_number: str | None = None @@ -23,6 +31,18 @@ class BasicUser(BaseSchema): role_key: str 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): id: int @@ -70,4 +90,8 @@ class CreateUserResponse(OkMessageSchema): class GetManagersResponse(BaseSchema): managers: List[UserSchema] + +class UploadPassportImageResponse(OkMessageSchema): + image_url: str | None = None + # endregion diff --git a/services/user.py b/services/user.py index b9d91ee..927f875 100644 --- a/services/user.py +++ b/services/user.py @@ -1,6 +1,8 @@ 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 schemas.user import * @@ -34,6 +36,7 @@ class UserService(BaseService): try: base_fields = request.data.model_dump_parent() del base_fields['pay_rate'] + del base_fields['passport_image_url'] user = User(**base_fields) self.session.add(user) await self.session.flush() @@ -62,6 +65,8 @@ class UserService(BaseService): return UpdateUserResponse(ok=False, message='Указанный пользователь не найден') base_fields = request.data.model_dump_parent() 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) await self.session.execute(stmt) await self.session.flush() @@ -93,3 +98,30 @@ class UserService(BaseService): return UpdateUserResponse(ok=True, message='Пользователь успешно обновлен') except Exception as 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))