fruites_store_service - [2] 구현

Dongwoo Kim·2022년 9월 14일
0

📃 프로젝트 기획

fruites_store_service


과일 쇼핑몰 서비스

MVP

사용자가 구매하고자 하는 상품(과일)을 선택하여 주문, 결제할 수 있다.

주요 기능

1. 회원관리

사용자는 이용자, 관리자로 나눠진다.

  • 사용자 정보
    • 기본정보
      • 이름
      • 패스워드
    • 사용자 유형
      • 이용자
        • 권한
          상품 : 조회
          주문 : 조회/입력/수정
          결제 : 조회/입력/수정
      • 관리자
        • 권한
          상품 : 조회/입력/수정/삭제
          주문 : 조회/입력/수정/삭제
          결제 : 조회/입력/수정/삭제
  • 공통기능
    • 회원가입/탈퇴, 회원정보 조회/수정
    • 로그인, 로그아웃

2. 상품관리

상품(과일)은 관리자에 의해 등록/수정/삭제 될 수 있으며 이용자는 조회만 가능하다.

  • 상품 정보
    • 기본정보
      • 이름
      • 설명
      • 판매지
      • 기본 배송비
    • 가격
      • 판매 단위별 가격
      • 재고 개수
    • 판매 상태 : 판매 준비중, 판매 중, 판매 완료

3. 주문관리

  • 주문은 관리자에 의해 등록/수정/삭제될 수 있다.
  • 이용자는 판매 중인 상품을 판매 단위별로 주문할 수 있다. (등록)
  • 이용자는 본인의 주문을 조회/수정 가능하다.
  • 주문 대기중일 때만 기본 정보를 수정할 수 있다.
  • 주문 정보
    • 기본정보
      • 주문자 (사용자)
      • 상품(과일)
      • 배송지
      • 배송비
      • 총 가격
    • 주문 상태 : 주문 대기중, 주문 취소, 주문 완료 & 결재 대기중, 결재 완료 & 배송 중, 배송 완료

4. 결제관리

  • 결제는 관리자에 의해 등록/수정/삭제될 수 있다.
  • 이용자는 주문 대기중인 본인의 주문에 대하여 결제할 수 있다. (등록)
  • 이용자는 본인의 결제을 조회/수정할 수 있다.
  • 결제 대기중일 때만 기본 정보를 수정할 수 있다.
  • 결제 정보
    • 기본 정보
      • 주문
      • 결제 가격
      • 결제 방법
    • 결제 상태 : 결제 대기중, 결제 취소, 결제 완료

기술 스택

Python3 Django DRF

ERD


📈 진행상황

완료


  • 유저, 상품, 주문, 결제 모델링
  • 유저 CRUD 기능 구현
  • 상품 CRUD 기능 구현
  • 주문 CRUD 기능 구현
  • 결제 CRUD 기능 구현
  • 유저 기능 error 핸들링
  • 상품 기능 error 핸들링
  • 주문 기능 error 핸들링
  • 결제 기능 error 핸들링
  • 상품 CRUD 권한 핸들링
  • 주문 CRUD 권한 핸들링
  • 결제 CRUD 권한 핸들링

진행중


  • 상품 재고, 상태 관리
  • 주문, 결제 상태 관리
  • Test 코드 작성

📌 대표 코드

1) CRUD 구현

# product.services.product_service.py

def get_product(product_id):
    """상품 정보 반환 함수

    Args:
        product_id (int): 상품 아이디

    Returns:
        product_info (dict): 상품 정보
    """

    product_obj = ProductModel.objects.get(id=product_id)

    product_info = ProductSerializer(product_obj).data

    return product_info


def create_product(product_info):
    """상품 정보로 상품 생성하는 함수

    Args:
        product_info (dict): 생성할 상품 정보 

    Returns:
        product_obj (ProductModel) : 생성된 상품 오브젝트


    """

    product_serializer = ProductSerializer(data=product_info)
    product_serializer.is_valid(raise_exception=True)
    product_obj = product_serializer.save()

    return product_obj

2) error 핸들링

# product.views.py

