diff --git a/src/dependencies.py b/src/dependencies.py index 98349578..e7e477eb 100644 --- a/src/dependencies.py +++ b/src/dependencies.py @@ -55,14 +55,14 @@ def get_current_active_user(user: User = Depends(get_current_user)): return user -def get_current_admin(user: User = Depends(get_current_active_user)): +def get_current_admin(user: User = Depends(get_current_user)): """ get_current_admin 사용법 예시 def example(current_user: User = Depends(get_current_admin)): return {"message": "Welcome Admin!"} """ - if not user.admin or not user.admin[0].admin_status or user.admin[0].is_deleted: + if not user.admin or not user.admin[-1].admin_status or user.admin[-1].is_deleted: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="The user doesn't have enough privileges" diff --git a/src/domain/enums/admin_status.py b/src/domain/enums/admin_status.py deleted file mode 100644 index 35948e10..00000000 --- a/src/domain/enums/admin_status.py +++ /dev/null @@ -1,18 +0,0 @@ -from enum import Enum - - -class AdminStatus(Enum): - ACTIVE = ("ACTIVE", "현재 활성화된 관리자입니다.") - INACTIVE = ("INACTIVE", "현재 비활성화된 관리자입니다.") - SUSPENDED = ("SUSPENDED", "현재 정지된 관리자입니다.") - - def __init__(self, status, description): - self.status = status - self.description = description - - def __str__(self): - return self.status - -# 사용 예시 -# print(AdminStatus.ACTIVE.admin_status) # ACTIVE -# print(AdminStatus.ACTIVE.description) # 현재 활성화된 관리자입니다. diff --git a/src/domain/enums/status.py b/src/domain/enums/status.py new file mode 100644 index 00000000..f22edc9d --- /dev/null +++ b/src/domain/enums/status.py @@ -0,0 +1,78 @@ +from enum import Enum + + +class ExtendEnum(Enum): + @classmethod + def is_valid_enum_value(cls, status) -> bool: + return status in cls._value2member_map_ + + def __call__(self): + return self.value + + def __repr__(self): + return f"<{self.__class__.__name__}.{self.name}({self.value})>" + +class AdminStatus(ExtendEnum): + ''' + # 사용 예시 + # print(AdminStatus.ACTIVE) # 현재 활성화된 관리자입니다. + # print(AdminStatus.ACTIVE()) # True + # print(AdminStatus.INACTIVE()) # False + ''' + ACTIVE = True + INACTIVE = False + + def __str__(self): + desc = "활성화된" if self.value else "비활성화된" + return f"현재 {desc} 관리자입니다." + + +class BookStatus(ExtendEnum): + AVAILABLE = True + INAVILABLE = False + + def __str__(self): + desc = "이용 가능" if self.value else "이용 불가능한" + return f"현재 {desc} 한 도서입니다." + +class BookRequestStatus(ExtendEnum): + ''' + # 사용 예시 + # print(BookRequestStatus.WAITING) # 해당 도서 구매 요청은 대기 중입니다. + # print(BookRequestStatus.WAITING()) # 0 + # print(BookRequestStatus.WAITING.__repr__()) # + # print(BookRequestStatus.is_valid_enum_value(3)) # True + ''' + WAITING = 0 + PURCHASED = 1 + CANCELED = 2 + REJECTED = 3 + + def __str__(self): + status_descriptions = { + 0: "해당 도서 구매 요청은 대기 중입니다.", + 1: "해당 도서 구매 요청은 구매 완료되었습니다.", + 2: "해당 도서 구매 요청은 신청자 취소되었습니다.", + 3: "해당 도서 구매 요청은 관리자 반려되었습니다." + } + return status_descriptions.get(self.value, "잘못된 도서 구매 요청 상태입니다.") + + +class ReturnStatus(ExtendEnum): + RETURNED = True + NOT_RETURNED = False + + def __str__(self): + desc = "반납 완료된" if self.value else "대출 중인" + return f"현재 {desc} 도서입니다." + + +class ExtendStatus(ExtendEnum): + EXTENDED = True + NOT_EXTENDED = False + + def __str__(self): + desc = "연장된" if self.value else "연장되지 않은" + return f"대출이 {desc} 상태입니다." + + diff --git a/src/domain/schemas/bookrequest_schemas.py b/src/domain/schemas/bookrequest_schemas.py index 2024dced..e50b7916 100644 --- a/src/domain/schemas/bookrequest_schemas.py +++ b/src/domain/schemas/bookrequest_schemas.py @@ -47,6 +47,15 @@ class DomainResBookRequest(BaseModel): publication_year: int = Field(title="publication_year", description="출판년도", example=2022, gt=0) request_link: str = Field(title="request_link", description="요청 링크", example="https://example.com/request") reason: str = Field(title="reason", description="이유", example="Need for study") - processing_status: int = Field(0, title="processing_status", description="처리 상태", example=0, ge=0, le=3) + processing_status: int = Field(0, title="processing_status", description="처리 상태", example=0) request_date: date = Field(title="request_date", description="요청 일자", example=date.today()) reject_reason: str | None = Field(None, title="reject_reason", description="거절 사유", example="Not available") + +class DomainResAdminGetBookRequest(BaseModel): + data: list[DomainResBookRequest] + total: int = Field(0, title="total", description="총 도서 구매 요청", example=0, ge=0) + +class DomainReqAdminPutBookRequest(BaseModel): + request_id: int = Field(title="book_request_id", description="도서 구매 요청 정보 id", example=1, gt=0) + processing_status: int = Field(0, title="processing_status", description="처리 상태", example=0) + reject_reason: str | None = Field(None, title="reject_reason", description="거절 사유", example="Not available") diff --git a/src/domain/schemas/loan_schemas.py b/src/domain/schemas/loan_schemas.py index 4d80600c..c66e6942 100644 --- a/src/domain/schemas/loan_schemas.py +++ b/src/domain/schemas/loan_schemas.py @@ -4,21 +4,7 @@ from pydantic import BaseModel, Field -class Loan(BaseModel): - id: int = Field(title="loan_id", description="대출 정보 id", example=1, gt=0) - book_id: int = Field(title="book_id", description="대출한 책 ID", example=1, gt=0) - user_id: int = Field(title="user_id", description="대출한 사용자 ID", example=1, gt=0) - created_at: datetime = Field(title="create_at", description="생성일시", example=datetime.now()) - updated_at: datetime = Field(title="update_at", description="수정일시", example=datetime.now()) - loan_date: date = Field(title="loan_date", description="대출 날짜", example=datetime.today().date()) - due_date: date = Field(title="due_date", description="반납 기한", example=(datetime.today() + timedelta(days=14)).date()) - extend_status: bool = Field(title="extend_status", description="연장 상태", example=True) - overdue_days: int = Field(title="overdue_days", description="연체 일자", example=1) - return_status: bool = Field(title="return_status", description="반납 상태", example=False) - return_date: date | None = Field(title="return_date", description="반납 날짜", example=None) - - -class DomainResGetLoanItem(BaseModel): +class DomainResGetLoan(BaseModel): loan_id: int = Field(title="loan_id", description="대출 정보 id", example=1, gt=0) book_id: int = Field(title="book_id", description="대출한 책 ID", example=1, gt=0) user_id: int = Field(title="user_id", description="대출한 사용자 ID", example=1, gt=0) @@ -56,25 +42,7 @@ class DomainReqPostLoan(BaseModel): book_id: int = Field(title="book_id", description="대출한 책 ID", example=1, gt=0) -class LoanCreate(BaseModel): - user_id: int = Field(title="user_id", description="대출한 사용자 ID", example=1, gt=0) - book_id: int = Field(title="book_id", description="대출한 책 ID", example=1, gt=0) - created_at: datetime = Field(title="create_at", description="생성일시", example=datetime.now()) - updated_at: datetime = Field(title="update_at", description="수정일시", example=datetime.now()) - - -class DomainResGetLoan(BaseModel): - loan_id: int = Field(title="loan_id", description="대출 정보 id", example=1, gt=0) - book_id: int = Field(title="book_id", description="대출한 책 ID", example=1, gt=0) - user_id: int = Field(title="user_id", description="대출한 사용자 ID", example=1, gt=0) - loan_date: date = Field(title="loan_date", description="대출 날짜", example=datetime.today().date()) - due_date: date = Field(title="due_date", description="반납 기한", example=(datetime.today() + timedelta(days=14)).date()) - extend_status: bool = Field(title="extend_status", description="연장 상태", example=True) - overdue_days: int = Field(title="overdue_days", description="연체 일자", example=1) - return_status: bool = Field(title="return_status", description="반납 상태", example=False) - return_date: date | None = Field(title="return_date", description="반납 날짜", example=None) - -class DomainAdminGetLoanItem(BaseModel): +class DomainAdminGetLoan(BaseModel): loan_id: int = Field(title="loan_id", description="대출 id", example=1, gt=0) book_id: int = Field(title="book_id", description="대출한 책 ID", example=1, gt=0) user_id: int = Field(title="user_id", description="대출한 사용자 ID", example=1, gt=0) diff --git a/src/domain/schemas/user_schemas.py b/src/domain/schemas/user_schemas.py index 2029404f..a5c5b64c 100644 --- a/src/domain/schemas/user_schemas.py +++ b/src/domain/schemas/user_schemas.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import date, datetime from pydantic import BaseModel, Field @@ -38,3 +38,23 @@ class DomainAdminGetUserItem(BaseModel): is_active: bool = Field(title="is_active", description="활동 상태") created_at: datetime = Field(title="create_at", description="생성일시") updated_at: datetime = Field(title="update_at", description="수정일시") + +class DomainReqAdminPutUser(BaseModel): + user_id: int = Field(title="user_id", description="관리자의 회원 ID", gt=0) + user_status: bool | None = Field(None, title="is_active", description="회원 상태(대출 가능 여부)", examples=[True]) + admin_status: bool | None = Field(None, title="admin_status", description="관리자 권한 상태") + expiration_date : date | None = Field(None, title="expiration_date", description="관리자 권한 만료일, \ + 기본적으로 권한 부여일로부터 1년", examples=["2025-07-02"]) +class DomainResAdminPutUser(BaseModel): + user_id: int = Field(title="user_id", description="유저 고유 ID", example=1111, gt=0) + auth_id: str = Field(title="auth_id", description="로그인 ID", example="gildong1") + email: str = Field(title="email", description="이메일 주소", example="KUCC@korea.ac.kr") + user_name: str = Field(title="user_name", description="사용자 이름", example="홍길동") + is_active: bool = Field(title="is_active", description="활동 상태", examples=[True]) + is_admin: bool = Field(title="is_admin", description="관리자 권환", examples=[False]) + expiration_date : date | None = Field(title="expiration_date", description="관리자 권한 만료일, \ + 기본적으로 권한 부여일로부터 1년", examples=["2025-07-02"]) + +class DomainReqAdminDelUser(BaseModel): + user_id: int = Field(title="user_id", description="유저 고유 ID", example=1111, gt=0) + diff --git a/src/domain/services/admin/book_service.py b/src/domain/services/admin/book_service.py index c129dccd..dde1821e 100644 --- a/src/domain/services/admin/book_service.py +++ b/src/domain/services/admin/book_service.py @@ -99,13 +99,6 @@ async def service_admin_search_books( async def service_admin_create_book(request: DomainReqAdminPostBook, db: Session): - # check if the book already exists in database - stmt = select(Book).where(Book.book_title == request.book_title) - exist_request = db.execute(stmt).scalar_one_or_none() - - if exist_request: - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, - detail="Already exists") if request.code[0] not in {category.name for category in BookCategoryStatus}: raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid Category") diff --git a/src/domain/services/admin/bookrequest_service.py b/src/domain/services/admin/bookrequest_service.py new file mode 100644 index 00000000..bd6a4748 --- /dev/null +++ b/src/domain/services/admin/bookrequest_service.py @@ -0,0 +1,133 @@ + +from datetime import datetime +from math import ceil + +from fastapi import HTTPException, status +from sqlalchemy import and_, func, select +from sqlalchemy.exc import IntegrityError +from sqlalchemy.orm import Session + +from domain.enums.status import BookRequestStatus +from domain.schemas.bookrequest_schemas import ( + DomainReqAdminPutBookRequest, + DomainResAdminGetBookRequest, + DomainResBookRequest, +) +from repositories.models import RequestedBook + + +async def service_admin_read_bookrequest(db: Session, page: int, limit: int): + total = db.execute(select(func.count()).select_from(RequestedBook) + .where(RequestedBook.is_deleted==False)).scalar() + if ceil(total/limit) DomainResGetLoan: + loan = get_item(Loan, loan_id, db) + + try: + if loan.return_status: # 반납 상태가 True이면 False로 변경 + loan.return_status = False + loan.return_date = None + else: # 반납 상태가 False이면 True로 변경 + loan.return_status = True + loan.return_date = datetime.now().date() + + db.flush() + except HTTPException as e: + raise e from e + except Exception as e: + db.rollback() + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Unexpected error occurred during update: {str(e)}", + ) from e + else: + db.commit() + db.refresh(loan) + + result = DomainResGetLoan( + loan_id=loan.id, + book_id=loan.book_id, + user_id=loan.user_id, + created_at=loan.created_at, + updated_at=loan.updated_at, + loan_date=loan.loan_date, + due_date=loan.due_date, + extend_status=loan.extend_status, + overdue_days=loan.overdue_days, + return_status=loan.return_status, + return_date=loan.return_date, + ) + + return result async def service_admin_search_loans( user_name: str | None, @@ -12,7 +59,7 @@ async def service_admin_search_loans( category_name: str | None, return_status: str | None, db: Session -) -> list[DomainAdminGetLoanItem]: +) -> list[DomainAdminGetLoan]: stmt = ( select(Loan) .options(joinedload(Loan.user), joinedload(Loan.book)) @@ -48,7 +95,7 @@ async def service_admin_search_loans( raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Loans not found") search_loans = [ - DomainAdminGetLoanItem( + DomainAdminGetLoan( loan_id=loan.id, book_id=loan.book_id, user_id=loan.user_id, @@ -77,7 +124,7 @@ async def service_admin_search_loans( return search_loans -async def service_admin_read_loans(db: Session) -> list[DomainAdminGetLoanItem]: +async def service_admin_read_loans(db: Session) -> list[DomainAdminGetLoan]: stmt = ( select(Loan) .options( @@ -96,7 +143,7 @@ async def service_admin_read_loans(db: Session) -> list[DomainAdminGetLoanItem]: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Books not found") search_loans = [ - DomainAdminGetLoanItem( + DomainAdminGetLoan( loan_id=loan.id, book_id=loan.book_id, user_id=loan.user_id, @@ -123,3 +170,4 @@ async def service_admin_read_loans(db: Session) -> list[DomainAdminGetLoanItem]: ) from e return search_loans + diff --git a/src/domain/services/admin/user_service.py b/src/domain/services/admin/user_service.py index a4e08117..fc321632 100644 --- a/src/domain/services/admin/user_service.py +++ b/src/domain/services/admin/user_service.py @@ -1,9 +1,18 @@ +from datetime import datetime, timedelta + from fastapi import HTTPException, status from sqlalchemy import select, text +from sqlalchemy.exc import NoResultFound from sqlalchemy.orm import Session, selectinload -from domain.schemas.user_schemas import DomainAdminGetUserItem -from repositories.models import User +from domain.schemas.user_schemas import ( + DomainAdminGetUserItem, + DomainReqAdminDelUser, + DomainReqAdminPutUser, + DomainResAdminPutUser, +) +from repositories.models import Admin, User +from utils.crud_utils import delete_item async def service_admin_search_users( @@ -26,7 +35,7 @@ async def service_admin_search_users( .params(user_name=f"{user_name}*") ) if authority is not None: - stmt = stmt.where(User.admin[0].admin_status == authority) + stmt = stmt.where(User.admin[-1].admin_status == authority) if active is not None: stmt = stmt.where(User.is_active == active) @@ -105,3 +114,100 @@ async def service_admin_read_users(db: Session) -> list[DomainAdminGetUserItem]: ) from e return search_users + + + +async def service_admin_update_user(request: DomainReqAdminPutUser, db: Session): + user_stmt = select(User).where(User.id == request.user_id, User.is_deleted == False) + try : + user = db.execute(user_stmt).scalar_one_or_none() + + if user is None : + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, \ + detail="Not found user by user_id") + + if request.user_status is not None : + user.is_active = request.user_status + + if user.admin and not user.admin[-1].is_deleted: + if request.admin_status == False: + # 기존 admin이 있고 admin status가 False로 요청된 경우 + user.admin[-1].admin_status = False + user.admin[-1].is_deleted = True + if request.expiration_date: + user.admin[-1].expiration_date = request.expiration_date + + elif request.admin_status: + # 기존 admin이 없고, 새 admin을 만들어야 하는 경우 + new_admin = Admin( + user_id=request.user_id, + admin_status=True, + expiration_date = request.expiration_date if request.expiration_date \ + else (datetime.today() + timedelta(days=365)).date() # 1년 후로 만료일 갱신 + ) + db.add(new_admin) + + else: + # admin 상태 값이 False로 요청되었지만 기존 admin도 없고 새 admin도 생성하지 않는 경우 + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Not updated admin_status value") + + db.flush() + db.commit() + + except HTTPException as e: + raise e + + except Exception as e: + db.rollback() + raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Unexpected error occurred during update: {str(e)}") from e + else : + db.refresh(user) + + response = DomainResAdminPutUser( + user_id=user.id, + auth_id=user.auth_id, + email=user.email, + user_name=user.user_name, + is_active=user.is_active, + is_admin=True if user.admin[-1].admin_status else False, + expiration_date=user.admin[-1].expiration_date if user.admin[-1].admin_status else None + ) + return response + + +async def service_admin_delete_user(request: DomainReqAdminDelUser, db: Session): + # 유저를 검색해서 관리자가 아니라면 바로 삭제. 관리자라면 관리자 테이블에서 이미 삭제되었는지 확인 후 삭제 + stmt = ( + select(User) + .where( + User.id == request.user_id, + User.is_deleted == False + ) + ) + + try: + user = db.execute(stmt).scalar_one() + + if user.admin: + if not user.admin[-1].is_deleted: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Cannot delete an active admin user" + ) + except NoResultFound as e: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="User not found" + ) from e + except HTTPException as e: + raise e + except Exception as e: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Unexpected error occurred during user retrieval: {str(e)}", + ) from e + + delete_item(User, request.user_id, db) + + return diff --git a/src/domain/services/auth_service.py b/src/domain/services/auth_service.py index ca67fb0e..d11f617c 100644 --- a/src/domain/services/auth_service.py +++ b/src/domain/services/auth_service.py @@ -99,10 +99,6 @@ async def login_with_username( if user is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found") - # Check if the user is active - if not user.is_active: - raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="User disabled") - # Create JWT tokens token_response = create_user_tokens(user.id) response = JSONResponse(content={ diff --git a/src/domain/services/bookrequest_service.py b/src/domain/services/bookrequest_service.py index 019a1562..950553fd 100644 --- a/src/domain/services/bookrequest_service.py +++ b/src/domain/services/bookrequest_service.py @@ -114,7 +114,11 @@ async def service_update_bookrequest(request_data: DomainReqPutBookRequest, db: return response -async def service_read_bookrequest_list(request_data: DomainReqGetBookRequest, db: Session) -> list[DomainResBookRequest]: +async def service_read_bookrequest_list( + request_data: DomainReqGetBookRequest, + db: Session + ) -> list[DomainResBookRequest]: + stmt = ( select(RequestedBook) .where(and_(RequestedBook.user_id == request_data.user_id, RequestedBook.is_deleted == False)) diff --git a/src/domain/services/loan_service.py b/src/domain/services/loan_service.py index fa921c83..09372b4e 100644 --- a/src/domain/services/loan_service.py +++ b/src/domain/services/loan_service.py @@ -4,12 +4,7 @@ from sqlalchemy import and_, select from sqlalchemy.orm import Session -from domain.schemas.loan_schemas import ( - DomainReqPostLoan, - DomainReqPutLoan, - DomainResGetLoan, - DomainResGetLoanItem, -) +from domain.schemas.loan_schemas import DomainReqPostLoan, DomainReqPutLoan, DomainResGetLoan from repositories.models import Book, Loan from utils.crud_utils import get_item @@ -24,8 +19,10 @@ async def service_read_loans_by_user_id(user_id, db: Session): result = [ DomainResGetLoan( loan_id=loan.id, - user_id=loan.user_id, book_id=loan.book_id, + user_id=loan.user_id, + created_at=loan.created_at, + updated_at=loan.updated_at, loan_date=loan.loan_date, due_date=loan.due_date, extend_status=loan.extend_status, @@ -35,6 +32,8 @@ async def service_read_loans_by_user_id(user_id, db: Session): ) for loan in loans ] + except HTTPException as e: + raise e from e except Exception as e: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, @@ -75,7 +74,7 @@ async def service_extend_loan(request: DomainReqPutLoan, db: Session): db.commit() db.refresh(loan) - result = DomainResGetLoanItem( + result = DomainResGetLoan( loan_id=loan.id, book_id=loan.book_id, user_id=loan.user_id, @@ -127,7 +126,7 @@ async def service_create_loan(request: DomainReqPostLoan, db: Session): db.commit() db.refresh(loan) - result = DomainResGetLoanItem( + result = DomainResGetLoan( loan_id=loan.id, book_id=loan.book_id, user_id=loan.user_id, diff --git a/src/main.py b/src/main.py index 28aada0b..6874a062 100644 --- a/src/main.py +++ b/src/main.py @@ -4,10 +4,10 @@ from starlette.exceptions import HTTPException as StarletteHTTPException from config import Settings -from routes.admin.admin_books_route import router as admin_books_router -from routes.admin.admin_notice_route import router as admin_notice_router from routes.admin.book_route import router as admin_book_router +from routes.admin.bookreqeust_route import router as admin_bookreqeust_router from routes.admin.loan_route import router as admin_loan_router +from routes.admin.notice_route import router as admin_notice_router from routes.admin.user_route import router as admin_user_router from routes.authentication_route import router as auth_router from routes.book_review_route import router as review_router @@ -39,7 +39,8 @@ "http://localhost:10242", "http://localhost:5173", "http://localhost:3000", - "https://localhost:8000" + "https://localhost:8000", + "https://kubook-frontend.vercel.app/" ] app.add_middleware( @@ -52,17 +53,16 @@ ) app.include_router(auth_router) -app.include_router(admin_books_router) app.include_router(books_router) app.include_router(user_router) app.include_router(loan_router) app.include_router(review_router) app.include_router(notice_router) - app.include_router(bookrequest_router) +app.include_router(admin_book_router) app.include_router(admin_loan_router) app.include_router(admin_user_router) -app.include_router(admin_book_router) +app.include_router(admin_bookreqeust_router) app.include_router(admin_notice_router) diff --git a/src/routes/admin/admin_books_route.py b/src/routes/admin/admin_books_route.py deleted file mode 100644 index 98e331d8..00000000 --- a/src/routes/admin/admin_books_route.py +++ /dev/null @@ -1,129 +0,0 @@ -from fastapi import APIRouter, Depends, status -from sqlalchemy.orm import Session - -from dependencies import get_current_admin, get_db -from domain.schemas.book_schemas import DomainReqAdminDelBook, DomainReqAdminPostBook, DomainReqAdminPutBook -from domain.services.admin.book_service import ( - service_admin_create_book, - service_admin_delete_book, - service_admin_update_book, -) -from routes.admin.request.book_request import RouteReqAdminPostBook, RouteReqAdminPutBook -from routes.admin.response.book_response import RouteResAdminPostBook, RouteResAdminPutBook - -router = APIRouter( - prefix="/admin/books", - tags=["admin/books"], - dependencies=[Depends(get_current_admin)] -) - - -@router.post( - "/", - summary="관리자 도서 정보 등록", - response_model=RouteResAdminPostBook, - status_code=status.HTTP_201_CREATED -) -async def create_admin_book( - request: RouteReqAdminPostBook, - db: Session = Depends(get_db), -): - domain_req = DomainReqAdminPostBook( - book_title = request.book_title, - code=request.code, - category_name = request.category_name, - subtitle=request.subtitle, - author=request.author, - publisher=request.publisher, - publication_year=request.publication_year, - image_url = request.image_url, - version = request.version, - major = request.major, - language=request.language, - book_status=request.book_status, - donor_name = request.donor_name - ) - domain_res = await service_admin_create_book(domain_req, db) - result = RouteResAdminPostBook( - book_id=domain_res.book_id, - book_title=domain_res.book_title, - code=domain_res.code, - category_name=domain_res.category_name, - subtitle=domain_res.subtitle, - author=domain_res.author, - publisher=domain_res.publisher, - publication_year=domain_res.publication_year, - image_url=domain_res.image_url, - version=domain_res.version, - major=domain_res.major, - language=domain_res.language, - book_status=domain_res.book_status, - donor_name=domain_res.donor_name, - created_at=domain_res.created_at, - updated_at=domain_res.updated_at - ) - return result - - -@router.put( - "/{book_id}", - summary="관리자 도서 정보 수정", - response_model=RouteResAdminPutBook, - status_code=status.HTTP_200_OK -) -async def update_admin_book( - book_id: int, - request: RouteReqAdminPutBook, - db: Session = Depends(get_db), -): - domain_req = DomainReqAdminPutBook( - book_id= book_id, - book_title = request.book_title, - code=request.code, - category_name = request.category_name, - subtitle=request.subtitle, - author=request.author, - publisher=request.publisher, - publication_year=request.publication_year, - image_url = request.image_url, - version = request.version, - major = request.major, - language=request.language, - book_status=request.book_status, - donor_name = request.donor_name - ) - domain_res = await service_admin_update_book(domain_req, db) - result = RouteResAdminPutBook( - book_id=domain_res.book_id, - book_title=domain_res.book_title, - code=domain_res.code, - category_name=domain_res.category_name, - subtitle=domain_res.subtitle, - author=domain_res.author, - publisher=domain_res.publisher, - publication_year=domain_res.publication_year, - image_url=domain_res.image_url, - version=domain_res.version, - major=domain_res.major, - language=domain_res.language, - book_status=domain_res.book_status, - donor_name=domain_res.donor_name, - created_at=domain_res.created_at, - updated_at=domain_res.updated_at - ) - return result - -@router.delete( - "/{book_id}", - summary="관리자 도서 정보 등록", - status_code=status.HTTP_204_NO_CONTENT -) -async def delete_admin_book( - book_id: int, - db: Session = Depends(get_db), -): - domain_req = DomainReqAdminDelBook( - book_id=book_id - ) - await service_admin_delete_book(domain_req, db) - return diff --git a/src/routes/admin/book_route.py b/src/routes/admin/book_route.py index 4433ffa7..eeb6b53b 100644 --- a/src/routes/admin/book_route.py +++ b/src/routes/admin/book_route.py @@ -4,8 +4,16 @@ from sqlalchemy.orm import Session from dependencies import get_current_admin, get_db -from domain.services.admin.book_service import service_admin_read_books, service_admin_search_books -from routes.admin.response.book_response import RouteResAdminGetBookList +from domain.schemas.book_schemas import DomainReqAdminDelBook, DomainReqAdminPostBook, DomainReqAdminPutBook +from domain.services.admin.book_service import ( + service_admin_create_book, + service_admin_delete_book, + service_admin_read_books, + service_admin_search_books, + service_admin_update_book, +) +from routes.admin.request.book_request import RouteReqAdminPostBook, RouteReqAdminPutBook +from routes.admin.response.book_response import RouteResAdminGetBookList, RouteResAdminPostBook, RouteResAdminPutBook router = APIRouter( prefix="/admin/books", @@ -13,7 +21,6 @@ dependencies=[Depends(get_current_admin)] ) - @router.get( "/search", response_model=RouteResAdminGetBookList, @@ -37,7 +44,6 @@ async def search_books( return_status: Annotated[ bool, Query(description="반납 여부", example=False) ] = None, - current_user=Depends(get_current_admin) ): response = await service_admin_search_books( @@ -65,7 +71,6 @@ async def search_books( ) async def get_all_books( db: Session = Depends(get_db), - current_user=Depends(get_current_admin) ): response = await service_admin_read_books( db=db @@ -77,3 +82,114 @@ async def get_all_books( ) return result + + +@router.post( + "/", + summary="관리자 도서 정보 등록", + response_model=RouteResAdminPostBook, + status_code=status.HTTP_201_CREATED +) +async def create_admin_book( + request: RouteReqAdminPostBook, + db: Session = Depends(get_db), +): + domain_req = DomainReqAdminPostBook( + book_title = request.book_title, + code=request.code, + category_name = request.category_name, + subtitle=request.subtitle, + author=request.author, + publisher=request.publisher, + publication_year=request.publication_year, + image_url = request.image_url, + version = request.version, + major = request.major, + language=request.language, + book_status=request.book_status, + donor_name = request.donor_name + ) + domain_res = await service_admin_create_book(domain_req, db) + result = RouteResAdminPostBook( + book_id=domain_res.book_id, + book_title=domain_res.book_title, + code=domain_res.code, + category_name=domain_res.category_name, + subtitle=domain_res.subtitle, + author=domain_res.author, + publisher=domain_res.publisher, + publication_year=domain_res.publication_year, + image_url=domain_res.image_url, + version=domain_res.version, + major=domain_res.major, + language=domain_res.language, + book_status=domain_res.book_status, + donor_name=domain_res.donor_name, + created_at=domain_res.created_at, + updated_at=domain_res.updated_at + ) + return result + + +@router.put( + "/{book_id}", + summary="관리자 도서 정보 수정", + response_model=RouteResAdminPutBook, + status_code=status.HTTP_200_OK +) +async def update_admin_book( + book_id: int, + request: RouteReqAdminPutBook, + db: Session = Depends(get_db), +): + domain_req = DomainReqAdminPutBook( + book_id= book_id, + book_title = request.book_title, + code=request.code, + category_name = request.category_name, + subtitle=request.subtitle, + author=request.author, + publisher=request.publisher, + publication_year=request.publication_year, + image_url = request.image_url, + version = request.version, + major = request.major, + language=request.language, + book_status=request.book_status, + donor_name = request.donor_name + ) + domain_res = await service_admin_update_book(domain_req, db) + result = RouteResAdminPutBook( + book_id=domain_res.book_id, + book_title=domain_res.book_title, + code=domain_res.code, + category_name=domain_res.category_name, + subtitle=domain_res.subtitle, + author=domain_res.author, + publisher=domain_res.publisher, + publication_year=domain_res.publication_year, + image_url=domain_res.image_url, + version=domain_res.version, + major=domain_res.major, + language=domain_res.language, + book_status=domain_res.book_status, + donor_name=domain_res.donor_name, + created_at=domain_res.created_at, + updated_at=domain_res.updated_at + ) + return result + +@router.delete( + "/{book_id}", + summary="관리자 도서 정보 삭제", + status_code=status.HTTP_204_NO_CONTENT +) +async def delete_admin_book( + book_id: int, + db: Session = Depends(get_db), +): + domain_req = DomainReqAdminDelBook( + book_id=book_id + ) + await service_admin_delete_book(domain_req, db) + return diff --git a/src/routes/admin/bookreqeust_route.py b/src/routes/admin/bookreqeust_route.py new file mode 100644 index 00000000..40bf9184 --- /dev/null +++ b/src/routes/admin/bookreqeust_route.py @@ -0,0 +1,79 @@ +from fastapi import APIRouter, Depends, Query, status +from sqlalchemy.orm import Session + +from dependencies import get_current_admin, get_db +from domain.schemas.bookrequest_schemas import DomainReqAdminPutBookRequest +from domain.services.admin.bookrequest_service import ( + service_admin_delete_bookrequest, + service_admin_read_bookrequest, + service_admin_update_bookrequest, +) +from routes.admin.request.bookrequest_request import RouteReqAdminPutBookRequest +from routes.admin.response.bookrequest_response import RouteResAdminGetBookRequestList, RouteResAdminPutBookRequest + +router = APIRouter( + prefix="/admin/book-requests", + tags=["admin/book-requests"], + dependencies=[Depends(get_current_admin)] +) + +@router.get( + "", + response_model= RouteResAdminGetBookRequestList, + status_code = status.HTTP_200_OK, + summary="관리자 도서 구매 요청 목록 조회" +) +async def admin_read_bookRequest( + db: Session = Depends(get_db), + page: int = Query(1, gt=0), + limit: int = Query(10, gt=0), +): + domain_result = await service_admin_read_bookrequest(db=db, page=page, limit=limit) + result = RouteResAdminGetBookRequestList( + data=domain_result.data, + count=len(domain_result.data), + total=domain_result.total + ) + return result + +@router.put( + "/{request_id}", + response_model=RouteResAdminPutBookRequest, + status_code=status.HTTP_200_OK, + summary="관리자 도서 구매 요청 상태 수정" +) +async def admin_update_bookrequest( + request_id: int, + request: RouteReqAdminPutBookRequest, + db: Session = Depends(get_db) +): + domain_req = DomainReqAdminPutBookRequest( + request_id = request_id, + processing_status = request.processing_status, + reject_reason = request.reject_reason + ) + domain_res = await service_admin_update_bookrequest(db=db, request=domain_req) + result = RouteResAdminPutBookRequest( + user_id=domain_res.user_id, + request_id=domain_res.request_id, + book_title=domain_res.book_title, + publication_year=domain_res.publication_year, + request_link=domain_res.request_link, + reason=domain_res.reason, + processing_status=domain_res.processing_status, + request_date=domain_res.request_date, + reject_reason=domain_res.reject_reason + ) + return result + +@router.delete( + "/{request_id}", + status_code=status.HTTP_204_NO_CONTENT, + summary="관리자 도서 구매 요청 삭제" +) +async def admin_delete_bookrequest( + request_id: int, + db: Session = Depends(get_db) +): + await service_admin_delete_bookrequest(request_id=request_id, db=db) + return diff --git a/src/routes/admin/loan_route.py b/src/routes/admin/loan_route.py index 63165dc0..aa487a21 100644 --- a/src/routes/admin/loan_route.py +++ b/src/routes/admin/loan_route.py @@ -1,10 +1,15 @@ from typing import Annotated -from fastapi import APIRouter, Depends, Query, status +from fastapi import APIRouter, Depends, Path, Query, status from sqlalchemy.orm import Session from dependencies import get_current_admin, get_db -from domain.services.admin.loan_service import service_admin_read_loans, service_admin_search_loans +from domain.schemas.loan_schemas import DomainResGetLoan +from domain.services.admin.loan_service import ( + service_admin_read_loans, + service_admin_search_loans, + service_admin_toggle_loan_return, +) from routes.admin.response.loan_response import RouteResAdminGetLoanList router = APIRouter( @@ -13,6 +18,23 @@ dependencies=[Depends(get_current_admin)] ) + +@router.put( + "/return/{loan_id}", + response_model=DomainResGetLoan, + status_code=status.HTTP_200_OK, + summary="관리자의 대출 반납 상태 수정" +) +async def toggle_loan( + loan_id: Annotated[int, Path(description="대출 정보 id", gt=0)], + db: Session = Depends(get_db), + current_admin=Depends(get_current_admin), +): + response = await service_admin_toggle_loan_return(loan_id, db) + + return response + + @router.get( "/search", response_model=RouteResAdminGetLoanList, @@ -72,3 +94,4 @@ async def get_all_loans( ) return result + diff --git a/src/routes/admin/admin_notice_route.py b/src/routes/admin/notice_route.py similarity index 97% rename from src/routes/admin/admin_notice_route.py rename to src/routes/admin/notice_route.py index 487d3ee6..5dea5730 100644 --- a/src/routes/admin/admin_notice_route.py +++ b/src/routes/admin/notice_route.py @@ -35,7 +35,6 @@ async def get_all_notices( page: int = Query(1, ge=1), limit: int = Query(7, le=50), db: Session=Depends(get_db), - current_user=Depends(get_current_admin) ): domain_res, total = await service_admin_read_notices(page, limit, db) response = RouteResAdminGetNoticeList( @@ -57,7 +56,6 @@ async def get_all_notices( async def get_notice( notice_id: int, db: Session=Depends(get_db), - current_user=Depends(get_current_admin) ): domain_res = await service_admin_read_notice(notice_id, db) response = RouteResAdminGetNotice( diff --git a/src/routes/admin/request/bookrequest_request.py b/src/routes/admin/request/bookrequest_request.py new file mode 100644 index 00000000..a98b84ef --- /dev/null +++ b/src/routes/admin/request/bookrequest_request.py @@ -0,0 +1,29 @@ +from fastapi import HTTPException, status +from pydantic import BaseModel, Field, field_validator, model_validator +from typing_extensions import Self + +from domain.enums.status import BookRequestStatus + + +class RouteReqAdminPutBookRequest(BaseModel): + processing_status: int = Field(0, title="processing_status", description="처리 상태", example=0) + reject_reason: str | None = Field(None, title="reject_reason", description="거절 사유", example="Not available") + + @field_validator("processing_status") + @classmethod + def valid_status(cls, processing_status): + if not BookRequestStatus.is_valid_enum_value(processing_status): + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="Invalid processing status" + ) + return processing_status + + @model_validator(mode='after') + def check_reject_reason(self) -> Self: + if self.processing_status == BookRequestStatus.REJECTED() and not self.reject_reason: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="Reject reason is missed" + ) + return self diff --git a/src/routes/admin/request/user_request.py b/src/routes/admin/request/user_request.py new file mode 100644 index 00000000..8c55be1c --- /dev/null +++ b/src/routes/admin/request/user_request.py @@ -0,0 +1,20 @@ +from datetime import date, datetime + +from fastapi import HTTPException, status +from pydantic import BaseModel, Field, model_validator + + +class RouteReqAdminPutUser(BaseModel): + user_status: bool | None = Field(None, title="is_active", description="회원 상태(대출 가능 여부)", examples=[True]) + admin_status: bool | None = Field(None, title="admin_status", description="관리자 권한 상태", examples=[True]) + expiration_date: date | None = Field(None, title="expiration_date", description="관리자 권한 만료일", \ + examples=[datetime(2025, 7, 2).date()]) + @model_validator(mode="after") + def check_at_least_one_field(cls, values): + # If all three fields are None, raise an error + if all(values is None for values in RouteReqAdminPutUser.model_fields): + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="At least one field must be provided." + ) + return values diff --git a/src/routes/admin/response/bookrequest_response.py b/src/routes/admin/response/bookrequest_response.py new file mode 100644 index 00000000..3575d35a --- /dev/null +++ b/src/routes/admin/response/bookrequest_response.py @@ -0,0 +1,34 @@ + +from datetime import date + +from pydantic import BaseModel, Field + +from domain.schemas.bookrequest_schemas import DomainResBookRequest + + +class RouteResAdminGetBookRequest(BaseModel): + user_id: int = Field(title="user_id", description="도서 구매를 요청한 사용자 ID", example=1, gt=0) + request_id: int = Field(title="request_id", description="도서 구매 요청 ID", example=1, gt=0) + book_title: str = Field(title="book_title", description="책 제목", example="FastAPI Tutorial") + publication_year: int = Field(title="publication_year", description="출판년도", example=2022, gt=0) + request_link: str = Field(title="request_link", description="요청 링크", example="https://example.com/request") + reason: str = Field(title="reason", description="이유", example="Need for study") + processing_status: int = Field(0, title="processing_status", description="처리 상태", example=0, ge=0, le=3) + request_date: date = Field(title="request_date", description="요청 일자", example=date.today()) + reject_reason: str | None = Field(None, title="reject_reason", description="거절 사유", example="Not available") + +class RouteResAdminGetBookRequestList(BaseModel): + data: list[DomainResBookRequest] + count: int + total: int = Field(0, title="total", description="총 도서 구매 요청", example=0, ge=0) + +class RouteResAdminPutBookRequest(BaseModel): + user_id: int = Field(title="user_id", description="도서 구매를 요청한 사용자 ID", example=1, gt=0) + request_id: int = Field(title="request_id", description="도서 구매 요청 ID", example=1, gt=0) + book_title: str = Field(title="book_title", description="책 제목", example="FastAPI Tutorial") + publication_year: int = Field(title="publication_year", description="출판년도", example=2022, gt=0) + request_link: str = Field(title="request_link", description="요청 링크", example="https://example.com/request") + reason: str = Field(title="reason", description="이유", example="Need for study") + processing_status: int = Field(0, title="processing_status", description="처리 상태", example=0) + request_date: date = Field(title="request_date", description="요청 일자", example=date.today()) + reject_reason: str | None = Field(None, title="reject_reason", description="거절 사유", example="Not available") diff --git a/src/routes/admin/response/loan_response.py b/src/routes/admin/response/loan_response.py index 969890b3..90837835 100644 --- a/src/routes/admin/response/loan_response.py +++ b/src/routes/admin/response/loan_response.py @@ -1,9 +1,9 @@ from pydantic import BaseModel -from domain.schemas.loan_schemas import DomainAdminGetLoanItem +from domain.schemas.loan_schemas import DomainAdminGetLoan class RouteResAdminGetLoanList(BaseModel): - data: list[DomainAdminGetLoanItem] + data: list[DomainAdminGetLoan] count: int diff --git a/src/routes/admin/response/setting_response.py b/src/routes/admin/response/setting_response.py new file mode 100644 index 00000000..4328b941 --- /dev/null +++ b/src/routes/admin/response/setting_response.py @@ -0,0 +1,54 @@ +from datetime import datetime + +from pydantic import BaseModel, Field + + +class ServiceDate(BaseModel): + ''' + # 서비스 기간 예시 + service = ServiceDate(start_date="2024-01-01", end_date="2024-12-31") + + # 서비스 기간 내에 있는지 확인 + print(service.is_service_duration()) # True 또는 False + + # Pydantic 모델로부터 `start_datetime`과 `end_datetime`에 접근 가능 + print(service.start_datetime) # 2024-01-01 00:00:00 + print(service.end_datetime) # 2024-12-31 23:59:59 + ''' + + start_date: str = Field(..., title="도서 서비스 시작일", description="도서 대출 및 도서 구매 신청 시작일", \ + examples=["2024-01-01", "2024-01-01 00:00:00"]) + end_date: str = Field(..., title="도서 서비스 종료일", description="도서 대출 및 도서 구매 신청 종료일", \ + examples=["2024-12-31", "2024-12-31 23:59:59"]) + + # @property를 사용하여 날짜 객체로 변환 + @property + def start_datetime(self) -> datetime: + return datetime.strptime(self.start_date, "%Y-%m-%d").replace(hour=0, minute=0, second=0) + + @property + def end_datetime(self) -> datetime: + return datetime.strptime(self.end_date, "%Y-%m-%d").replace(hour=23, minute=59, second=59) + + def is_service_duration(self) -> bool: + """현재 시간 기준으로 서비스 기간 내에 있는지 확인""" + current_datetime = datetime.now() + return self.start_datetime <= current_datetime <= self.end_datetime + +class ExtendSetting(BaseModel): + extend_days : int = Field(..., title="연장일수", description="1회 연장 시 추가되는 대출 기간", \ + examples=[3, 7, 10], ge=1) + extend_max_count : int = Field(..., title="최대 연장횟수", description="대출 1건 당 가능한 연장 횟수", \ + examples=[3, 7, 10], ge=1) + +class LoanSetting(BaseModel): + loan_days : int = Field(..., title="대출 기간", description="1회 당 대출 기간", \ + examples=[3, 7, 10], ge=1) + loan_max_count : int = Field(..., title="대출 가능 권수", description="1회당 대출 가능 권수", \ + examples=[3, 7, 10], ge=1) + +class BookRequestSetting(BaseModel): + request_max_count : int = Field(..., title="도서 구매 최대 권수", description="1인당 구매 가능한 최대 도서 권수", \ + examples=[3, 7, 10], ge=1) + request_max_price : int = Field(..., title="도서 구매 최대 가격", description="1인당 구매 가능한 최고 도서 가격", \ + examples=[15000, 30000, 50000], ge=1) diff --git a/src/routes/admin/response/user_response.py b/src/routes/admin/response/user_response.py index cb659fd9..303e035f 100644 --- a/src/routes/admin/response/user_response.py +++ b/src/routes/admin/response/user_response.py @@ -1,4 +1,6 @@ -from pydantic import BaseModel +from datetime import date + +from pydantic import BaseModel, Field from domain.schemas.user_schemas import DomainAdminGetUserItem @@ -6,3 +8,13 @@ class RouteResAdminGetUserList(BaseModel): data: list[DomainAdminGetUserItem] count: int + +class RouteResAdminPutUser(BaseModel): + user_id: int = Field(title="user_id", description="유저 고유 ID", example=1111, gt=0) + auth_id: str = Field(title="auth_id", description="로그인 ID", example="gildong1") + email: str = Field(title="email", description="이메일 주소", example="KUCC@korea.ac.kr") + user_name: str = Field(title="user_name", description="사용자 이름", example="홍길동") + is_active: bool = Field(title="is_active", description="활동 상태", examples=[True]) + is_admin: bool = Field(title="is_admin", description="관리자 권환", examples=[False]) + expiration_date : date | None = Field(title="expiration_date", description="관리자 권한 만료일, \ + 기본적으로 권한 부여일로부터 1년", examples=["2025-07-02"]) diff --git a/src/routes/admin/user_route.py b/src/routes/admin/user_route.py index b0d8fc3a..2312b775 100644 --- a/src/routes/admin/user_route.py +++ b/src/routes/admin/user_route.py @@ -1,11 +1,18 @@ from typing import Annotated -from fastapi import APIRouter, Depends, Query, status +from fastapi import APIRouter, Depends, Path, Query, status from sqlalchemy.orm import Session from dependencies import get_current_admin, get_db -from domain.services.admin.user_service import service_admin_read_users, service_admin_search_users -from routes.admin.response.user_response import RouteResAdminGetUserList +from domain.schemas.user_schemas import DomainReqAdminDelUser, DomainReqAdminPutUser +from domain.services.admin.user_service import ( + service_admin_delete_user, + service_admin_read_users, + service_admin_search_users, + service_admin_update_user, +) +from routes.admin.request.user_request import RouteReqAdminPutUser +from routes.admin.response.user_response import RouteResAdminGetUserList, RouteResAdminPutUser router = APIRouter( prefix="/admin/users", @@ -13,7 +20,6 @@ dependencies=[Depends(get_current_admin)] ) - @router.get( "/search", response_model=RouteResAdminGetUserList, @@ -68,3 +74,54 @@ async def get_all_users( ) return result + +@router.put( + "/{user_id}", + summary="관리자의 회원 상태 및 권한 수정", + status_code=status.HTTP_200_OK, + response_model=RouteResAdminPutUser +) +async def update_admin_user( + user_id : int, + request: RouteReqAdminPutUser, + db: Session = Depends(get_db), +): + domain_request = DomainReqAdminPutUser( + user_id=user_id, + user_status=request.user_status, + admin_status=request.admin_status, + expiration_date=request.expiration_date + ) + + domain_response = await service_admin_update_user(request=domain_request, db=db) + + response = RouteResAdminPutUser( + user_id=domain_response.user_id, + auth_id=domain_response.auth_id, + email=domain_response.email, + user_name=domain_response.user_name, + is_active=domain_response.is_active, + is_admin=domain_response.is_admin, + expiration_date=domain_response.expiration_date + ) + + return response + + + +@router.delete( + "/{user_id}", + status_code=status.HTTP_204_NO_CONTENT, + summary="회원 탈퇴", +) +async def delete_user( + user_id: Annotated[int, Path(description="삭제할 사용자 ID")], + db: Session = Depends(get_db), + current_user=Depends(get_current_admin) +): + domain_req = DomainReqAdminDelUser( + user_id=user_id + ) + await service_admin_delete_user(domain_req, db) + return + diff --git a/src/routes/book_review_route.py b/src/routes/book_review_route.py index 5efc5d55..30adce4a 100644 --- a/src/routes/book_review_route.py +++ b/src/routes/book_review_route.py @@ -1,7 +1,7 @@ from fastapi import APIRouter, Depends, Query, status from sqlalchemy.orm import Session -from dependencies import get_current_active_user, get_db +from dependencies import get_current_user, get_db from domain.schemas.book_review_schemas import ( DomainReqPostReview, DomainReqPutReview, @@ -21,7 +21,6 @@ router = APIRouter( prefix="/reviews", tags=["reviews"], - dependencies=[Depends(get_current_active_user)] ) @@ -34,7 +33,6 @@ async def get_all_reviews_by_book_id( book_id: int = Query(alias="books"), db: Session = Depends(get_db), - current_user=Depends(get_current_active_user) ): domain_res = await service_read_reviews_by_book_id(book_id, db) @@ -50,10 +48,11 @@ async def get_all_reviews_by_book_id( response_model=RouteResGetReviewList, status_code=status.HTTP_200_OK, summary="회원의 전체 리뷰 목록 조회", + dependencies=[Depends(get_current_user)] ) async def get_all_user_reviews( db: Session = Depends(get_db), - current_user=Depends(get_current_active_user) + current_user=Depends(get_current_user) ): domain_res = await service_read_reviews_by_user_id(current_user.id, db) @@ -68,12 +67,13 @@ async def get_all_user_reviews( "", response_model=DomainResPostReview, status_code=status.HTTP_201_CREATED, - summary="리뷰 작성" + summary="리뷰 작성", + dependencies=[Depends(get_current_user)] ) async def create_review( route_req: RouteReqPostReview, db: Session = Depends(get_db), - current_user=Depends(get_current_active_user) + current_user=Depends(get_current_user) ): domain_req = DomainReqPostReview( user_id=current_user.id, @@ -87,12 +87,13 @@ async def create_review( @router.delete( "/{review_id}", status_code=status.HTTP_204_NO_CONTENT, - summary="리뷰 삭제" + summary="리뷰 삭제", + dependencies=[Depends(get_current_user)] ) async def delete_reivew( review_id: int, db: Session = Depends(get_db), - current_user=Depends(get_current_active_user) + current_user=Depends(get_current_user) ): await service_delete_review(review_id, current_user.id, db) return @@ -101,13 +102,14 @@ async def delete_reivew( "/{review_id}", response_model=DomainResGetReviewItem, status_code=status.HTTP_200_OK, - summary="리뷰 수정" + summary="리뷰 수정", + dependencies=[Depends(get_current_user)] ) async def update_review( review_id: int, review_update_data: RouteReqPutReview, db: Session = Depends(get_db), - current_user=Depends(get_current_active_user) + current_user=Depends(get_current_user) ): domain_req = DomainReqPutReview( review_id=review_id, diff --git a/src/routes/bookrequest_route.py b/src/routes/bookrequest_route.py index 80c3c29c..d3e19860 100644 --- a/src/routes/bookrequest_route.py +++ b/src/routes/bookrequest_route.py @@ -1,32 +1,31 @@ from fastapi import APIRouter, Depends, status from sqlalchemy.orm import Session -from dependencies import get_current_active_user, get_db +from dependencies import get_current_active_user, get_current_user, get_db from domain.schemas.bookrequest_schemas import ( DomainReqDelBookRequest, - DomainReqGetBookRequest, DomainReqPostBookRequest, DomainReqPutBookRequest, ) from domain.services.bookrequest_service import ( service_create_bookrequest, service_delete_bookrequest, - service_read_bookrequest_list, service_update_bookrequest, ) from routes.request.bookrequest_request import RouteReqPostBookRequest, RouteReqPutBookRequest -from routes.response.bookrequest_response import RouteResBookRequest, RouteResBookRequestList, RouteResPostBookRequest +from routes.response.bookrequest_response import RouteResBookRequest, RouteResPostBookRequest router = APIRouter( prefix="/book-requests", tags=["book-requests"], - dependencies=[Depends(get_current_active_user)] + dependencies=[Depends(get_current_user)] ) @router.post( "", response_model=RouteResPostBookRequest, status_code=status.HTTP_201_CREATED, + dependencies=[Depends(get_current_active_user)], summary="구매 요청" ) async def create_book_request( @@ -58,38 +57,8 @@ async def create_book_request( return result -@router.get( - "/my-requests", - summary="user의 도서 구매 요청 목록 조회", - response_model=RouteResBookRequestList, - status_code=status.HTTP_200_OK, -) -async def get_user_bookrequests( - db: Session = Depends(get_db), current_user=Depends(get_current_active_user) -): - domain_req = DomainReqGetBookRequest(user_id=current_user.id) - domain_res = await service_read_bookrequest_list(domain_req, db) - converted_res = [ - RouteResBookRequest( - user_id=item.user_id, - request_id=item.request_id, - book_title=item.book_title, - publication_year=item.publication_year, - request_link=item.request_link, - reason=item.reason, - processing_status=item.processing_status, - request_date=item.request_date, - reject_reason=item.reject_reason, - ) - for item in domain_res - ] - - result = RouteResBookRequestList(data=converted_res, count=len(converted_res)) - return result - - @router.put( - "/users/{request_id}", + "/{request_id}", summary="user의 도서 구매 요청 수정", response_model=RouteResBookRequest, status_code=status.HTTP_200_OK, @@ -98,7 +67,7 @@ async def update_user_bookrequest( request_id: int, request_data: RouteReqPutBookRequest, db: Session = Depends(get_db), - current_user=Depends(get_current_active_user), + current_user=Depends(get_current_user), ): domain_req = DomainReqPutBookRequest( user_id=current_user.id, @@ -130,7 +99,7 @@ async def update_user_bookrequest( status_code=status.HTTP_204_NO_CONTENT, ) async def delete_user_bookrequest( - request_id: int, db: Session = Depends(get_db), current_user=Depends(get_current_active_user) + request_id: int, db: Session = Depends(get_db), current_user=Depends(get_current_user) ) -> None: domain_req = DomainReqDelBookRequest(user_id=current_user.id, request_id=request_id) await service_delete_bookrequest(domain_req, db) diff --git a/src/routes/loan_route.py b/src/routes/loan_route.py index fc46dbfd..b802294a 100644 --- a/src/routes/loan_route.py +++ b/src/routes/loan_route.py @@ -2,7 +2,8 @@ from sqlalchemy.orm import Session from dependencies import get_current_active_user, get_db -from domain.schemas.loan_schemas import DomainReqPostLoan, DomainReqPutLoan, DomainResGetLoanItem + +from domain.schemas.loan_schemas import DomainReqPostLoan, DomainReqPutLoan, DomainResGetLoan from domain.services.loan_service import service_create_loan, service_extend_loan from routes.request.loan_request import RouteReqPostLoan from routes.response.loan_response import RouteResPostLoan @@ -35,7 +36,7 @@ async def create_loan( @router.put( "/{loan_id}/extend", - response_model=DomainResGetLoanItem, + response_model=DomainResGetLoan, status_code=status.HTTP_200_OK, summary="대출 연장", ) diff --git a/src/routes/notice_route.py b/src/routes/notice_route.py index 6f4a9dd3..a963f8ed 100644 --- a/src/routes/notice_route.py +++ b/src/routes/notice_route.py @@ -1,14 +1,13 @@ from fastapi import APIRouter, Depends, Query, status from sqlalchemy.orm import Session -from dependencies import get_current_active_user, get_db +from dependencies import get_db from domain.services.notice_service import service_read_notice, service_read_notices from routes.response.notice_response import RouteResGetNotice, RouteResGetNoticeList router=APIRouter( prefix="/notice", tags=["notice"], - dependencies=[Depends(get_current_active_user)] ) @router.get( @@ -22,7 +21,6 @@ async def get_all_notices( page: int = Query(1, ge=1), limit: int = Query(7, le=50), db: Session=Depends(get_db), - current_user=Depends(get_current_active_user) ): domain_res, total = await service_read_notices(page, limit, db) response = RouteResGetNoticeList( @@ -44,7 +42,6 @@ async def get_all_notices( async def get_notice( notice_id: int, db: Session=Depends(get_db), - current_user=Depends(get_current_active_user) ): domain_res = await service_read_notice(notice_id, db) response = RouteResGetNotice( diff --git a/src/routes/response/bookrequest_response.py b/src/routes/response/bookrequest_response.py index b2e91aed..76ab94f4 100644 --- a/src/routes/response/bookrequest_response.py +++ b/src/routes/response/bookrequest_response.py @@ -18,7 +18,7 @@ class RouteResBookRequest(BaseModel): user_id: int = Field(title="user_id", description="도서 구매를 요청한 사용자 ID", example=1, gt=0) request_id: int = Field(title="request_id", description="도서 구매 요청 ID", example=1, gt=0) book_title: str = Field(title="book_title", description="책 제목", example="FastAPI Tutorial") - publication_year: int = Field(0, title="publication_year", description="출판년도", example=2022, gt=0) + publication_year: int = Field(title="publication_year", description="출판년도", example=2022, gt=0) request_link: str = Field(title="request_link", description="요청 링크", example="https://example.com/request") reason: str = Field(title="reason", description="이유", example="Need for study") processing_status: int = Field(0, title="processing_status", description="처리 상태", example=0, ge=0, le=3) diff --git a/src/routes/user_route.py b/src/routes/user_route.py index ce37c74f..de42a3b0 100644 --- a/src/routes/user_route.py +++ b/src/routes/user_route.py @@ -1,29 +1,31 @@ from fastapi import APIRouter, Depends, status from sqlalchemy.orm import Session -from dependencies import get_current_active_user, get_db +from dependencies import get_current_user, get_db +from domain.schemas.bookrequest_schemas import DomainReqGetBookRequest +from domain.services.bookrequest_service import service_read_bookrequest_list from domain.services.loan_service import service_read_loans_by_user_id from domain.services.user_service import service_read_user, service_update_user +from routes.request.user_request import RouteReqPutUser +from routes.response.bookrequest_response import RouteResBookRequest, RouteResBookRequestList from routes.response.loan_response import RouteResGetLoanList from routes.response.user_response import RouteResGetUser, RouteResPutUser -from routes.request.user_request import RouteReqPutUser router = APIRouter( prefix="/users", tags=["users"], - dependencies=[Depends(get_current_active_user)] + dependencies=[Depends(get_current_user)] ) - @router.get( - "/{user_id}/loans", + "/my-loans", response_model=RouteResGetLoanList, status_code=status.HTTP_200_OK, summary="회원의 전체 대출 목록 조회", ) async def get_all_user_loans( db: Session = Depends(get_db), - current_user=Depends(get_current_active_user) + current_user=Depends(get_current_user) ): result = await service_read_loans_by_user_id(current_user.id, db) @@ -41,7 +43,7 @@ async def get_all_user_loans( ) async def get_user( db: Session = Depends(get_db), - current_user=Depends(get_current_active_user) + current_user=Depends(get_current_user) ): result = await service_read_user(current_user.id, db) @@ -56,6 +58,35 @@ async def get_user( ) return response +@router.get( + "/my-requests", + summary="user의 도서 구매 요청 목록 조회", + response_model=RouteResBookRequestList, + status_code=status.HTTP_200_OK, +) +async def get_user_bookrequests( + db: Session = Depends(get_db), current_user=Depends(get_current_user) +): + domain_req = DomainReqGetBookRequest(user_id=current_user.id) + domain_res = await service_read_bookrequest_list(domain_req, db) + converted_res = [ + RouteResBookRequest( + user_id=item.user_id, + request_id=item.request_id, + book_title=item.book_title, + publication_year=item.publication_year, + request_link=item.request_link, + reason=item.reason, + processing_status=item.processing_status, + request_date=item.request_date, + reject_reason=item.reject_reason, + ) + for item in domain_res + ] + + result = RouteResBookRequestList(data=converted_res, count=len(converted_res)) + return result + @router.put( "/my-info", response_model=RouteResPutUser, @@ -65,7 +96,7 @@ async def get_user( async def put_user( request: RouteReqPutUser, db: Session = Depends(get_db), - current_user=Depends(get_current_active_user) + current_user=Depends(get_current_user) ): result = await service_update_user(current_user.id, db, request)