Skip to content

Commit 245a914

Browse files
Feat #21 - 고객의 반려견 목록과 소유한 티켓 목록 조회 API 구현 (#22)
- 해당 유치원에서 고객에게 등록된 반려동물 목록 조회 기능 구현 - 해당 유치원에서 고객이 소유하고 있는 티켓 목록 조회 기능 구현 Close #21
1 parent 3d5b4d3 commit 245a914

File tree

15 files changed

+376
-13
lines changed

15 files changed

+376
-13
lines changed

.github/dependabot.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,5 @@ updates:
2222
schedule:
2323
interval: "weekly"
2424
target-branch: "dependencies"
25+
ignore:
26+
- "djangorestframework-simplejwt"

config/root_urls.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,15 @@
2323
)
2424
),
2525
),
26+
path(
27+
"guest/api/v1/reservations",
28+
include(
29+
(
30+
"mung_manager.reservations.apis.urls",
31+
"api-reservations",
32+
)
33+
),
34+
),
2635
]
2736

2837
from config.settings.debug_toolbar.setup import DebugToolbarSetup # noqa

mung_manager/commons/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ class SYSTEM_CODE:
9797
# Customer code
9898
UNIQUE_PET_NAME = ("unique_pet_name", "Customer pet names must be unique.")
9999
NOT_FOUND_CUSTOMER = ("not_found_customer", "Customer does not exist.")
100+
INACTIVE_CUSTOMER = ("inactive_customer", "Customer is inactive.")
100101
NOT_FOUND_CUSTOMER_PET = (
101102
"not_found_customer_pet",
102103
"Customer pet does not exist.",

mung_manager/commons/selectors.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
1-
from mung_manager.errors.exceptions import AlreadyExistsException, NotFoundException
1+
from mung_manager.errors.exceptions import (
2+
AlreadyExistsException,
3+
NotFoundException,
4+
PermissionDeniedException,
5+
)
6+
7+
8+
def get_object_or_permission_denied(objects, msg, code):
9+
if objects is None:
10+
if msg:
11+
raise PermissionDeniedException(msg, code)
12+
return objects
213

314

415
def get_object_or_not_found(objects, msg, code):

mung_manager/customers/containers.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from dependency_injector import containers, providers
22

3+
from mung_manager.customers.selectors.customer_pets import CustomerPetSelector
4+
from mung_manager.customers.selectors.customer_tickets import CustomerTicketSelector
35
from mung_manager.customers.selectors.customers import CustomerSelector
46
from mung_manager.customers.services.customers import CustomerService
57

@@ -18,3 +20,5 @@ class CustomerContainer(containers.DeclarativeContainer):
1820
CustomerService,
1921
customer_selector=customer_selector,
2022
)
23+
customer_pet_selector = providers.Factory(CustomerPetSelector)
24+
customer_ticket_selector = providers.Factory(CustomerTicketSelector)

mung_manager/customers/selectors/abstracts.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
from django.db.models.query import QuerySet
55

6-
from mung_manager.customers.models import Customer
6+
from mung_manager.customers.models import Customer, CustomerPet
77
from mung_manager.errors.exceptions import NotImplementedException
88

99