# 상품 CRUD View
class ProductView(APIView):
    permission_classes = [IsManagerOrIsAuthenticatedReadOnly]

    # 상품 조회
    def get(self, request, id):
        try:
            product_info = get_product(id)
            return Response(product_info, status=status.HTTP_200_OK)
        
        except ProductModel.DoesNotExist:
            return Response({"error": "상품를 찾을 수 없습니다."}, status=status.HTTP_404_NOT_FOUND)
        
        except:
            return Response({"error": "상품 조회에 실패했습니다."}, status=status.HTTP_400_BAD_REQUEST)

    # 상픔 생성
    def post(self, request):
        try:
            product_obj = create_product(request.data)
            return Response({"success" : "상품 생성 성공"}, status=status.HTTP_200_OK)
        
        except exceptions.ValidationError as e:
            error_key = list(e.detail.keys())[0]
            error_detail = e.detail.get(error_key, "")

            if error_key == "name":
                if error_detail[0].code == "blank":
                    return Response({"error": "상품 이름이 빈칸입니다."}, status=status.HTTP_400_BAD_REQUEST) 
                
                elif error_detail[0].code == "max_length":
                    return Response({"error": "상품 이름은 20글자 이하로 작성해주세요."}, status=status.HTTP_400_BAD_REQUEST)
        
                elif error_detail[0].code == "invalid":
                    return Response({"error": "상품 이름 입력 값이 유효하지않습니다."}, status=status.HTTP_400_BAD_REQUEST)

                elif error_detail[0].code == "required":
                    return Response({"error": "상품 이름을 입력해야 합니다."}, status=status.HTTP_400_BAD_REQUEST)

            elif error_key == "description": 
                if error_detail[0].code == "blank":
                    return Response({"error": "상품 설명이 빈칸입니다."}, status=status.HTTP_400_BAD_REQUEST) 
                
                elif error_detail[0].code == "max_length":
                    return Response({"error": "상품 설명은 200글자 이하로 작성해주세요."}, status=status.HTTP_400_BAD_REQUEST)

                elif error_detail[0].code == "invalid":
                    return Response({"error": "상품 설명 입력 값이 유효하지않습니다."}, status=status.HTTP_400_BAD_REQUEST) 

            
            elif error_key == "address": 
                if error_detail[0].code == "blank":
                    return Response({"error": "판매지가 빈칸입니다."}, status=status.HTTP_400_BAD_REQUEST) 
                
                elif error_detail[0].code == "max_length":
                    return Response({"error": "판매지는 100글자 이하로 작성해주세요."}, status=status.HTTP_400_BAD_REQUEST)

                elif error_detail[0].code == "invalid":
                    return Response({"error": "판매지 입력 값이 유효하지않습니다."}, status=status.HTTP_400_BAD_REQUEST) 

            elif error_key == "delivery_cost": 
                if error_detail[0].code == "invalid":
                    return Response({"error": "배송비 입력 값이 유효하지않습니다."}, status=status.HTTP_400_BAD_REQUEST) 

            elif error_key == "status": 
                if error_detail[0].code == "blank":
                    return Response({"error": "판매 상태가 빈칸입니다."}, status=status.HTTP_400_BAD_REQUEST) 
                
                elif error_detail[0].code == "invalid_choice":
                    return Response({"error": "판매 상태가 잘못 선택되었습니다."}, status=status.HTTP_400_BAD_REQUEST)

                elif error_detail[0].code == "invalid":
                    return Response({"error": "판매 상태 입력 값이 유효하지않습니다."}, status=status.HTTP_400_BAD_REQUEST) 


            return Response({"error" : "상품 생성 실패"}, status=status.HTTP_400_BAD_REQUEST)
        
        except:    
            return Response({"error" : "상품 생성 실패"}, status=status.HTTP_400_BAD_REQUEST)

	...

3) 권한 설정

# product.permissions.py

from rest_framework.permissions import BasePermission
from rest_framework.exceptions import APIException
from rest_framework import status

class GenericAPIException(APIException):
    def __init__(self, status_code, detail=None, code=None):
        self.status_code=status_code
        super().__init__(detail=detail, code=code)

class IsManagerOrIsAuthenticatedReadOnly(BasePermission):
    """
    관리자는 모두 가능, 로그인 사용자는 조회만 가능
    """
    SAFE_METHODS = ('GET', )
    message = '접근 권한이 없습니다.'

    def has_permission(self, request, view):
        user = request.user

        if not user.is_authenticated:
            response ={
                    "detail": "서비스를 이용하기 위해 로그인 해주세요.",
                }
            raise GenericAPIException(status_code=status.HTTP_401_UNAUTHORIZED, detail=response)

        if user.is_authenticated and user.type == "manager":
            return True
            
        if user.is_authenticated and request.method in self.SAFE_METHODS:
            return True
        
        return False



📎 기획문서 & Github

기획문서 - fruites_store_service

Github - fruites_store_service

profile
kimphysicsman

0개의 댓글