diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml
index dc05090..00a8707 100644
--- a/.github/workflows/ci-cd.yml
+++ b/.github/workflows/ci-cd.yml
@@ -95,6 +95,7 @@ jobs:
sudo docker rm $(sudo docker ps -aq)
sudo docker pull ${{ secrets.DOCKER_USERNAME }}/karrot
sudo docker run -d -p 8080:8080 \
+ --memory="768m" --cpus="0.8" \
-e JWT_SECRET_KEY=${{ secrets.JWT_SECRET_KEY }} \
-e AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_ID }} \
-e AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY }} \
@@ -107,4 +108,4 @@ jobs:
-e KAKAO_CLI_ID=${{ secrets.KAKAO_CLI_ID }} \
-e KAKAO_CLI_SECRET=${{ secrets.KAKAO_CLI_SECRET }} \
${{ secrets.DOCKER_USERNAME }}/karrot
- sudo docker image prune -f
\ No newline at end of file
+ sudo docker system prune -a -f --volumes
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
index cd4fbc5..50c620c 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,12 @@
-FROM openjdk:17
+# FROM openjdk:17
+# ARG JAR_FILE=build/libs/karrot-0.0.1-SNAPSHOT.jar
+# COPY ${JAR_FILE} /app.jar
+# EXPOSE 8080
+# ENTRYPOINT ["java", "-jar", "/app.jar"]
+
+FROM eclipse-temurin:17-jre-alpine
+WORKDIR /app
ARG JAR_FILE=build/libs/karrot-0.0.1-SNAPSHOT.jar
-COPY ${JAR_FILE} /app.jar
-EXPOSE 8080
-ENTRYPOINT ["java", "-jar", "/app.jar"]
\ No newline at end of file
+COPY ${JAR_FILE} app.jar
+ENTRYPOINT ["java", "-Xmx512m", "-Xms256m", "-jar", "app.jar"]
+EXPOSE 8080
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..4d8a7a6
--- /dev/null
+++ b/README.md
@@ -0,0 +1,100 @@
+# 우리 프로젝트 이름
+
+이 프로젝트는 온라인 중고거래 및 커뮤니티 플랫폼인 "당근 마켓"을 클론한 프로젝트입니다. 기존의 중고거래 기능에 더해 **경매 기능**을 추가하여 사용자들이 물품을 경매 형식으로 거래할 수 있도록 확장했습니다. 이 프로젝트는 필수 스펙과 권장 스펙을 충족하며, 새로운 기능인 경매를 통해 사용자 경험을 향상시켰습니다. [서비스 바로가기](https://toykarrot.shop/)
+
+---
+
+## 팀원 소개
+
+- **김정훈**: 소셜 로그인, 환경 설정, 보안 설정
+- **박원석**:
+- **이준용**:
+
+---
+
+## 클론 코딩 필수 스펙
+
+### **필수 스펙**
+
+- **회원가입 / 로그인 / 소셜 로그인**
+ - 닉네임, 아이디, 비밀번호, 이메일을 통한 회원가입
+ - 소셜 로그인 (Google, Kakao, Naver) 지원
+- **유저 계정 페이지**
+ - 프로필 수정 (사진, 닉네임, 동네)
+ - 매너온도 확인
+ - 판매내역 조회
+ - 내 매너 평가 및 거래 후기 조회
+- **글 작성 / 댓글 작성**
+ - 중고거래 및 동네생활 게시글 작성
+ - 댓글 및 댓글 좋아요 기능
+- **페이지네이션**
+ - 게시글 목록 및 댓글 목록에 페이지네이션 적용
+- **AWS 배포**
+ - EC2와 S3를 통한 프로젝트 배포
+
+### **권장 스펙**
+
+- **HTTPS 설정**
+ - 보안 강화를 위한 HTTPS 적용
+- **Github Actions CI/CD**
+ - 자동화된 배포 및 테스트를 위한 CI/CD 파이프라인 구축
+
+---
+
+## 새로운 기능: **경매**
+
+- **경매 물품 올리기**
+ - 판매자가 경매 물품을 등록할 수 있습니다.
+ - 시작가와 경매 종료 시간을 설정할 수 있습니다.
+- **경매 참여**
+ - 구매자는 경매에 참여하여 입찰할 수 있습니다.
+ - 입찰 가격은 시작가의 5% 배수로 인상 가능합니다.
+- **경매 종료**
+ - 제한 시간 내에 가장 높은 가격을 부른 구매자에게 물품이 판매됩니다.
+
+---
+
+## 전체 기능
+
+### 회원가입
+- 닉네임, 아이디, 비밀번호, 이메일을 받아 회원가입을 진행합니다.
+
+### 로그인
+- 일반 로그인 및 소셜 로그인을 지원합니다.
+
+### 동네 설정
+- 사용자의 동네를 설정합니다. 아직 지역 기반 서비스는 구현하지 못했습니다.
+
+### 홈 (중고거래)
+- **물품 올리기**: 판매자가 물품을 등록할 수 있습니다.
+- **물품 조회**: 현재 판매중인 모든 물품을 최신순으로 보여줍니다.
+- **물품 관심 기능**: 관심 있는 물품을 저장할 수 있습니다.
+- **판매자와 채팅**: 구매자와 판매자가 실시간으로 채팅할 수 있습니다.
+
+### 동네 생활
+- **글쓰기**: 사용자가 동네 생활 게시글을 작성할 수 있습니다.
+- **글 조회**: 게시글을 조회하고 댓글을 작성할 수 있습니다.
+- **댓글 좋아요**: 댓글에 좋아요를 누를 수 있습니다.
+- **공감**: 게시글에 공감을 표시할 수 있습니다.
+
+### 경매 (새로운 기능)
+- **경매 물품 올리기**: 판매자가 경매 물품을 등록합니다.
+- **경매 참여**: 구매자가 경매에 참여하여 입찰합니다.
+- **경매 종료**: 가장 높은 가격을 부른 구매자에게 물품이 판매됩니다.
+
+### 채팅
+- **채팅 기록 저장**: 물건을 구매할 때 채팅했던 기록이 저장됩니다.
+- **실시간 채팅**: WebSocket을 이용하여 실시간 채팅을 구현하였습니다.
+
+### 나의 당근
+- **프로필 수정**: 사진, 닉네임, 동네를 수정할 수 있습니다.
+- **매너온도 확인**: 사용자의 매너온도를 확인할 수 있습니다.
+- **판매내역**: 판매한 물품의 내역을 조회할 수 있습니다.
+- **내 매너 평가 조회**: 다른 사용자로부터 받은 매너 평가를 확인할 수 있습니다.
+- **거래 후기 조회**: 거래 후기를 조회할 수 있습니다.
+
+### 타사용자 프로필
+- **매너 칭찬**: 다른 사용자의 매너를 칭찬할 수 있습니다.
+- **판매물품 목록**: 해당 사용자가 판매 중인 물품을 조회할 수 있습니다.
+- **받은 매너 평가**: 해당 사용자가 받은 매너 평가를 확인할 수 있습니다.
+- **받은 거래 후기 조회**: 해당 사용자가 받은 거래 후기를 조회할 수 있습니다.
diff --git a/src/main/kotlin/com/toyProject7/karrot/SecurityConfig.kt b/src/main/kotlin/com/toyProject7/karrot/SecurityConfig.kt
index 48aed6e..e676376 100644
--- a/src/main/kotlin/com/toyProject7/karrot/SecurityConfig.kt
+++ b/src/main/kotlin/com/toyProject7/karrot/SecurityConfig.kt
@@ -35,7 +35,6 @@ class SecurityConfig(
registry
.requestMatchers(
*SecurityConstants.PUBLIC_PATHS,
- "/ws/**",
).permitAll()
.anyRequest().authenticated()
}
diff --git a/src/main/kotlin/com/toyProject7/karrot/auction/AuctionException.kt b/src/main/kotlin/com/toyProject7/karrot/auction/AuctionException.kt
index 46e0d2a..ece7a16 100644
--- a/src/main/kotlin/com/toyProject7/karrot/auction/AuctionException.kt
+++ b/src/main/kotlin/com/toyProject7/karrot/auction/AuctionException.kt
@@ -23,8 +23,20 @@ class AuctionPermissionDeniedException : AuctionException(
msg = "Permission denied",
)
-class AuctionBadPriceException : AuctionException(
+class AuctionTooLowPriceException : AuctionException(
errorCode = 0,
httpStatusCode = HttpStatus.BAD_REQUEST,
msg = "Your price is lower than current price",
)
+
+class AuctionTooFineUnitExceptions : AuctionException(
+ errorCode = 0,
+ httpStatusCode = HttpStatus.BAD_REQUEST,
+ msg = "Minimum unit is 5% of the start price",
+)
+
+class AuctionOverException : AuctionException(
+ errorCode = 0,
+ httpStatusCode = HttpStatus.BAD_REQUEST,
+ msg = "Over auction time",
+)
diff --git a/src/main/kotlin/com/toyProject7/karrot/auction/service/AuctionService.kt b/src/main/kotlin/com/toyProject7/karrot/auction/service/AuctionService.kt
index 37928ea..e1506a2 100644
--- a/src/main/kotlin/com/toyProject7/karrot/auction/service/AuctionService.kt
+++ b/src/main/kotlin/com/toyProject7/karrot/auction/service/AuctionService.kt
@@ -2,9 +2,11 @@ package com.toyProject7.karrot.auction.service
import com.toyProject7.karrot.article.ArticlePermissionDeniedException
import com.toyProject7.karrot.article.controller.UpdateStatusRequest
-import com.toyProject7.karrot.auction.AuctionBadPriceException
import com.toyProject7.karrot.auction.AuctionNotFoundException
+import com.toyProject7.karrot.auction.AuctionOverException
import com.toyProject7.karrot.auction.AuctionPermissionDeniedException
+import com.toyProject7.karrot.auction.AuctionTooFineUnitExceptions
+import com.toyProject7.karrot.auction.AuctionTooLowPriceException
import com.toyProject7.karrot.auction.controller.Auction
import com.toyProject7.karrot.auction.controller.AuctionMessage
import com.toyProject7.karrot.auction.controller.PostAuctionRequest
@@ -32,15 +34,21 @@ class AuctionService(
@Transactional
fun updatePrice(auctionMessage: AuctionMessage): AuctionMessage {
val auctionEntity = auctionRepository.findByIdOrNull(auctionMessage.auctionId) ?: throw AuctionNotFoundException()
+ if (Instant.now().isAfter(auctionEntity.endTime)) {
+ throw AuctionOverException()
+ }
if (auctionEntity.currentPrice >= auctionMessage.price) {
- throw AuctionBadPriceException()
+ throw AuctionTooLowPriceException()
+ }
+ if ((auctionMessage.price - auctionEntity.currentPrice) % (auctionEntity.startingPrice * 0.05).toInt() != 0) {
+ throw AuctionTooFineUnitExceptions()
}
val bidder = userService.getUserEntityByNickname(auctionMessage.senderNickname)
auctionEntity.currentPrice = auctionMessage.price
auctionEntity.bidder = bidder
- if (ChronoUnit.MINUTES.between(auctionEntity.endTime, Instant.now()) <= 1) {
- auctionEntity.endTime.plus(1, ChronoUnit.MINUTES)
+ if (ChronoUnit.SECONDS.between(Instant.now(), auctionEntity.endTime) <= 60) {
+ auctionEntity.endTime = Instant.now().plus(1, ChronoUnit.MINUTES)
}
return auctionMessage
diff --git a/src/main/kotlin/com/toyProject7/karrot/chatRoom/controller/ChatRoomController.kt b/src/main/kotlin/com/toyProject7/karrot/chatRoom/controller/ChatRoomController.kt
index 1d2a620..6f7e4e3 100644
--- a/src/main/kotlin/com/toyProject7/karrot/chatRoom/controller/ChatRoomController.kt
+++ b/src/main/kotlin/com/toyProject7/karrot/chatRoom/controller/ChatRoomController.kt
@@ -1,6 +1,5 @@
package com.toyProject7.karrot.chatRoom.controller
-import com.toyProject7.karrot.article.controller.Article
import com.toyProject7.karrot.chatRoom.service.ChatRoomService
import com.toyProject7.karrot.user.AuthUser
import com.toyProject7.karrot.user.controller.User
@@ -55,6 +54,6 @@ data class CreateChatRoomRequest(
)
data class ChatRoomResponse(
- val article: Article,
+ val chatRoom: ChatRoom,
val messages: List,
)
diff --git a/src/main/kotlin/com/toyProject7/karrot/chatRoom/service/ChatRoomService.kt b/src/main/kotlin/com/toyProject7/karrot/chatRoom/service/ChatRoomService.kt
index 355c42d..f2136c6 100644
--- a/src/main/kotlin/com/toyProject7/karrot/chatRoom/service/ChatRoomService.kt
+++ b/src/main/kotlin/com/toyProject7/karrot/chatRoom/service/ChatRoomService.kt
@@ -75,7 +75,7 @@ class ChatRoomService(
)
val messages = chatMessageEntities.map { chatMessageEntity -> ChatMessage.fromEntity(chatMessageEntity) }
return ChatRoomResponse(
- article = chatRoom.article,
+ chatRoom = chatRoom,
messages = messages,
)
}
diff --git a/src/main/kotlin/com/toyProject7/karrot/comment/controller/CommentController.kt b/src/main/kotlin/com/toyProject7/karrot/comment/controller/CommentController.kt
index 326a788..2e33d55 100644
--- a/src/main/kotlin/com/toyProject7/karrot/comment/controller/CommentController.kt
+++ b/src/main/kotlin/com/toyProject7/karrot/comment/controller/CommentController.kt
@@ -1,27 +1,21 @@
package com.toyProject7.karrot.comment.controller
-import com.toyProject7.karrot.comment.persistence.CommentRepository
import com.toyProject7.karrot.comment.service.CommentService
-import com.toyProject7.karrot.feed.controller.FeedPreview
-import com.toyProject7.karrot.feed.persistence.FeedEntity
import com.toyProject7.karrot.user.AuthUser
import com.toyProject7.karrot.user.controller.User
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.DeleteMapping
-import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
-import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/api")
class CommentController(
private val commentService: CommentService,
- private val commentRepository: CommentRepository,
) {
@PostMapping("/comment/post/{feedId}")
fun postComment(
@@ -69,19 +63,6 @@ class CommentController(
commentService.unlikeComment(commentId, user.id)
return ResponseEntity.ok("Unliked Successfully")
}
-
- @GetMapping("/myfeed/comment")
- fun getFeedsByUserComments(
- @RequestParam("feedId") feedId: Long,
- @AuthUser user: User,
- ): ResponseEntity> {
- val feeds: List = commentService.getFeedsByUserComments(feedId, user.id)
- val response =
- feeds.map { feed ->
- FeedPreview.fromEntity(feed)
- }
- return ResponseEntity.ok(response)
- }
}
data class CommentRequest(
diff --git a/src/main/kotlin/com/toyProject7/karrot/comment/persistence/CommentRepository.kt b/src/main/kotlin/com/toyProject7/karrot/comment/persistence/CommentRepository.kt
index 7a911c6..421ca6d 100644
--- a/src/main/kotlin/com/toyProject7/karrot/comment/persistence/CommentRepository.kt
+++ b/src/main/kotlin/com/toyProject7/karrot/comment/persistence/CommentRepository.kt
@@ -1,22 +1,7 @@
package com.toyProject7.karrot.comment.persistence
-import com.toyProject7.karrot.feed.persistence.FeedEntity
-import com.toyProject7.karrot.user.persistence.UserEntity
import org.springframework.data.jpa.repository.JpaRepository
-import org.springframework.data.jpa.repository.Query
-import org.springframework.data.repository.query.Param
interface CommentRepository : JpaRepository {
- @Query(
- """
- SELECT DISTINCT c.feed
- FROM comments c
- WHERE c.user = :user AND c.feed.id < :feedId
- ORDER BY c.feed.id DESC
- """,
- )
- fun findFeedsByUserComments(
- @Param("user") user: UserEntity,
- @Param("feedId") feedId: Long,
- ): List
+ fun findByUserId(userId: String): List
}
diff --git a/src/main/kotlin/com/toyProject7/karrot/comment/service/CommentService.kt b/src/main/kotlin/com/toyProject7/karrot/comment/service/CommentService.kt
index 245830a..f473857 100644
--- a/src/main/kotlin/com/toyProject7/karrot/comment/service/CommentService.kt
+++ b/src/main/kotlin/com/toyProject7/karrot/comment/service/CommentService.kt
@@ -8,7 +8,6 @@ import com.toyProject7.karrot.comment.persistence.CommentEntity
import com.toyProject7.karrot.comment.persistence.CommentLikesEntity
import com.toyProject7.karrot.comment.persistence.CommentLikesRepository
import com.toyProject7.karrot.comment.persistence.CommentRepository
-import com.toyProject7.karrot.feed.persistence.FeedEntity
import com.toyProject7.karrot.feed.service.FeedService
import com.toyProject7.karrot.user.service.UserService
import org.springframework.data.repository.findByIdOrNull
@@ -106,13 +105,8 @@ class CommentService(
}
@Transactional
- fun getFeedsByUserComments(
- feedId: Long,
- id: String,
- ): List {
- val user = userService.getUserEntityById(id)
- val feeds = commentRepository.findFeedsByUserComments(user, feedId)
- return feeds
+ fun getCommentsByUser(id: String): List {
+ return commentRepository.findByUserId(id)
}
@Transactional
diff --git a/src/main/kotlin/com/toyProject7/karrot/feed/controller/FeedController.kt b/src/main/kotlin/com/toyProject7/karrot/feed/controller/FeedController.kt
index e0564ae..a882c76 100644
--- a/src/main/kotlin/com/toyProject7/karrot/feed/controller/FeedController.kt
+++ b/src/main/kotlin/com/toyProject7/karrot/feed/controller/FeedController.kt
@@ -111,6 +111,19 @@ class FeedController(
}
return ResponseEntity.ok(response)
}
+
+ @GetMapping("/myfeed/comment")
+ fun getFeedsThatUserComments(
+ @RequestParam("feedId") feedId: Long,
+ @AuthUser user: User,
+ ): ResponseEntity> {
+ val feeds: List = feedService.getFeedsThatUserComments(user.id, feedId)
+ val response =
+ feeds.map { feed ->
+ FeedPreview.fromEntity(feed)
+ }
+ return ResponseEntity.ok(response)
+ }
}
data class PostFeedRequest(
diff --git a/src/main/kotlin/com/toyProject7/karrot/feed/service/FeedService.kt b/src/main/kotlin/com/toyProject7/karrot/feed/service/FeedService.kt
index 32ea054..2250cee 100644
--- a/src/main/kotlin/com/toyProject7/karrot/feed/service/FeedService.kt
+++ b/src/main/kotlin/com/toyProject7/karrot/feed/service/FeedService.kt
@@ -213,6 +213,22 @@ class FeedService(
return feeds
}
+ @Transactional
+ fun getFeedsThatUserComments(
+ id: String,
+ feedId: Long,
+ ): List {
+ val comments: List = commentService.getCommentsByUser(id)
+ val feeds: List =
+ comments
+ .map { it.feed }
+ .filter { it.id!! < feedId }
+ .distinctBy { it.id }
+ .sortedByDescending { it.id }
+ if (feeds.size < 10) return feeds
+ return feeds.subList(0, 10)
+ }
+
@Transactional
fun getFeedEntityById(feedId: Long): FeedEntity {
return feedRepository.findByIdOrNull(feedId) ?: throw FeedNotFoundException()
diff --git a/src/main/kotlin/com/toyProject7/karrot/profile/ProfileException.kt b/src/main/kotlin/com/toyProject7/karrot/profile/ProfileException.kt
index c90a8de..03aa70a 100644
--- a/src/main/kotlin/com/toyProject7/karrot/profile/ProfileException.kt
+++ b/src/main/kotlin/com/toyProject7/karrot/profile/ProfileException.kt
@@ -16,3 +16,9 @@ class ProfileNotFoundException : ProfileException(
httpStatusCode = HttpStatus.NOT_FOUND,
msg = "Profile not found",
)
+
+class ProfileEditNicknameConflictException : ProfileException(
+ errorCode = 0,
+ httpStatusCode = HttpStatus.CONFLICT,
+ msg = "Nickname conflict",
+)
diff --git a/src/main/kotlin/com/toyProject7/karrot/profile/service/ProfileService.kt b/src/main/kotlin/com/toyProject7/karrot/profile/service/ProfileService.kt
index ade0f9b..4af7b8d 100644
--- a/src/main/kotlin/com/toyProject7/karrot/profile/service/ProfileService.kt
+++ b/src/main/kotlin/com/toyProject7/karrot/profile/service/ProfileService.kt
@@ -6,17 +6,16 @@ import com.toyProject7.karrot.article.service.ArticleService
import com.toyProject7.karrot.image.persistence.ImageUrlEntity
import com.toyProject7.karrot.image.service.ImageService
import com.toyProject7.karrot.manner.controller.Manner
+import com.toyProject7.karrot.profile.ProfileEditNicknameConflictException
import com.toyProject7.karrot.profile.ProfileNotFoundException
import com.toyProject7.karrot.profile.controller.EditProfileRequest
import com.toyProject7.karrot.profile.controller.Profile
import com.toyProject7.karrot.profile.persistence.ProfileEntity
import com.toyProject7.karrot.profile.persistence.ProfileRepository
import com.toyProject7.karrot.review.controller.Review
-import com.toyProject7.karrot.review.service.ReviewService
import com.toyProject7.karrot.user.controller.User
import com.toyProject7.karrot.user.persistence.UserEntity
import com.toyProject7.karrot.user.service.UserService
-import org.springframework.context.annotation.Lazy
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.time.Instant
@@ -28,7 +27,6 @@ class ProfileService(
private val userService: UserService,
private val articleService: ArticleService,
private val imageService: ImageService,
- @Lazy private val reviewService: ReviewService,
) {
@Transactional
fun getMyProfile(user: User): Profile {
@@ -71,6 +69,10 @@ class ProfileService(
user: User,
request: EditProfileRequest,
): Profile {
+ if (user.nickname != request.nickname && userService.existUserEntityByNickname(request.nickname)) {
+ throw ProfileEditNicknameConflictException()
+ }
+
val userEntity = userService.getUserEntityById(user.id)
val profileEntity = profileRepository.findByUserId(user.id) ?: throw ProfileNotFoundException()
val itemCount = getItemCount(user.id)
@@ -122,7 +124,14 @@ class ProfileService(
nickname: String,
reviewId: Long,
): List {
- return reviewService.getPreviousReviews(nickname, reviewId)
+ val userEntity = userService.getUserEntityByNickname(nickname)
+ val profileEntity = profileRepository.findByUserId(userEntity.id!!) ?: throw ProfileNotFoundException()
+
+ return profileEntity.reviews
+ .filter { it.id!! < reviewId }
+ .sortedByDescending { it.createdAt }
+ .take(10)
+ .map { Review.fromEntity(it) }
}
fun getItemCount(id: String): Int {
diff --git a/src/main/kotlin/com/toyProject7/karrot/review/controller/ReviewController.kt b/src/main/kotlin/com/toyProject7/karrot/review/controller/ReviewController.kt
index a5c1aec..1097603 100644
--- a/src/main/kotlin/com/toyProject7/karrot/review/controller/ReviewController.kt
+++ b/src/main/kotlin/com/toyProject7/karrot/review/controller/ReviewController.kt
@@ -1,10 +1,7 @@
package com.toyProject7.karrot.review.controller
import com.toyProject7.karrot.review.service.ReviewService
-import com.toyProject7.karrot.user.AuthUser
-import com.toyProject7.karrot.user.controller.User
import org.springframework.http.ResponseEntity
-import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RestController
@@ -13,13 +10,11 @@ import org.springframework.web.bind.annotation.RestController
class ReviewController(
private val reviewService: ReviewService,
) {
- @PostMapping("/api/{sellerNickname}/review")
+ @PostMapping("/api/review/post")
fun createReview(
@RequestBody request: ReviewCreateRequest,
- @PathVariable sellerNickname: String,
- @AuthUser user: User,
): ResponseEntity {
- val review = reviewService.createReview(sellerNickname, user.nickname, request.content, request.location)
+ val review = reviewService.createReview(request)
return ResponseEntity.status(201).body(review)
}
}
@@ -27,6 +22,9 @@ class ReviewController(
data class ReviewCreateRequest(
val content: String,
val location: String,
+ val isWritedByBuyer: Boolean,
+ val sellerId: String,
+ val buyerId: String,
)
typealias ReviewCreateResponse = Review
diff --git a/src/main/kotlin/com/toyProject7/karrot/review/service/ReviewService.kt b/src/main/kotlin/com/toyProject7/karrot/review/service/ReviewService.kt
index bc74bcd..8b84c84 100644
--- a/src/main/kotlin/com/toyProject7/karrot/review/service/ReviewService.kt
+++ b/src/main/kotlin/com/toyProject7/karrot/review/service/ReviewService.kt
@@ -3,9 +3,9 @@ package com.toyProject7.karrot.review.service
import com.toyProject7.karrot.profile.service.ProfileService
import com.toyProject7.karrot.review.ReviewContentLengthOutOfRangeException
import com.toyProject7.karrot.review.controller.Review
+import com.toyProject7.karrot.review.controller.ReviewCreateRequest
import com.toyProject7.karrot.review.persistence.ReviewEntity
import com.toyProject7.karrot.review.persistence.ReviewRepository
-import com.toyProject7.karrot.user.controller.User
import com.toyProject7.karrot.user.service.UserService
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
@@ -18,51 +18,36 @@ class ReviewService(
private val profileService: ProfileService,
) {
@Transactional
- fun createReview(
- sellerNickname: String,
- buyerNickname: String,
- content: String,
- location: String,
- ): Review {
- validateContent(content)
+ fun createReview(request: ReviewCreateRequest): Review {
+ validateContent(request.content)
- val sellerEntity = userService.getUserEntityByNickname(sellerNickname)
- val seller = User.fromEntity(sellerEntity)
- val sellerProfileEntity = profileService.getProfileEntityByUserId(seller.id)
+ val sellerEntity = userService.getUserEntityById(request.sellerId)
+ val sellerProfileEntity = profileService.getProfileEntityByUserId(request.sellerId)
- val buyerEntity = userService.getUserEntityByNickname(buyerNickname)
- val buyer = User.fromEntity(buyerEntity)
- val buyerProfileEntity = profileService.getProfileEntityByUserId(buyer.id)
+ val buyerEntity = userService.getUserEntityById(request.buyerId)
+ val buyerProfileEntity = profileService.getProfileEntityByUserId(request.buyerId)
val reviewEntity =
ReviewEntity(
seller = sellerEntity,
buyer = buyerEntity,
- content = content,
- location = location,
+ content = request.content,
+ location = request.location,
createdAt = Instant.now(),
updatedAt = Instant.now(),
).let {
reviewRepository.save(it)
}
- sellerProfileEntity.reviews += reviewEntity
- buyerProfileEntity.reviews += reviewEntity
+ if (request.isWritedByBuyer) {
+ sellerProfileEntity.reviews += reviewEntity
+ } else {
+ buyerProfileEntity.reviews += reviewEntity
+ }
return Review.fromEntity(reviewEntity)
}
- @Transactional
- fun getPreviousReviews(
- nickname: String,
- reviewId: Long,
- ): List {
- return reviewRepository.findTop10BySellerNicknameOrBuyerNicknameAndIdBeforeOrderByCreatedAtDesc(nickname, nickname, reviewId).map {
- reviewEntity ->
- Review.fromEntity(reviewEntity)
- }
- }
-
private fun validateContent(content: String) {
if (content.isBlank()) {
throw ReviewContentLengthOutOfRangeException()
diff --git a/src/main/kotlin/com/toyProject7/karrot/security/SecurityConstants.kt b/src/main/kotlin/com/toyProject7/karrot/security/SecurityConstants.kt
index 0da6517..2977c13 100644
--- a/src/main/kotlin/com/toyProject7/karrot/security/SecurityConstants.kt
+++ b/src/main/kotlin/com/toyProject7/karrot/security/SecurityConstants.kt
@@ -8,5 +8,6 @@ object SecurityConstants {
"/auth/**",
"/login/oauth2/**",
"/api/test",
+ "/ws/**",
)
}
diff --git a/src/main/kotlin/com/toyProject7/karrot/socialLogin/handler/CustomAuthenticationSuccessHandler.kt b/src/main/kotlin/com/toyProject7/karrot/socialLogin/handler/CustomAuthenticationSuccessHandler.kt
index b59095b..319c3f5 100644
--- a/src/main/kotlin/com/toyProject7/karrot/socialLogin/handler/CustomAuthenticationSuccessHandler.kt
+++ b/src/main/kotlin/com/toyProject7/karrot/socialLogin/handler/CustomAuthenticationSuccessHandler.kt
@@ -31,10 +31,9 @@ class CustomAuthenticationSuccessHandler(
val attributes = oauth2User.attributes
val providerId = extractProviderId(attributes, provider)
val email = extractEmail(attributes, provider)
- val name = extractName(attributes, provider)
// Create or retrieve the user
- val user = userService.createOrRetrieveSocialUser(email, providerId, provider, name)
+ val user = userService.createOrRetrieveSocialUser(email, providerId, provider)
// Generate JWT
val accessToken = UserAccessTokenUtil.generateAccessToken(user.id)
@@ -86,13 +85,13 @@ class CustomAuthenticationSuccessHandler(
}
}
- private fun extractName(
+ /*private fun extractName(
attributes: Map,
provider: String,
): String {
return when (provider) {
"google" -> attributes["name"] as String
- "naver" -> (attributes["response"] as Map<*, *>)["name"] as String
+ "naver" -> (attributes["response"] as Map<*, *>)["nickname"] as String
"kakao" -> {
val kakaoAccount = attributes["kakao_account"] as Map<*, *>
val profile = kakaoAccount["profile"] as Map<*, *>
@@ -100,5 +99,5 @@ class CustomAuthenticationSuccessHandler(
}
else -> "Unknown"
}
- }
+ }*/
}
diff --git a/src/main/kotlin/com/toyProject7/karrot/user/service/UserService.kt b/src/main/kotlin/com/toyProject7/karrot/user/service/UserService.kt
index bf5a8e5..e3ca5a6 100644
--- a/src/main/kotlin/com/toyProject7/karrot/user/service/UserService.kt
+++ b/src/main/kotlin/com/toyProject7/karrot/user/service/UserService.kt
@@ -108,16 +108,26 @@ class UserService(
email: String,
providerId: String,
provider: String,
- username: String,
): User {
// Check if the user exists by email
val existingUser = userRepository.findSocialUserByEmail(email)
return existingUser?.let {
- // Convert existingUser (of type SocialUser) to User DTO
+ // If the user exists, convert to User DTO and return
User.fromEntity(it)
} ?: run {
- // If the user doesn't exist, create a new one
+ // If the user doesn't exist, generate a unique random username
+ var username = generateRandomString()
+ var isUnique = false
+
+ while (!isUnique) {
+ if (!userRepository.existsByNickname(username)) {
+ isUnique = true
+ }
+ username = generateRandomString()
+ }
+
+ // Create a new SocialUser with the generated username
val newUser =
SocialUser(
email = email,
@@ -129,18 +139,29 @@ class UserService(
imageUrl = null,
updatedAt = Instant.now(),
)
- val savedUser = userRepository.save(newUser) // This should save as SocialUser
+ // Save the new user
+ val savedUser = userRepository.save(newUser)
+
+ // Create and save the associated profile
val profileEntity =
ProfileEntity(
user = newUser,
)
profileService.saveProfileEntity(profileEntity)
- User.fromEntity(savedUser) // Convert and return as User DTO
+ // Convert and return as User DTO
+ User.fromEntity(savedUser)
}
}
+ private fun generateRandomString(length: Int = 8): String {
+ val characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ return (1..length)
+ .map { characters.random() }
+ .joinToString("")
+ }
+
@Transactional
fun getUserEntityById(id: String): UserEntity {
return userRepository.findByIdOrNull(id) ?: throw UserNotFoundException()
@@ -158,4 +179,9 @@ class UserService(
fun getUserEntityByNickname(nickname: String): UserEntity {
return userRepository.findByNickname(nickname) ?: throw UserNotFoundException()
}
+
+ @Transactional
+ fun existUserEntityByNickname(nickname: String): Boolean {
+ return userRepository.existsByNickname(nickname)
+ }
}
diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml
index d6fa4fe..5fed258 100644
--- a/src/main/resources/application.yaml
+++ b/src/main/resources/application.yaml
@@ -29,9 +29,8 @@ spring:
authorizationGrantType: authorization_code
redirectUri: "https://toykarrot.shop/{action}/oauth2/code/{registrationId}"
scope:
- - nickname
+ - profile
- email
- - profile_image
clientName: Naver
kakao:
clientId: '${KAKAO_CLI_ID}'
@@ -41,7 +40,6 @@ spring:
redirectUri: "https://toykarrot.shop/{action}/oauth2/code/{registrationId}"
scope:
- profile_nickname
- - profile_image
- account_email
clientName: Kakao
provider: