Skip to content

Commit

Permalink
Merge pull request #13 from price-offer/feature/#12-post
Browse files Browse the repository at this point in the history
Feature/#12 post
  • Loading branch information
awesomeo184 authored Sep 3, 2023
2 parents 29e1bf4 + 885f5d5 commit 1d712f7
Show file tree
Hide file tree
Showing 22 changed files with 537 additions and 14 deletions.
11 changes: 11 additions & 0 deletions src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,14 @@ include::{snippets}/authentication/kakao-login/query-parameters.adoc[]
include::{snippets}/authentication/kakao-login/http-response.adoc[]
==== Response Fields
include::{snippets}/authentication/kakao-login/response-fields.adoc[]

= 판매 게시글
== 판매 게시글 생성
==== Example Curl
include::{snippets}/posts/create-post/curl-request.adoc[]
==== Example Request
include::{snippets}/posts/create-post/http-request.adoc[]
==== Request Fields
include::{snippets}/posts/create-post/request-fields.adoc[]
==== Example Response
include::{snippets}/posts/create-post/http-response.adoc[]
4 changes: 2 additions & 2 deletions src/main/java/com/offer/authentication/KakaoOAuthGateway.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.offer.authentication;

import com.offer.member.Member.OAuthType;
import com.offer.client.KakaoAccessTokenRequest;
import com.offer.client.KakaoProfileClient;
import com.offer.client.KakaoTokenClient;
import com.offer.client.KakaoSocialProfileResponse;
import com.offer.client.KakaoTokenClient;
import com.offer.member.Member.OAuthType;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

import com.offer.authentication.JwtTokenProvider;
import com.offer.authentication.KakaoOAuthGateway;
import com.offer.member.Member;
import com.offer.member.MemberRepository;
import com.offer.member.RandomNicknameGenerator;
import com.offer.authentication.SocialProfile;
import com.offer.authentication.application.response.OAuthLoginResponse;
import com.offer.authentication.application.response.OAuthLoginUrlResponse;
import com.offer.member.Member;
import com.offer.member.MemberRepository;
import com.offer.member.RandomNicknameGenerator;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.offer.authentication.presentation;

import com.offer.authentication.application.OAuthService;
import com.offer.authentication.application.response.OAuthLoginResponse;
import com.offer.authentication.application.response.OAuthLoginUrlResponse;
import com.offer.authentication.application.OAuthService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.offer.authentication.presentation;

import com.offer.authentication.presentation.LoginMember.Authority;
import lombok.Getter;
import lombok.Setter;
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.RequestScope;

@Component
@RequestScope
@Getter
@Setter
public class AuthenticationContext {

private String principal; // 토큰에서 추출한 memberId
private Authority authority;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.offer.authentication.presentation;

import com.offer.authentication.JwtTokenProvider;
import com.offer.utils.AuthorizationTokenExtractor;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.util.Objects;
import org.springframework.stereotype.Component;
import org.springframework.web.cors.CorsUtils;
import org.springframework.web.servlet.HandlerInterceptor;

@Component
public class AuthenticationInterceptor implements HandlerInterceptor {

private final JwtTokenProvider jwtTokenProvider;
private final AuthenticationContext authenticationContext;

public AuthenticationInterceptor(JwtTokenProvider jwtTokenProvider,
final AuthenticationContext authenticationContext) {
this.jwtTokenProvider = jwtTokenProvider;
this.authenticationContext = authenticationContext;
}

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
if (CorsUtils.isPreFlightRequest(request)) {
return true;
}

String token = AuthorizationTokenExtractor.extractToken(request);
if (Objects.nonNull(token)) {
String subject = jwtTokenProvider.extractSubject(token);
authenticationContext.setPrincipal(subject);
}
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.offer.authentication.presentation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthenticationPrincipal {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.offer.authentication.presentation;

import com.offer.authentication.presentation.LoginMember.Authority;
import java.util.Objects;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

@Component
public class AuthenticationPrincipalArgumentResolver implements HandlerMethodArgumentResolver {

private final AuthenticationContext authenticationContext;

public AuthenticationPrincipalArgumentResolver(AuthenticationContext authenticationContext) {
this.authenticationContext = authenticationContext;
}

@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(AuthenticationPrincipal.class);
}

@Override
public LoginMember resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {

String principal = authenticationContext.getPrincipal();
if (Objects.isNull(principal)) {
authenticationContext.setAuthority(Authority.ANONYMOUS);
return new LoginMember(Authority.ANONYMOUS);
}

authenticationContext.setAuthority(Authority.MEMBER);
return new LoginMember(Long.valueOf(principal), Authority.MEMBER);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.offer.authentication.presentation;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class LoginMember {

private Long id;
private Authority authority;

public LoginMember(Authority authority) {
this(null, authority);
}

public LoginMember(Long id, Authority authority) {
this.id = id;
this.authority = authority;
}

public boolean isAnonymous() {
return Authority.ANONYMOUS.equals(authority);
}

public boolean isMember() {
return Authority.MEMBER.equals(authority);
}

public enum Authority {
ANONYMOUS, MEMBER
}
}
10 changes: 10 additions & 0 deletions src/main/java/com/offer/config/JpaAuditConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.offer.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@Configuration
@EnableJpaAuditing
public class JpaAuditConfig {

}
6 changes: 4 additions & 2 deletions src/main/java/com/offer/member/Member.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,17 @@
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

@Entity
@EntityListeners(AuditingEntityListener.class)
@Builder
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@AllArgsConstructor
@Builder
@ToString
public class Member {

@Id
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/com/offer/member/MemberRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,9 @@ public interface MemberRepository extends JpaRepository<Member, Long> {
default Member getByOauthTypeAndOauthId(OAuthType oauthType, Long oauthId) {
return findByOauthTypeAndOauthId(oauthType, oauthId).orElseThrow(() -> new IllegalArgumentException("존재하지 않는 회원입니다."));
}

default Member getById(Long id) {
return findById(id)
.orElseThrow(() -> new IllegalArgumentException("존재하지 않는 회원입니다. id: " + id));
}
}
26 changes: 26 additions & 0 deletions src/main/java/com/offer/post/application/PostService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.offer.post.application;

import com.offer.member.Member;
import com.offer.member.MemberRepository;
import com.offer.post.application.request.PostCreateRequest;
import com.offer.post.domain.Post;
import com.offer.post.domain.PostRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class PostService {

private final MemberRepository memberRepository;
private final PostRepository postRepository;

@Transactional
public Long createPost(PostCreateRequest request, Long memberId) {
Member member = memberRepository.getById(memberId);
Post post = request.toEntity(member);
return postRepository.save(post).getId();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.offer.post.application.request;

import com.offer.member.Member;
import com.offer.post.domain.Post;
import java.util.List;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class PostCreateRequest {

private String title;
private String category;
private int price;
private String location;
private String productCondition; // 상품 상태
private String tradeType;
private String thumbnailImageUrl;
private List<String> imageUrls;
private String description;

@Builder
public PostCreateRequest(String title, String category, int price, String location,
String productCondition, String tradeType, String thumbnailImageUrl, List<String> imageUrls,
String description) {
this.title = title;
this.category = category;
this.price = price;
this.location = location;
this.productCondition = productCondition;
this.tradeType = tradeType;
this.thumbnailImageUrl = thumbnailImageUrl;
this.imageUrls = imageUrls;
this.description = description;
}

public Post toEntity(Member member) {
return Post.builder()
.seller(member)
.title(title)
.category(category)
.price(price)
.location(location)
.productCondition(productCondition)
.tradeType(tradeType)
.thumbnailImageUrl(thumbnailImageUrl)
.description(description)
.build();
}
}
61 changes: 61 additions & 0 deletions src/main/java/com/offer/post/domain/Post.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.offer.post.domain;

import com.offer.member.Member;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import java.time.LocalDateTime;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EntityListeners(AuditingEntityListener.class)
@Getter
@ToString
public class Post {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne
private Member seller;
private String title;
private int price;
private String category;
private String description;
private String thumbnailImageUrl;
private String location;
private String tradeType;
private String productCondition;

@CreatedDate
private LocalDateTime createdAt;

@LastModifiedDate
private LocalDateTime modifiedAt;

@Builder
public Post(Member seller, String title, int price, String category, String description,
String thumbnailImageUrl, String location, String tradeType, String productCondition) {
this.seller = seller;
this.title = title;
this.price = price;
this.category = category;
this.description = description;
this.thumbnailImageUrl = thumbnailImageUrl;
this.location = location;
this.tradeType = tradeType;
this.productCondition = productCondition;
}
}
11 changes: 11 additions & 0 deletions src/main/java/com/offer/post/domain/PostRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.offer.post.domain;

import org.springframework.data.jpa.repository.JpaRepository;

public interface PostRepository extends JpaRepository<Post, Long> {

default Post getById(Long id) {
return findById(id)
.orElseThrow(() -> new IllegalArgumentException("존재하지 않는 포스트입니다. id=" + id));
}
}
Loading

0 comments on commit 1d712f7

Please sign in to comment.