@@ -23,3 +23,21 @@ def get_queryset_by_phone_number_and_user_id_is_null(
2323
@abstractmethod
2424
def exists_by_user_and_pet_kindergarden_id(self, user, pet_kindergarden_id: int) -> bool:
2525
raise NotImplementedException()
26+
27+
@abstractmethod
28+
def get_by_user_and_pet_kindergarden_id_for_active_customer(
29+
self, user, pet_kindergarden_id: int
30+
) -> Optional[Customer]:
31+
raise NotImplementedException()
32+
33+
34+
class AbstractCustomerPetSelector(ABC):
35+
@abstractmethod
36+
def get_queryset_by_customer(self, customer: Customer) -> QuerySet[CustomerPet]:
37+
raise NotImplementedException()
38+
39+
40+
class AbstractCustomerTicketSelector(ABC):
41+
@abstractmethod
42+
def get_queryset_by_customer(self, customer: Customer) -> dict[str, list]:
43+
raise NotImplementedException()
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from django.db.models.query import QuerySet
2+
3+
from mung_manager.customers.models import Customer, CustomerPet
4+
from mung_manager.customers.selectors.abstracts import AbstractCustomerPetSelector
5+
6+
7+
class CustomerPetSelector(AbstractCustomerPetSelector):
8+
"""
9+
이 클래스는 고객 반려동물을 DB에서 PULL하는 비즈니스 로직을 담당합니다.
10+
"""
11+
12+
def get_queryset_by_customer(self, customer: Customer) -> QuerySet[CustomerPet]:
13+
"""
14+
이 함수는 고객 객체로 해당 반려동물 유치원에 속한 고객의 반려동물 목록을 조회합니다.
15+
16+
Args:
17+
customer (Customer): 고객 객체
18+
19+
Returns:
20+
QuerySet[CustomerPet]: 등록된 반려동물 목록이 존재하지 않으면 빈 쿼리셋을 반환합니다.
21+
"""
22+
return CustomerPet.objects.filter(customer=customer, is_deleted=False)
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
from django.utils import timezone
2+
3+
from mung_manager.customers.models import Customer, CustomerTicket
4+
from mung_manager.customers.selectors.abstracts import AbstractCustomerTicketSelector
5+
from mung_manager.tickets.enums import TicketType
6+
7+
8+
class CustomerTicketSelector(AbstractCustomerTicketSelector):
9+
"""
10+
이 클래스는 고객 티켓을 DB에서 PULL하는 비즈니스 로직을 담당합니다.
11+
"""
12+
13+
def get_queryset_by_customer(self, customer: Customer) -> dict[str, list]:
14+
"""
15+
고객 객체로 해당 고객이 소유하고 있는 만료되지 않은 티켓의 목록을 조회합니다.
16+
17+
Args:
18+
customer (Customer): 고객 객체
19+
20+
Returns:
21+
QuerySet: 소유하고 있는 티켓이 존재하지 않으면 빈 쿼리셋을 반환합니다.
22+
"""
23+
customer_tickets = CustomerTicket.objects.filter(
24+
customer=customer,
25+
expired_at__gte=timezone.now(),
26+
unused_count__gt=0,
27+
).select_related("ticket")
28+
29+
time_customer_tickets = []
30+
all_day_customer_tickets = []
31+
hotel_customer_tickets = []
32+
for customer_ticket in customer_tickets:
33+
ticket_type = customer_ticket.ticket.ticket_type
34+
if ticket_type == TicketType.TIME.value:
35+
time_customer_tickets.append(customer_ticket)
36+
elif ticket_type == TicketType.ALL_DAY.value:
37+
all_day_customer_tickets.append(customer_ticket)
38+
elif ticket_type == TicketType.HOTEL.value:
39+
hotel_customer_tickets.append(customer_ticket)
40+
41+
return {
42+
"time": time_customer_tickets,
43+
"all_day": all_day_customer_tickets,
44+
"hotel": hotel_customer_tickets,
45+
}

mung_manager/customers/selectors/customers.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,21 @@ def exists_by_user_and_pet_kindergarden_id(self, user, pet_kindergarden_id: int)
5252
bool: 사용자가 해당 유치원에 등록되어 있으면 True, 그렇지 않으면 False.
5353
"""
5454
return Customer.objects.filter(user=user, pet_kindergarden=pet_kindergarden_id).exists()
55+
56+
def get_by_user_and_pet_kindergarden_id_for_active_customer(
57+
self, user, pet_kindergarden_id: int
58+
) -> Optional[Customer]:
59+
"""
60+
사용자 객체와 반려동물 유치원 아이디로 등록된 활성화 고객을 조회합니다.
61+
62+
Args:
63+
user (User): 확인할 사용자 객체
64+
pet_kindergarden_id (int): 반려동물 유치원 아이디
65+
66+
Returns:
67+
Optional[Customer]: 등록된 활성화 고객이 존재하지 않으면 None을 반환
68+
"""
69+
try:
70+
return Customer.objects.filter(user=user, pet_kindergarden_id=pet_kindergarden_id, is_active=True).get()
71+
except Customer.DoesNotExist:
72+
return None
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
from drf_spectacular.types import OpenApiTypes
2+
from drf_spectacular.utils import OpenApiResponse, extend_schema
3+
from rest_framework import status
4+
5+
from mung_manager.commons.base.api_managers import BaseAPIManager
6+
from mung_manager.reservations.apis.apis import (
7+
ReservationCustomerPetListAPI,
8+
ReservationCustomerTicketListAPI,
9+
)
10+
from mung_manager.schemas.errors.authentications import (
11+
ErrorAuthenticationPasswordChangedSchema,
12+
ErrorAuthenticationUserDeletedSchema,
13+
ErrorAuthenticationUserInactiveSchema,
14+
ErrorAuthenticationUserNotFoundSchema,
15+
ErrorAuthorizationHeaderSchema,
16+
ErrorTokenIdentificationSchema,
17+
)
18+
from mung_manager.schemas.errors.commons import (
19+
ErrorAuthenticationFailedSchema,
20+
ErrorInvalidTokenSchema,
21+
ErrorNotAuthenticatedSchema,
22+
ErrorPermissionDeniedSchema,
23+
ErrorUnknownServerSchema,
24+
)
25+
from mung_manager.schemas.errors.customers import ErrorCustomerPermissionDeniedSchema
26+
27+
28+
class ReservationCustomerPetListAPIManager(BaseAPIManager):
29+
VIEWS_BY_METHOD = {
30+
"GET": ReservationCustomerPetListAPI.as_view,
31+
}
32+
33+
@extend_schema(
34+
tags=["예약"],
35+
summary="고객의 반려견 목록 조회",
36+
description="""
37+
Rogic
38+
- 반려동물 유치원에 등록된 고객의 반려견 목록을 조회합니다.
39+
""",
40+
responses={
41+
status.HTTP_200_OK: VIEWS_BY_METHOD["GET"]().cls.OutputSerializer,
42+
status.HTTP_401_UNAUTHORIZED: OpenApiResponse(
43+
response=OpenApiTypes.OBJECT,
44+
examples=[
45+
ErrorAuthenticationFailedSchema,
46+
ErrorNotAuthenticatedSchema,
47+
ErrorInvalidTokenSchema,
48+
ErrorAuthorizationHeaderSchema,
49+
ErrorAuthenticationPasswordChangedSchema,
50+
ErrorAuthenticationUserDeletedSchema,
51+
ErrorAuthenticationUserInactiveSchema,
52+
ErrorAuthenticationUserNotFoundSchema,
53+
ErrorTokenIdentificationSchema,
54+
],
55+
),
56+
status.HTTP_403_FORBIDDEN: OpenApiResponse(
57+
response=OpenApiTypes.OBJECT,
58+
examples=[
59+
ErrorPermissionDeniedSchema,
60+
ErrorCustomerPermissionDeniedSchema,
61+
],
62+
),
63+
status.HTTP_500_INTERNAL_SERVER_ERROR: OpenApiResponse(
64+
response=OpenApiTypes.OBJECT, examples=[ErrorUnknownServerSchema]
65+
),
66+
},
67+
)
68+
def get(self, request, *args, **kwargs):
69+
return self.VIEWS_BY_METHOD["GET"]()(request, *args, **kwargs)
70+
71+
72+
class ReservationCustomerTicketListAPIManager(BaseAPIManager):
73+
VIEWS_BY_METHOD = {
74+
"GET": ReservationCustomerTicketListAPI.as_view,
75+
}
76+
77+
@extend_schema(
78+
tags=["예약"],
79+
summary="고객의 잔여 티켓 목록 조회",
80+
description="""
81+
Rogic
82+
- 반려동물 유치원에 등록된 고객의 잔여 티켓 목록을 조회합니다.
83+
""",
84+
responses={
85+
status.HTTP_200_OK: VIEWS_BY_METHOD["GET"]().cls.OutputSerializer,
86+
status.HTTP_401_UNAUTHORIZED: OpenApiResponse(
87+
response=OpenApiTypes.OBJECT,
88+
examples=[
89+
ErrorAuthenticationFailedSchema,
90+
ErrorNotAuthenticatedSchema,
91+
ErrorInvalidTokenSchema,
92+
ErrorAuthorizationHeaderSchema,
93+
ErrorAuthenticationPasswordChangedSchema,
94+
ErrorAuthenticationUserDeletedSchema,
95+
ErrorAuthenticationUserInactiveSchema,
96+
ErrorAuthenticationUserNotFoundSchema,
97+
ErrorTokenIdentificationSchema,
98+
],
99+
),
100+
status.HTTP_403_FORBIDDEN: OpenApiResponse(
101+
response=OpenApiTypes.OBJECT,
102+
examples=[
103+
ErrorPermissionDeniedSchema,
104+
ErrorCustomerPermissionDeniedSchema,
105+
],
106+
),
107+
status.HTTP_500_INTERNAL_SERVER_ERROR: OpenApiResponse(
108+
response=OpenApiTypes.OBJECT, examples=[ErrorUnknownServerSchema]
109+
),
110+
},
111+
)
112+
def get(self, request, *args, **kwargs):
113+
return self.VIEWS_BY_METHOD["GET"]()(request, *args, **kwargs)

0 commit comments

Comments
 (0)