diff --git a/src/main/java/com/campus/campus/domain/manager/application/dto/request/RewardRequest.java b/src/main/java/com/campus/campus/domain/manager/application/dto/request/RewardRequest.java new file mode 100644 index 00000000..604567b8 --- /dev/null +++ b/src/main/java/com/campus/campus/domain/manager/application/dto/request/RewardRequest.java @@ -0,0 +1,11 @@ +package com.campus.campus.domain.manager.application.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; + +public record RewardRequest( + @NotBlank + @Schema(description = "보상으로 지급 이미지 url", example = "https://www.example.com.png") + String rewardImageUrl +) { +} diff --git a/src/main/java/com/campus/campus/domain/manager/application/dto/response/StampRewardNeededUserListResponse.java b/src/main/java/com/campus/campus/domain/manager/application/dto/response/StampRewardNeededUserListResponse.java new file mode 100644 index 00000000..f9ad1362 --- /dev/null +++ b/src/main/java/com/campus/campus/domain/manager/application/dto/response/StampRewardNeededUserListResponse.java @@ -0,0 +1,18 @@ +package com.campus.campus.domain.manager.application.dto.response; + +import io.swagger.v3.oas.annotations.media.Schema; + +public record StampRewardNeededUserListResponse( + @Schema(description = "보상 지급이 필요한 사용자 ID", example = "1") + Long userId, + + @Schema(description = "보상 지급이 필요한 사용자 이름", example = "한승현") + String nickname, + + @Schema(description = "사용자의 스탬프 수", example = "10") + int stampCount, + + @Schema(description = "사용자가 보상이 필요한지 여부", example = "true") + boolean rewardNeeded +) { +} diff --git a/src/main/java/com/campus/campus/domain/manager/application/mapper/ManagerMapper.java b/src/main/java/com/campus/campus/domain/manager/application/mapper/ManagerMapper.java index 77b72dd3..83105f48 100644 --- a/src/main/java/com/campus/campus/domain/manager/application/mapper/ManagerMapper.java +++ b/src/main/java/com/campus/campus/domain/manager/application/mapper/ManagerMapper.java @@ -3,11 +3,15 @@ import org.springframework.stereotype.Component; import com.campus.campus.domain.council.domain.entity.StudentCouncil; +import com.campus.campus.domain.manager.application.dto.request.RewardRequest; import com.campus.campus.domain.manager.application.dto.response.CertifyRequestCouncilResponse; import com.campus.campus.domain.manager.application.dto.response.CouncilApproveOrDenyResponse; import com.campus.campus.domain.manager.application.dto.response.CertifyRequestCouncilListResponse; import com.campus.campus.domain.manager.application.dto.response.ManagerLoginResponse; +import com.campus.campus.domain.manager.application.dto.response.StampRewardNeededUserListResponse; import com.campus.campus.domain.manager.domain.entity.Manager; +import com.campus.campus.domain.stamp.domain.entity.Reward; +import com.campus.campus.domain.user.domain.entity.User; import lombok.RequiredArgsConstructor; @@ -45,4 +49,24 @@ public CertifyRequestCouncilResponse toCertifyRequestCouncilResponse(StudentCoun studentCouncil.getElectionImageUrl() ); } + + public StampRewardNeededUserListResponse toStampRewardNeededUserListResponse(User user, int stampCount) { + String nickname = user.getCampusNickname(); + if (nickname == null || nickname.isBlank()) { + nickname = user.getNickname(); + } + return new StampRewardNeededUserListResponse( + user.getId(), + nickname, + stampCount, + user.isRewardNeeded() + ); + } + + public Reward createReward(User user, RewardRequest rewardRequest) { + return Reward.builder() + .user(user) + .rewardImageUrl(rewardRequest.rewardImageUrl()) + .build(); + } } diff --git a/src/main/java/com/campus/campus/domain/manager/application/service/ManagerService.java b/src/main/java/com/campus/campus/domain/manager/application/service/ManagerService.java index 6a1885fa..c7754742 100644 --- a/src/main/java/com/campus/campus/domain/manager/application/service/ManagerService.java +++ b/src/main/java/com/campus/campus/domain/manager/application/service/ManagerService.java @@ -14,15 +14,22 @@ import com.campus.campus.domain.council.domain.repository.StudentCouncilRepository; import com.campus.campus.domain.manager.application.dto.request.CouncilApproveOrDenyRequest; import com.campus.campus.domain.manager.application.dto.request.ManagerLoginRequest; +import com.campus.campus.domain.manager.application.dto.request.RewardRequest; import com.campus.campus.domain.manager.application.dto.response.CertifyRequestCouncilResponse; import com.campus.campus.domain.manager.application.dto.response.CouncilApproveOrDenyResponse; import com.campus.campus.domain.manager.application.dto.response.CertifyRequestCouncilListResponse; import com.campus.campus.domain.manager.application.dto.response.ManagerLoginResponse; +import com.campus.campus.domain.manager.application.dto.response.StampRewardNeededUserListResponse; import com.campus.campus.domain.manager.application.exception.ManagerNotFoundException; import com.campus.campus.domain.manager.application.exception.PasswordNotCorrectException; import com.campus.campus.domain.manager.application.mapper.ManagerMapper; import com.campus.campus.domain.manager.domain.entity.Manager; import com.campus.campus.domain.manager.domain.repository.ManagerRepository; +import com.campus.campus.domain.stamp.domain.entity.Reward; +import com.campus.campus.domain.stamp.domain.repository.RewardRepository; +import com.campus.campus.domain.user.application.exception.UserNotFoundException; +import com.campus.campus.domain.user.domain.entity.User; +import com.campus.campus.domain.user.domain.repository.UserRepository; import com.campus.campus.global.util.jwt.JwtProvider; import com.campus.campus.global.util.jwt.application.service.RedisTokenService; @@ -34,6 +41,8 @@ public class ManagerService { private final StudentCouncilRepository studentCouncilRepository; private final ManagerRepository managerRepository; + private final UserRepository userRepository; + private final RewardRepository rewardRepository; private final PasswordEncoder passwordEncoder; private final JwtProvider jwtProvider; private final RedisTokenService redisTokenService; @@ -96,6 +105,31 @@ public CertifyRequestCouncilResponse getCertifyRequestCouncil(Long councilId) { return managerMapper.toCertifyRequestCouncilResponse(studentCouncil); } + public List getStampRewardNeededUserList() { + List rewardNeededUsers = userRepository.findRewardNeededUsersWithStampCount(); + + return rewardNeededUsers.stream() + .map(rewardNeededUser -> { + User user = (User)rewardNeededUser[0]; + Long count = (Long)rewardNeededUser[1]; + + return managerMapper.toStampRewardNeededUserListResponse(user, count.intValue()); + } + ).toList(); + } + + @Transactional + public void grantRewardToUser(Long userId, RewardRequest rewardRequest) { + User user = userRepository.findByIdAndDeletedAtIsNull(userId) + .orElseThrow(UserNotFoundException::new); + + Reward reward = managerMapper.createReward(user, rewardRequest); + + rewardRepository.save(reward); + + user.updateRewardNeeded(false); + } + private void sendCouncilApprovedMail(String to) { SimpleMailMessage message = new SimpleMailMessage(); message.setTo(to); diff --git a/src/main/java/com/campus/campus/domain/manager/presentation/ManagerController.java b/src/main/java/com/campus/campus/domain/manager/presentation/ManagerController.java index e28a8557..5ffc9175 100644 --- a/src/main/java/com/campus/campus/domain/manager/presentation/ManagerController.java +++ b/src/main/java/com/campus/campus/domain/manager/presentation/ManagerController.java @@ -13,10 +13,12 @@ import com.campus.campus.domain.manager.application.dto.request.CouncilApproveOrDenyRequest; import com.campus.campus.domain.manager.application.dto.request.ManagerLoginRequest; +import com.campus.campus.domain.manager.application.dto.request.RewardRequest; import com.campus.campus.domain.manager.application.dto.response.CertifyRequestCouncilResponse; import com.campus.campus.domain.manager.application.dto.response.CouncilApproveOrDenyResponse; import com.campus.campus.domain.manager.application.dto.response.CertifyRequestCouncilListResponse; import com.campus.campus.domain.manager.application.dto.response.ManagerLoginResponse; +import com.campus.campus.domain.manager.application.dto.response.StampRewardNeededUserListResponse; import com.campus.campus.domain.manager.application.service.ManagerService; import com.campus.campus.global.common.response.CommonResponse; @@ -68,4 +70,25 @@ public CommonResponse> getCertifyRequest return CommonResponse.success(ManagerResponseCode.CERTIFY_REQUEST_LIST_SUCCESS, responses); } + + @GetMapping("/rewards/necessary-users") + @PreAuthorize("hasRole('MANAGER')") + @Operation(summary = "스탬프 보상이 필요한 유저 목록 조회") + public CommonResponse> getStampRewardNeededUserList() { + List responses = managerService.getStampRewardNeededUserList(); + + return CommonResponse.success(ManagerResponseCode.REWARD_NEEDED_USER_LIST_SUCCESS, responses); + } + + @PostMapping("reward/grant/{userId}") + @PreAuthorize("hasRole('MANAGER')") + @Operation(summary = "스탬프 보상 지급") + public CommonResponse grantRewardToUser( + @PathVariable Long userId, + @Valid @RequestBody RewardRequest rewardRequest + ) { + managerService.grantRewardToUser(userId, rewardRequest); + + return CommonResponse.success(ManagerResponseCode.GRANT_REWARD_SUCCESS); + } } diff --git a/src/main/java/com/campus/campus/domain/manager/presentation/ManagerResponseCode.java b/src/main/java/com/campus/campus/domain/manager/presentation/ManagerResponseCode.java index e6530468..affba51e 100644 --- a/src/main/java/com/campus/campus/domain/manager/presentation/ManagerResponseCode.java +++ b/src/main/java/com/campus/campus/domain/manager/presentation/ManagerResponseCode.java @@ -13,7 +13,9 @@ public enum ManagerResponseCode implements ResponseCodeInterface { MANAGER_LOGIN_SUCCESS(200, HttpStatus.OK, "관리자 로그인에 성공했습니다."), COUNCIL_APPROVE_OR_DENY_SUCCESS(200, HttpStatus.OK, "학생회 승인 혹은 거부 및 메일 전송에 성공했습니다."), CERTIFY_REQUEST_LIST_SUCCESS(200, HttpStatus.OK, "학생회 인증 요청 목록 조회에 성공했습니다."), - CERTIFY_REQUEST_ELECTION_IMAGE_SUCCESS(200, HttpStatus.OK, "해당 학생회 인증 요청 당선 사진 조회에 성공했습니다."); + CERTIFY_REQUEST_ELECTION_IMAGE_SUCCESS(200, HttpStatus.OK, "해당 학생회 인증 요청 당선 사진 조회에 성공했습니다."), + REWARD_NEEDED_USER_LIST_SUCCESS(200, HttpStatus.OK, "스탬프 보상이 필요한 유저 목록 조회에 성공했습니다."), + GRANT_REWARD_SUCCESS(200, HttpStatus.OK, "스탬프 보상 지급에 성공했습니다."); private final int code; private final HttpStatus status; diff --git a/src/main/java/com/campus/campus/domain/review/application/dto/response/ReviewCreateResult.java b/src/main/java/com/campus/campus/domain/review/application/dto/response/ReviewCreateResult.java index 4974b4d7..4e646eb4 100644 --- a/src/main/java/com/campus/campus/domain/review/application/dto/response/ReviewCreateResult.java +++ b/src/main/java/com/campus/campus/domain/review/application/dto/response/ReviewCreateResult.java @@ -5,7 +5,7 @@ @Builder public record ReviewCreateResult( boolean isFirstReviewOfPlace, - int userReviewCountOfPlace - // int NumberOfUserStamp, + int userReviewCountOfPlace, + int numberOfUserStamp ) { } diff --git a/src/main/java/com/campus/campus/domain/review/application/mapper/ReviewMapper.java b/src/main/java/com/campus/campus/domain/review/application/mapper/ReviewMapper.java index 9ab0cfc9..2212d316 100644 --- a/src/main/java/com/campus/campus/domain/review/application/mapper/ReviewMapper.java +++ b/src/main/java/com/campus/campus/domain/review/application/mapper/ReviewMapper.java @@ -87,11 +87,12 @@ public CursorPageReviewResponse toCursorReviewResponse(List oldImages, ReviewRequest requ } private ReviewCreateResult getCreateResult(Place place, User user) { - //해당 장소 리뷰 개수 long totalReviewCountOfPlace = reviewRepository.countByPlace_PlaceId(place.getPlaceId()); - //해당 장소에서 유저가 쓴 리뷰가 몇번째인지 long count = reviewRepository.countByPlaceAndUser(place, user); boolean isFirstReviewOfPlace = totalReviewCountOfPlace == 1; + int NumberOfStamp = stampRepository.countByUser(user); - return reviewMapper.toReviewCreateResult(isFirstReviewOfPlace, count); + return reviewMapper.toReviewCreateResult(isFirstReviewOfPlace, count, NumberOfStamp); } diff --git a/src/main/java/com/campus/campus/domain/review/domain/entity/Review.java b/src/main/java/com/campus/campus/domain/review/domain/entity/Review.java index 8f61236f..f781cc86 100644 --- a/src/main/java/com/campus/campus/domain/review/domain/entity/Review.java +++ b/src/main/java/com/campus/campus/domain/review/domain/entity/Review.java @@ -47,11 +47,12 @@ public class Review extends BaseEntity { @JoinColumn(name = "place_id", nullable = false) private Place place; - public void update( - String content, - Double star - ) { + public void update(String content, Double star) { this.content = content; this.star = star; } + + public void verify() { + this.isVerified = true; + } } diff --git a/src/main/java/com/campus/campus/domain/stamp/application/dto/response/RewardResponse.java b/src/main/java/com/campus/campus/domain/stamp/application/dto/response/RewardResponse.java new file mode 100644 index 00000000..4b5ad35a --- /dev/null +++ b/src/main/java/com/campus/campus/domain/stamp/application/dto/response/RewardResponse.java @@ -0,0 +1,17 @@ +package com.campus.campus.domain.stamp.application.dto.response; + +import java.time.LocalDateTime; + +import io.swagger.v3.oas.annotations.media.Schema; + +public record RewardResponse( + @Schema(description = "보상 id", example = "1") + Long rewardId, + + @Schema(description = "보상 이미지 url", example = "https://www.example.com.png") + String rewardImageUrl, + + @Schema(description = "지급일") + LocalDateTime rewardDate +) { +} diff --git a/src/main/java/com/campus/campus/domain/stamp/application/dto/response/StampInfoResponse.java b/src/main/java/com/campus/campus/domain/stamp/application/dto/response/StampInfoResponse.java new file mode 100644 index 00000000..b81d9baf --- /dev/null +++ b/src/main/java/com/campus/campus/domain/stamp/application/dto/response/StampInfoResponse.java @@ -0,0 +1,14 @@ +package com.campus.campus.domain.stamp.application.dto.response; + +import java.util.List; + +import io.swagger.v3.oas.annotations.media.Schema; + +public record StampInfoResponse( + @Schema(description = "스탬프 개수", example = "3") + int stampCount, + + @Schema(description = "스탬프에 해당하는 리뷰 목록") + List reviews +) { +} diff --git a/src/main/java/com/campus/campus/domain/stamp/application/dto/response/StampReviewResponse.java b/src/main/java/com/campus/campus/domain/stamp/application/dto/response/StampReviewResponse.java new file mode 100644 index 00000000..b2dcfabc --- /dev/null +++ b/src/main/java/com/campus/campus/domain/stamp/application/dto/response/StampReviewResponse.java @@ -0,0 +1,17 @@ +package com.campus.campus.domain.stamp.application.dto.response; + +import java.time.LocalDateTime; + +import io.swagger.v3.oas.annotations.media.Schema; + +public record StampReviewResponse( + @Schema(description = "리뷰 id", example = "1") + Long reviewId, + + @Schema(description = "리뷰 장소 이름", example = "투썸 플레이스") + String placeName, + + @Schema(description = "리뷰 작성 시간", example = "2024-01-10T18:00:00") + LocalDateTime reviewCreatedAt +) { +} diff --git a/src/main/java/com/campus/campus/domain/stamp/application/mapper/StampMapper.java b/src/main/java/com/campus/campus/domain/stamp/application/mapper/StampMapper.java new file mode 100644 index 00000000..79287c8b --- /dev/null +++ b/src/main/java/com/campus/campus/domain/stamp/application/mapper/StampMapper.java @@ -0,0 +1,41 @@ +package com.campus.campus.domain.stamp.application.mapper; + +import org.springframework.stereotype.Component; + +import com.campus.campus.domain.review.domain.entity.Review; +import com.campus.campus.domain.stamp.application.dto.response.RewardResponse; +import com.campus.campus.domain.stamp.application.dto.response.StampReviewResponse; +import com.campus.campus.domain.stamp.domain.entity.Reward; +import com.campus.campus.domain.stamp.domain.entity.Stamp; +import com.campus.campus.domain.user.domain.entity.User; + +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +public class StampMapper { + public RewardResponse toRewardResponse(Reward reward) { + return new RewardResponse( + reward.getRewardId(), + reward.getRewardImageUrl(), + reward.getCreatedAt() + ); + } + + public StampReviewResponse toStampReviewResponse(Stamp stamp) { + Review review = stamp.getReview(); + + return new StampReviewResponse( + review.getId(), + review.getPlace().getPlaceName(), + review.getCreatedAt() + ); + } + + public Stamp createStamp(User user, Review review) { + return Stamp.builder() + .user(user) + .review(review) + .build(); + } +} diff --git a/src/main/java/com/campus/campus/domain/stamp/application/service/StampService.java b/src/main/java/com/campus/campus/domain/stamp/application/service/StampService.java new file mode 100644 index 00000000..5b001484 --- /dev/null +++ b/src/main/java/com/campus/campus/domain/stamp/application/service/StampService.java @@ -0,0 +1,69 @@ +package com.campus.campus.domain.stamp.application.service; + +import java.util.List; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.campus.campus.domain.review.domain.entity.Review; +import com.campus.campus.domain.stamp.application.dto.response.RewardResponse; +import com.campus.campus.domain.stamp.application.dto.response.StampInfoResponse; +import com.campus.campus.domain.stamp.application.dto.response.StampReviewResponse; +import com.campus.campus.domain.stamp.application.mapper.StampMapper; +import com.campus.campus.domain.stamp.domain.entity.Stamp; +import com.campus.campus.domain.stamp.domain.repository.RewardRepository; +import com.campus.campus.domain.stamp.domain.repository.StampRepository; +import com.campus.campus.domain.user.application.exception.UserNotFoundException; +import com.campus.campus.domain.user.domain.entity.User; +import com.campus.campus.domain.user.domain.repository.UserRepository; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class StampService { + private final StampRepository stampRepository; + private final RewardRepository rewardRepository; + private final UserRepository userRepository; + private final StampMapper stampMapper; + + @Transactional + public void grantStampForReview(User user, Review review) { + if (stampRepository.existsByReview(review)) { + return; + } + + Stamp stamp = stampMapper.createStamp(user, review); + stampRepository.save(stamp); + + int currentStampCount = stampRepository.countByUser(user); + if (currentStampCount % 10 == 0 && currentStampCount!=0 && !user.isRewardNeeded()) { + user.updateRewardNeeded(true); + } + } + + public StampInfoResponse getStampInfo(Long userId) { + User user = userRepository.findByIdAndDeletedAtIsNull(userId) + .orElseThrow(UserNotFoundException::new); + + int stampCount = stampRepository.countByUser(user); + + List reviews = stampRepository.findAllByUserWithReviewAndPlace(user) + .stream() + .map(stampMapper::toStampReviewResponse) + .toList(); + + return new StampInfoResponse(stampCount, reviews); + } + + public List findRewards(Long userId) { + User user = userRepository.findByIdAndDeletedAtIsNull(userId) + .orElseThrow(UserNotFoundException::new); + + return rewardRepository.findAllByUserOrderByRewardIdDesc(user) + .stream() + .map(stampMapper::toRewardResponse) + .toList(); + } +} diff --git a/src/main/java/com/campus/campus/domain/stamp/domain/entity/Reward.java b/src/main/java/com/campus/campus/domain/stamp/domain/entity/Reward.java new file mode 100644 index 00000000..ff8fa30b --- /dev/null +++ b/src/main/java/com/campus/campus/domain/stamp/domain/entity/Reward.java @@ -0,0 +1,39 @@ +package com.campus.campus.domain.stamp.domain.entity; + +import com.campus.campus.domain.user.domain.entity.User; +import com.campus.campus.global.entity.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Table(name = "rewards") +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class Reward extends BaseEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "reward_id") + private Long rewardId; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id", nullable = false) + private User user; + + @Column(name = "reward_image_url") + private String rewardImageUrl; +} diff --git a/src/main/java/com/campus/campus/domain/stamp/domain/entity/Stamp.java b/src/main/java/com/campus/campus/domain/stamp/domain/entity/Stamp.java new file mode 100644 index 00000000..192dcac5 --- /dev/null +++ b/src/main/java/com/campus/campus/domain/stamp/domain/entity/Stamp.java @@ -0,0 +1,42 @@ +package com.campus.campus.domain.stamp.domain.entity; + +import com.campus.campus.domain.review.domain.entity.Review; +import com.campus.campus.domain.user.domain.entity.User; +import com.campus.campus.global.entity.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Table(name = "stamps") +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class Stamp extends BaseEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "stamp_id") + private Long stampId; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id", nullable = false) + private User user; + + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "review_id", nullable = false) + private Review review; +} diff --git a/src/main/java/com/campus/campus/domain/stamp/domain/repository/RewardRepository.java b/src/main/java/com/campus/campus/domain/stamp/domain/repository/RewardRepository.java new file mode 100644 index 00000000..5d5b3144 --- /dev/null +++ b/src/main/java/com/campus/campus/domain/stamp/domain/repository/RewardRepository.java @@ -0,0 +1,12 @@ +package com.campus.campus.domain.stamp.domain.repository; + +import java.util.List; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.campus.campus.domain.stamp.domain.entity.Reward; +import com.campus.campus.domain.user.domain.entity.User; + +public interface RewardRepository extends JpaRepository { + List findAllByUserOrderByRewardIdDesc(User user); +} diff --git a/src/main/java/com/campus/campus/domain/stamp/domain/repository/StampRepository.java b/src/main/java/com/campus/campus/domain/stamp/domain/repository/StampRepository.java new file mode 100644 index 00000000..35204c2c --- /dev/null +++ b/src/main/java/com/campus/campus/domain/stamp/domain/repository/StampRepository.java @@ -0,0 +1,27 @@ +package com.campus.campus.domain.stamp.domain.repository; + +import java.util.List; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import com.campus.campus.domain.review.domain.entity.Review; +import com.campus.campus.domain.stamp.domain.entity.Stamp; +import com.campus.campus.domain.user.domain.entity.User; + +public interface StampRepository extends JpaRepository { + boolean existsByReview(Review review); + + int countByUser(User user); + + @Query(""" + SELECT s + FROM Stamp s + JOIN FETCH s.review r + JOIN FETCH r.place + WHERE s.user = :user + ORDER BY s.createdAt DESC + """) + List findAllByUserWithReviewAndPlace(@Param("user") User user); +} diff --git a/src/main/java/com/campus/campus/domain/stamp/presentation/StampController.java b/src/main/java/com/campus/campus/domain/stamp/presentation/StampController.java new file mode 100644 index 00000000..072c3805 --- /dev/null +++ b/src/main/java/com/campus/campus/domain/stamp/presentation/StampController.java @@ -0,0 +1,39 @@ +package com.campus.campus.domain.stamp.presentation; + +import java.util.List; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.campus.campus.domain.stamp.application.dto.response.RewardResponse; +import com.campus.campus.domain.stamp.application.dto.response.StampInfoResponse; +import com.campus.campus.domain.stamp.application.service.StampService; +import com.campus.campus.global.annotation.CurrentUserId; +import com.campus.campus.global.common.response.CommonResponse; + +import io.swagger.v3.oas.annotations.Operation; +import lombok.RequiredArgsConstructor; + +@RestController +@RequestMapping("/stamps") +@RequiredArgsConstructor +public class StampController { + private final StampService stampService; + + @GetMapping + @Operation(summary = "스탬프 개수 및 리뷰 목록 조회") + public CommonResponse getStampInfo(@CurrentUserId Long userId) { + StampInfoResponse stampInfoResponse = stampService.getStampInfo(userId); + + return CommonResponse.success(StampResponseCode.STAMP_INFO_SUCCESS, stampInfoResponse); + } + + @GetMapping("/rewards") + @Operation(summary = "스탬프 보상 목록 조회 기능") + public CommonResponse> getRewards(@CurrentUserId Long userId) { + List rewardResponse = stampService.findRewards(userId); + + return CommonResponse.success(StampResponseCode.REWARD_LIST_SUCCESS, rewardResponse); + } +} diff --git a/src/main/java/com/campus/campus/domain/stamp/presentation/StampResponseCode.java b/src/main/java/com/campus/campus/domain/stamp/presentation/StampResponseCode.java new file mode 100644 index 00000000..f45a043e --- /dev/null +++ b/src/main/java/com/campus/campus/domain/stamp/presentation/StampResponseCode.java @@ -0,0 +1,19 @@ +package com.campus.campus.domain.stamp.presentation; + +import org.springframework.http.HttpStatus; + +import com.campus.campus.global.common.response.ResponseCodeInterface; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum StampResponseCode implements ResponseCodeInterface { + REWARD_LIST_SUCCESS(200, HttpStatus.OK, "스탬프 보상 목록 조회에 성공했습니다."), + STAMP_INFO_SUCCESS(200, HttpStatus.OK, "스탬프 정보 조회에 성공했습니다."); + + private final int code; + private final HttpStatus status; + private final String message; +} diff --git a/src/main/java/com/campus/campus/domain/user/domain/entity/User.java b/src/main/java/com/campus/campus/domain/user/domain/entity/User.java index ac97f60f..86e4fbd7 100644 --- a/src/main/java/com/campus/campus/domain/user/domain/entity/User.java +++ b/src/main/java/com/campus/campus/domain/user/domain/entity/User.java @@ -18,6 +18,7 @@ import jakarta.persistence.Table; import lombok.AccessLevel; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; @@ -49,6 +50,10 @@ public class User extends BaseEntity { @Column(name = "campus_nickname") private String campusNickname; + @Column(name = "reward_needed") + @Builder.Default + private boolean rewardNeeded = false; + @Column(name = "deleted_at") private LocalDateTime deletedAt; @@ -81,4 +86,8 @@ public void delete(LocalDateTime now) { public boolean isProfileNotCompleted() { return this.school == null || this.major == null; } + + public void updateRewardNeeded(boolean rewardNeeded) { + this.rewardNeeded = rewardNeeded; + } } diff --git a/src/main/java/com/campus/campus/domain/user/domain/repository/UserRepository.java b/src/main/java/com/campus/campus/domain/user/domain/repository/UserRepository.java index c24d5593..3e44b996 100644 --- a/src/main/java/com/campus/campus/domain/user/domain/repository/UserRepository.java +++ b/src/main/java/com/campus/campus/domain/user/domain/repository/UserRepository.java @@ -32,4 +32,14 @@ public interface UserRepository extends JpaRepository { AND u.deletedAt IS NULL """) Optional findByIdWithAcademicInfo(@Param("userId") Long userId); + + @Query(""" + SELECT u, COUNT(s) + FROM User u + LEFT JOIN Stamp s ON s.user = u + WHERE u.rewardNeeded = true + AND u.deletedAt IS NULL + GROUP BY u + """) + List findRewardNeededUsersWithStampCount(); }