1. 프로젝트에 사용할 라이브러리 설치 (build.gradle)
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
//security
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6:3.1.2.RELEASE'
implementation 'org.springframework.boot:spring-boot-starter-security'
// Email
implementation 'org.springframework.boot:spring-boot-starter-mail'
//db
runtimeOnly 'com.mysql:mysql-connector-j'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
//lombok
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
//aws
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
}
2. 엔티티 생성
2.1. Member
- member는 여러개의 order, qna, review, like, cartItem을 가질 수 있음 => 1 : N
- member는 한개의 delivery를 가질 수 있음 => 1 : 1
- member가 삭제되면 qna, review, like, cart 모두 삭제 => orphanRemoval = true 설정
- membership은 @Enumerated(EnumType.STRING)을 설정 => db에 저장될 때 string 타입으로 저장
- edit 메소드 => 회원정보 수정시 사용
- deliveryCreate 메소드 => 새로운 배송지 등록할 때 사용
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Getter
@Table(name = "member")
public class Member {
@Id @GeneratedValue
@Column(name = "member_id")
private Long id;
private String email; //로그인에 사용되는 id
private String name; //유저이름
private String password; //비밀번호
@OneToOne(fetch = FetchType.LAZY,cascade = CascadeType.ALL)
private Delivery delivery; //주소
@Enumerated(EnumType.STRING)
private Membership membership; //권한
@OneToMany(mappedBy = "member")
private List<Order> orders; //주문목록
@OneToMany(mappedBy = "member",orphanRemoval = true)
private List<QnA> QnAs; //작성한 문의 목록
@OneToMany(mappedBy = "member", orphanRemoval = true)
private List<Review> reviews;//작성한 리뷰 목록
@OneToMany(mappedBy = "member",orphanRemoval = true)
private List<Like> likes; //위시리스트
@OneToMany(mappedBy = "member",orphanRemoval = true)
private List<Cart> cartItems; //장바구니목록
//회원수정
public void edit(String newPassword,String name){
this.password=newPassword;
this.name=name;
}
public void deliveryCreate(Delivery newDelivery){
this.delivery=newDelivery;
}
}
2.2. Membership
public enum Membership {
FRIEND, BUDDY, SOULMATE, ADMIN
}
2.3. Address
- 임베디드 타입 사용
- Delivery 테이블에 컬럼으로 존재
@Embeddable
@Getter
public class Address {
private String zipcode;
private String streetAddress;
private String detailAddress;
public Address() {
}
public Address(String zipcode, String streetAddress, String detailAddress) {
this.zipcode = zipcode;
this.streetAddress = streetAddress;
this.detailAddress = detailAddress;
}
public void addressEdit(String zipcode, String streetAddress, String detailAddress) {
this.zipcode = zipcode;
this.streetAddress = streetAddress;
this.detailAddress = detailAddress;
}
}
2.4. Delivery
- address를 내장
- 주문, 회원과 일대일 매핑 => 1 : 1
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Getter
public class Delivery {
@Id @GeneratedValue
private Long id;
@OneToOne(fetch = FetchType.LAZY,mappedBy ="delivery")
private Member member; //회원
@OneToOne(mappedBy = "delivery",fetch = FetchType.LAZY)
private Order order; //주문
private String deliveryName; //배송지명
private String memberName;
@Embedded
private Address address; //배송주소
private String phoneNumber; //핸드폰 번호
}
2.5. DeliveryStatus
public enum DeliveryStatus {
READY, START, COMPLETE
}
2.6. Item
- itemCategory는 @Enumerated(EnumType.STRING)을 설정 => db에 저장될 때 string 타입으로 저장
- 한개의 Item은 여러개의 UploadImage를 가질 수 있음 => 1 : N
- modifyStock 메소드 => 상품 수정 시 재고 변경할 때 사용
- removeStock 메소드 => 상품 주문 시 재고 줄일 때 사용 => 재고가 주문한 수량보다 적으면 수량부족예외 발생
- addStock 메소드 => 상품 주문 취소 시 재고 늘릴 때 사용
- edit 메소드 => 상품 Entity 수정 시 사용
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Getter
public class Item {
@Id @GeneratedValue
private Long id;
private String name; //상품 이름
private String sizeTip; //사이즈 설명
private String itemTip; //상품 설명
private Integer delivery_price; //배송비
private Integer price; //상품 가격
@Enumerated(EnumType.STRING)
private ItemCategory itemCategory; //상품 종류
private Integer stockQuantity; //재고
@OneToMany(mappedBy = "item", orphanRemoval = true)
private List<UploadImage> uploadImages; //상품을 설명하는 이미지
//==비즈니스 로직==//
public void modifyStock(int quantity){
this.stockQuantity =quantity;
}
public void removeStock(int quantity){
int restStock = stockQuantity-quantity;
if(restStock<0){
throw new NotEnoughStockException("need more stock");
}
this.stockQuantity = restStock;
}
public void addStock(int quantity){
this.stockQuantity+=quantity;
}
public Item edit(Integer price,ItemCategory itemCategory,Integer stock ,String itemTip, String sizeTip) {
this.price=price;
this.itemTip=itemTip;
this.sizeTip=sizeTip;
modifyStock(stock);
return this;
}
}
2.7. ItemCategory
- 총 7개의 카테고리로 구성
- of 메소드 => 대소문자를 구분하지 않고 카테고리를 찾기 위해 사용
public enum ItemCategory {
CAKE, TARTE , MADELEINE, FINANCIER, MACARON, SCONE, MUFFIN;
public static ItemCategory of(String category){
if(category.equalsIgnoreCase("cake")){
return CAKE;
} else if(category.equalsIgnoreCase("tarte")){
return TARTE;
} else if(category.equalsIgnoreCase("madeleine")){
return MADELEINE;
} else if(category.equalsIgnoreCase("financier")){
return FINANCIER;
} else if(category.equalsIgnoreCase("macaron")){
return MACARON;
} else if(category.equalsIgnoreCase("scone")){
return SCONE;
} else if(category.equalsIgnoreCase("muffin")){
return MUFFIN;
}
else return null;
}
}
2.8. Cart
- increaseCount 메소드 => 해당 상품이 이미 장바구니에 존재하는 경우 해당 상품의 count만 증가
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Getter
public class Cart {
@Id @GeneratedValue
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
private Member member;
@ManyToOne(fetch = FetchType.LAZY)
private Item item;
private Integer count;
public static Cart create(Item item, int count, Member member){
return Cart.builder()
.item(item)
.count(count)
.member(member)
.build();
}
public boolean increaseCount(int cnt){
this.count+=cnt;
return true;
}
}
2.9. Review
- 한개의 review는 한개의 UploadImage를 가질 수 있음
- saveMemberItem 메소드 => 리뷰 생성 시 reviewJoinRequest를 통해 review를 만드는데 reviewJoinRequest DTO는 Member와 Item에 대한 정보가 없으므로 해당 정보를 따로 추가해주기 위해 사용
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Getter
public class Review extends BaseEntity{
@Id @GeneratedValue
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
private Member member;
@ManyToOne(fetch = FetchType.LAZY)
private Item item;
private Integer rating; //별점
private String title;
private String content;
@OneToOne(fetch = FetchType.LAZY,mappedBy = "review", orphanRemoval = true)
private UploadImage uploadImage;
public void saveMemberItem(Member member,Item item){
this.member=member;
this.item=item;
member.getReviews().add(this);
}
public Review setUploadImage(UploadImage uploadImage) {
this.uploadImage=uploadImage;
return this;
}
}
2.10. QnA
- 해당 Q&A에 답변이 등록되면 answerStatus가 COMPLETE으로 변경
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Getter
public class QnA extends BaseEntity{
@Id @GeneratedValue
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
private Member member; //글을 작성한 회원
@ManyToOne(fetch = FetchType.LAZY)
private Item item; //어떤 상품에 대한 글인지 확인하기 위해
private String title; //제목
private String content; //내용
private String answer;
@Enumerated(EnumType.STRING)
private AnswerStatus answerStatus; //답변 상태
@OneToOne(fetch = FetchType.LAZY,mappedBy = "qna", orphanRemoval = true)
private UploadImage uploadImage;
public void saveMemberItem(Member loginMember, Item item) {
this.member=loginMember;
this.item=item;
loginMember.getQnAs().add(this);
}
public QnA setUploadImage(UploadImage uploadImage) {
this.uploadImage=uploadImage;
return this;
}
public QnA createAnswer(String answer) {
this.answer=answer;
this.answerStatus=AnswerStatus.COMPLETE;
return this;
}
}
2.11. AnswerStatus
public enum AnswerStatus {
READY, COMPLETE
}
2.12. Order
- addOrderItem 메소드 => 주문에 OrderItem을 추가하면서 OrderItem에도 해당 주문을 추가 (양방향 연관관계)
- createorder 메소드 => 주문 생성 시 사용
- getTotalPrice 메소드 => 총 주문 금액 구할 때 사용
- changeDeliveryStatus = > 관리자가 배송시작 버튼 누를 때 호출됨
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Getter
@Table(name = "orders")
public class Order extends BaseEntity{
@Id @GeneratedValue
private Long id;
@ManyToOne(fetch = FetchType.LAZY,cascade = CascadeType.ALL)
@JoinColumn(name = "member_id")
private Member member; //주문한 회원
private Integer totalPrice;
@OneToMany(mappedBy = "order",orphanRemoval = true, cascade = CascadeType.ALL)
private List<OrderItem> orderItems; //order과 item은 다대다 관계이므로 orderItem 만들어 일대다 관계로 변경
@OneToOne(fetch = FetchType.LAZY)
private Delivery delivery; //배송정보
@Enumerated(EnumType.STRING)
private OrderStatus orderStatus; //주문상태(주문,취소)
@Enumerated(EnumType.STRING)
private DeliveryStatus deliveryStatus; //배송상태
public void addOrderItem(OrderItem orderItem){
orderItems.add(orderItem);
orderItem.setOrder(this);
}
public static Order createOrder(Member member,List<OrderItem> orderItemList,Delivery delivery){
Order order = Order.builder()
.member(member).
orderStatus(OrderStatus.ORDER)
.deliveryStatus(DeliveryStatus.READY)
.delivery(delivery).build();
order.orderItems=new ArrayList<>();
for(OrderItem orderItem: orderItemList){
order.addOrderItem(orderItem);
}
order.totalPrice=order.getTotalPrice(orderItemList);
return order;
}
public int getTotalPrice(List<OrderItem>orderItems){
int totalPrice=0;
for(OrderItem orderItem:orderItems){
totalPrice += orderItem.getTotalPrice();
}
return totalPrice;
}
public boolean cancel() {
if(this.getDeliveryStatus()==DeliveryStatus.READY){
this.orderStatus=OrderStatus.CANCEL;
for(OrderItem orderItem:orderItems){
Integer count = orderItem.getCount();
orderItem.getItem().addStock(count);
}
return true;
}
else{
return false;
}
}
public boolean changeDeliveryStatus(){
this.deliveryStatus=DeliveryStatus.START;
return true;
}
}
2.13. OrderStatus
public enum OrderStatus {
ORDER, CANCEL
}
2.14. OrderItem
- Order과 Item이 다대다 매핑이므로 일대다 매핑으로 만들기 위해 중간 엔티티 생성
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Getter
public class OrderItem {
@Id @GeneratedValue
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
private Order order;
@ManyToOne(fetch = FetchType.LAZY)
private Item item;
private Integer orderPrice; //주문했을때의 상품 가격
private Integer count; //상품 개수
public static OrderItem createOrderItem(Item item,int count){
System.out.println(item.getId()+" "+count);
item.removeStock(count);
return OrderItem.builder()
.item(item)
.count(count)
.orderPrice(item.getPrice())
.build();
}
public int getTotalPrice(){
return orderPrice*count;
}
public void setOrder(Order order) {
this.order=order;
}
}
2.15. UploadImage
- 이미지 저장할 때 사용할 클래스
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Getter
public class UploadImage {
@Id @GeneratedValue
private Long id;
private String originalFilename; //원본 파일명
private String savedFilename; //서버에 저장된 파일명
@ManyToOne(fetch = FetchType.LAZY)
private Item item;
@OneToOne(fetch = FetchType.LAZY)
private Review review;
@OneToOne(fetch = FetchType.LAZY)
private QnA qna;
}
2.16. BaseEntity
- 시간을 저장해야 할 때 사용할 클래스
@Getter
@MappedSuperclass //추상클래스에 사용할 수 있으며 엔티티가 될 수 없고 상속을 통해서 사용해야 함
@EntityListeners(AuditingEntityListener.class)
public class BaseEntity {
@CreatedDate //jpa 저장소가 save()할 때 자동으로 생성시간을 만듦
@Column(updatable = false)
private LocalDateTime createdAt;
@LastModifiedDate //jpa 저장소가 수정할 때 자동으로 생성시간을 만듦
private LocalDateTime lastModifiedAt;
}
'web > Bakery Shop Project' 카테고리의 다른 글
| 쇼핑몰 구현 8 - 회원 기능 (0) | 2024.02.23 |
|---|---|
| 쇼핑몰 구현 7 - 환경설정 (0) | 2024.02.23 |
| 쇼핑몰 구현 5 - 설계 & 결과 (0) | 2024.02.22 |
| 쇼핑몰 구현 4 - API 엔드포인트 설계 (0) | 2024.01.31 |
| 쇼핑몰 구현 3 - db설계 (0) | 2024.01.31 |