diff --git a/src/main/java/com/example/couphoneserver/controller/AuthController.java b/src/main/java/com/example/couphoneserver/controller/AuthController.java index f88fad1..8058253 100644 --- a/src/main/java/com/example/couphoneserver/controller/AuthController.java +++ b/src/main/java/com/example/couphoneserver/controller/AuthController.java @@ -2,8 +2,8 @@ import com.example.couphoneserver.common.annotation.NoAuth; import com.example.couphoneserver.common.response.BaseResponse; -import com.example.couphoneserver.dto.member.request.LoginRequestDto; -import com.example.couphoneserver.dto.member.response.LoginResponseDto; +import com.example.couphoneserver.dto.auth.LoginRequestDto; +import com.example.couphoneserver.dto.auth.LoginResponseDto; import com.example.couphoneserver.service.MemberService; import io.swagger.v3.oas.annotations.Operation; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/example/couphoneserver/controller/MemberController.java b/src/main/java/com/example/couphoneserver/controller/MemberController.java index c450556..d192788 100644 --- a/src/main/java/com/example/couphoneserver/controller/MemberController.java +++ b/src/main/java/com/example/couphoneserver/controller/MemberController.java @@ -1,15 +1,13 @@ package com.example.couphoneserver.controller; -import com.example.couphoneserver.common.annotation.NoAuth; import com.example.couphoneserver.common.response.BaseResponse; import com.example.couphoneserver.domain.entity.Member; -import com.example.couphoneserver.dto.member.request.AddMemberRequestDto; -import com.example.couphoneserver.dto.member.response.MemberInfoResponseDto; -import com.example.couphoneserver.dto.member.response.MemberResponseDto; +import com.example.couphoneserver.dto.member.response.GetMemberCouponBrandsResponse; +import com.example.couphoneserver.dto.member.response.GetMemberResponse; +import com.example.couphoneserver.dto.member.response.PatchMemberResponse; import com.example.couphoneserver.service.MemberService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*; @@ -22,18 +20,10 @@ public class MemberController { private final MemberService memberService; - @NoAuth - @PostMapping - @Operation(summary = "이메일으로 회원 가입", description = - "Request Body 에 이메일, 비밀번호를 담아서 보내면 비밀번호는 DB 에 암호화되어 관리합니다.") - public BaseResponse signup(@Valid @RequestBody AddMemberRequestDto request) { - return new BaseResponse<>(memberService.save(request)); - } - @PatchMapping("/{member-id}") @Operation(summary = "회원 탈퇴", description = "회원의 상태를 TERMINATED 으로 변경합니다. path variable 로 멤버 id 담아서 보내주세요!") - public BaseResponse delete(@PathVariable("member-id") Long memberId){ + public BaseResponse delete(@PathVariable("member-id") Long memberId) { Member member = memberService.findOneById(memberId); return new BaseResponse<>(memberService.delete(member)); } @@ -41,8 +31,23 @@ public BaseResponse delete(@PathVariable("member-id") Long me @GetMapping("/{member-id}") @Operation(summary = "회원 정보 조회", description = "회원 정보를 조회합니다. path variable 로 멤버 id 담아서 보내주세요!") - public BaseResponse show(@PathVariable("member-id") Long memberId){ + public BaseResponse show(@PathVariable("member-id") Long memberId) { Member member = memberService.findOneById(memberId); return new BaseResponse<>(memberService.getMemberInfo(member)); } + + @GetMapping("/{member-id}/brands") + @Operation(summary = "정렬 조건에 따른 브랜드 조회", description = + """ + path variable 으로 member-id 를 보내면 해당 회원이 가지고 있는 쿠폰 브랜드 리스트를 반환합니다. + 정렬 조건은 query string 으로 sort 값을 보내주세요. {1, 2, 3} 에 따라 달라집니다. + - 1(default)번 옵션은 쿠폰 많은 순, 생성 시간이 이른 순 + - 2번 옵션은 생성 시간이 이른 순, 쿠폰 많은 순 + - 3번 옵션은 브랜드 이름 순으로 정렬하여 데이터를 반환합니다. + """) + public BaseResponse getBrands( + @PathVariable("member-id") Long memberId, + @RequestParam(required = false, defaultValue = "1", value = "sort") String sort) { + return new BaseResponse<>(memberService.getBrands(memberId, Integer.parseInt(sort))); + } } diff --git a/src/main/java/com/example/couphoneserver/dto/member/request/LoginRequestDto.java b/src/main/java/com/example/couphoneserver/dto/auth/LoginRequestDto.java similarity index 90% rename from src/main/java/com/example/couphoneserver/dto/member/request/LoginRequestDto.java rename to src/main/java/com/example/couphoneserver/dto/auth/LoginRequestDto.java index 575e294..017d546 100644 --- a/src/main/java/com/example/couphoneserver/dto/member/request/LoginRequestDto.java +++ b/src/main/java/com/example/couphoneserver/dto/auth/LoginRequestDto.java @@ -1,4 +1,4 @@ -package com.example.couphoneserver.dto.member.request; +package com.example.couphoneserver.dto.auth; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.Email; diff --git a/src/main/java/com/example/couphoneserver/dto/member/response/LoginResponseDto.java b/src/main/java/com/example/couphoneserver/dto/auth/LoginResponseDto.java similarity index 94% rename from src/main/java/com/example/couphoneserver/dto/member/response/LoginResponseDto.java rename to src/main/java/com/example/couphoneserver/dto/auth/LoginResponseDto.java index 6a6e363..8a71959 100644 --- a/src/main/java/com/example/couphoneserver/dto/member/response/LoginResponseDto.java +++ b/src/main/java/com/example/couphoneserver/dto/auth/LoginResponseDto.java @@ -1,4 +1,4 @@ -package com.example.couphoneserver.dto.member.response; +package com.example.couphoneserver.dto.auth; import com.example.couphoneserver.domain.MemberGrade; import io.swagger.v3.oas.annotations.media.Schema; diff --git a/src/main/java/com/example/couphoneserver/dto/member/request/AddMemberRequestDto.java b/src/main/java/com/example/couphoneserver/dto/member/request/AddMemberRequestDto.java deleted file mode 100644 index 8c104c5..0000000 --- a/src/main/java/com/example/couphoneserver/dto/member/request/AddMemberRequestDto.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.example.couphoneserver.dto.member.request; - -import com.example.couphoneserver.domain.entity.Member; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.Email; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Size; -import lombok.*; - -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@AllArgsConstructor -@Getter -@ToString -public class AddMemberRequestDto { - @NotNull - @Size(min=3, max=100) - @Schema(example = "김이름") - private String name; - @NotNull - @Email - @Size(min=3, max=100) - @Schema(example = "aaa@naver.com", description = "반드시 이메일 형식이어야 합니다.") - private String email; - @NotNull - @Size(min=3, max=100) - @Schema(example = "1234!@#$") - private String password; - - public Member toEntity(){ - return Member.builder() - .name(name) - .email(email) - .password(password) - .build(); - } -} \ No newline at end of file diff --git a/src/main/java/com/example/couphoneserver/dto/member/response/BrandDto.java b/src/main/java/com/example/couphoneserver/dto/member/response/BrandDto.java new file mode 100644 index 0000000..eaaed96 --- /dev/null +++ b/src/main/java/com/example/couphoneserver/dto/member/response/BrandDto.java @@ -0,0 +1,24 @@ +package com.example.couphoneserver.dto.member.response; + +import com.example.couphoneserver.domain.CouponItemStatus; +import com.example.couphoneserver.dto.brand.GetBrandResponse; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; + +import java.time.LocalDateTime; + +@Getter +public class BrandDto { + @Schema(description = "브랜드 정보") + GetBrandResponse brandInfo; + @Schema(description = "쿠폰 상태", example = "ACTIVE") + CouponItemStatus couponItemStatus; + @Schema(description = "만료 시간", example = "2024-01-29 18:35:46.434060") + LocalDateTime expiredDate; + + public BrandDto(GetBrandResponse brand, CouponItemStatus status) { + this.expiredDate = brand.getCreatedDate().plusMonths(6); + this.couponItemStatus = status; + this.brandInfo = brand; + } +} diff --git a/src/main/java/com/example/couphoneserver/dto/member/response/GetMemberCouponBrandsResponse.java b/src/main/java/com/example/couphoneserver/dto/member/response/GetMemberCouponBrandsResponse.java new file mode 100644 index 0000000..10df028 --- /dev/null +++ b/src/main/java/com/example/couphoneserver/dto/member/response/GetMemberCouponBrandsResponse.java @@ -0,0 +1,34 @@ +package com.example.couphoneserver.dto.member.response; + +import com.example.couphoneserver.domain.MemberGrade; +import com.example.couphoneserver.domain.MemberStatus; +import com.example.couphoneserver.domain.entity.Member; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; + +import java.util.List; + +@Getter +public class GetMemberCouponBrandsResponse { + @Schema(example = "1", description = "회원 아이디") + private Long id; + @Schema(example = "김이름", description = "회원 이름") + private String name; + @Schema(example = "aaa@naver.com", description = "이메일") + private String email; + @Schema(example = "ROLE_MEMBER", description = "회원 권한") + private MemberGrade memberGrade; + @Schema(example = "ACTIVE", description = "회원 상태") + private MemberStatus memberStatus; + @Schema(description = "회원 정보와, 회원이 가지고 있는 쿠폰에 대한 브랜드 목록을 반환") + private List brandInfoList; + + public GetMemberCouponBrandsResponse(Member member, List brandsInfoList) { + this.id = member.getId(); + this.name = member.getName(); + this.email = member.getEmail(); + this.memberGrade = member.getGrade(); + this.memberStatus = member.getStatus(); + this.brandInfoList= brandsInfoList; + } +} diff --git a/src/main/java/com/example/couphoneserver/dto/member/response/MemberInfoResponseDto.java b/src/main/java/com/example/couphoneserver/dto/member/response/GetMemberResponse.java similarity index 92% rename from src/main/java/com/example/couphoneserver/dto/member/response/MemberInfoResponseDto.java rename to src/main/java/com/example/couphoneserver/dto/member/response/GetMemberResponse.java index ae5b755..40cea59 100644 --- a/src/main/java/com/example/couphoneserver/dto/member/response/MemberInfoResponseDto.java +++ b/src/main/java/com/example/couphoneserver/dto/member/response/GetMemberResponse.java @@ -10,7 +10,7 @@ @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class MemberInfoResponseDto { +public class GetMemberResponse { @Schema(example = "1", description = "회원 아이디") private Long id; @Schema(example = "김이름", description = "회원 이름") @@ -22,7 +22,7 @@ public class MemberInfoResponseDto { @Schema(example = "ACTIVE", description = "회원 상태") private MemberStatus memberStatus; - public MemberInfoResponseDto(Member member) { + public GetMemberResponse(Member member) { id = member.getId(); name = member.getName(); email = member.getEmail(); diff --git a/src/main/java/com/example/couphoneserver/dto/member/response/MemberResponseDto.java b/src/main/java/com/example/couphoneserver/dto/member/response/PatchMemberResponse.java similarity index 83% rename from src/main/java/com/example/couphoneserver/dto/member/response/MemberResponseDto.java rename to src/main/java/com/example/couphoneserver/dto/member/response/PatchMemberResponse.java index f8e5ea7..4fb73c8 100644 --- a/src/main/java/com/example/couphoneserver/dto/member/response/MemberResponseDto.java +++ b/src/main/java/com/example/couphoneserver/dto/member/response/PatchMemberResponse.java @@ -9,10 +9,10 @@ @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class MemberResponseDto { +public class PatchMemberResponse { @Schema(example = "1", description = "회원 아이디") private Long id; - public MemberResponseDto(Member member) { + public PatchMemberResponse(Member member) { id = member.getId(); } } diff --git a/src/main/java/com/example/couphoneserver/repository/CouponItemRepository.java b/src/main/java/com/example/couphoneserver/repository/CouponItemRepository.java index c981e89..55da191 100644 --- a/src/main/java/com/example/couphoneserver/repository/CouponItemRepository.java +++ b/src/main/java/com/example/couphoneserver/repository/CouponItemRepository.java @@ -20,4 +20,16 @@ public interface CouponItemRepository extends JpaRepository { List findAllByMemberIdAndBrandId(Long mid, Long bid); Optional findById(Long couponId); + + // 특정 회원이 가지고 있는 유효한 쿠폰을 1. 도장 개수 많은 순, 2. 생성시간이 빠른 순으로 조회. + @Query("SELECT c FROM CouponItem c WHERE c.member.id = :memberId AND c.status <> 'EXPIRED' ORDER BY c.stampCount DESC, c.createdDate ASC") + List findByMemberIdOrderByStampCountAndCreatedDate(@Param("memberId") Long memberId); + + // 특정 회원이 가지고 있는 유효한 쿠폰을 1. 생성시간이 빠른 순, 2. 도장 개수가 많은 순으로 조회. + @Query("SELECT c FROM CouponItem c WHERE c.member.id = :memberId AND c.status <> 'EXPIRED' ORDER BY c.createdDate, c.stampCount DESC") + List findByMemberIdOrderByCreatedDateAndStampCount(@Param("memberId") Long memberId); + + // 특정 회원이 가지고 있는 유효한 쿠폰을 브랜드 이름으로 조회 + @Query("SELECT c FROM CouponItem c WHERE c.member.id = :memberId AND c.status <> 'EXPIRED' ORDER BY c.brand.name") + List findByMemberIdOrderByBrandName(@Param("memberId") Long memberId); } diff --git a/src/main/java/com/example/couphoneserver/service/MemberService.java b/src/main/java/com/example/couphoneserver/service/MemberService.java index 89a4add..b205003 100644 --- a/src/main/java/com/example/couphoneserver/service/MemberService.java +++ b/src/main/java/com/example/couphoneserver/service/MemberService.java @@ -3,12 +3,17 @@ import com.example.couphoneserver.common.exception.MemberException; import com.example.couphoneserver.domain.MemberGrade; import com.example.couphoneserver.domain.MemberStatus; +import com.example.couphoneserver.domain.entity.CouponItem; import com.example.couphoneserver.domain.entity.Member; -import com.example.couphoneserver.dto.member.request.AddMemberRequestDto; -import com.example.couphoneserver.dto.member.request.LoginRequestDto; -import com.example.couphoneserver.dto.member.response.LoginResponseDto; -import com.example.couphoneserver.dto.member.response.MemberInfoResponseDto; -import com.example.couphoneserver.dto.member.response.MemberResponseDto; +import com.example.couphoneserver.dto.auth.LoginRequestDto; +import com.example.couphoneserver.dto.auth.LoginResponseDto; +import com.example.couphoneserver.dto.brand.GetBrandResponse; +import com.example.couphoneserver.dto.member.response.BrandDto; +import com.example.couphoneserver.dto.member.response.GetMemberCouponBrandsResponse; +import com.example.couphoneserver.dto.member.response.GetMemberResponse; +import com.example.couphoneserver.dto.member.response.PatchMemberResponse; +import com.example.couphoneserver.repository.BrandRepository; +import com.example.couphoneserver.repository.CouponItemRepository; import com.example.couphoneserver.repository.MemberRepository; import com.example.couphoneserver.utils.jwt.JwtTokenProvider; import lombok.RequiredArgsConstructor; @@ -38,25 +43,14 @@ @RequiredArgsConstructor public class MemberService { private final MemberRepository memberRepository; + private final CouponItemRepository couponItemRepository; + + private final BrandRepository brandRepository; private final BCryptPasswordEncoder bCryptPasswordEncoder; private final JwtTokenProvider jwtProvider; private final AuthenticationManagerBuilder authenticationManagerBuilder; private final RefreshTokenService refreshTokenService; - /** - * 이메일로 회원 가입 - */ - @Transactional - public MemberResponseDto save(AddMemberRequestDto dto) throws UsernameNotFoundException { - validateDuplicateMemberByEmail(dto.getEmail()); - validateDuplicateMemberByName(dto.getName()); - Member savedMember = memberRepository.save( - new Member(dto.getName(), dto.getEmail(), bCryptPasswordEncoder.encode(dto.getPassword()), - MemberStatus.ACTIVE, MemberGrade.ROLE_MEMBER) - ); - return new MemberResponseDto(savedMember); - } - @Transactional public void saveByEmailAndName(LoginRequestDto dto) throws UsernameNotFoundException { validateDuplicateMemberByEmail(dto.getEmail()); @@ -96,9 +90,9 @@ public Long join(Member member) { * 회원 탈퇴 처리 */ @Transactional - public MemberResponseDto delete(Member member) { + public PatchMemberResponse delete(Member member) { member.setTerminated(); - return new MemberResponseDto(member); + return new PatchMemberResponse(member); } @Transactional @@ -131,8 +125,8 @@ public Member findOneByEmail(String email) { /** * 단일 회원 정보 조회 */ - public MemberInfoResponseDto getMemberInfo(Member member) { - return new MemberInfoResponseDto(member); + public GetMemberResponse getMemberInfo(Member member) { + return new GetMemberResponse(member); } /** @@ -185,4 +179,31 @@ public Optional getMemberByEmail(String email) { } + public GetMemberCouponBrandsResponse getBrands(Long memberId, int option) { + // 회원이 가지고 있는 쿠폰의 브랜드 정보 리스트와 회원 정보를 같이 반환합니다. + // 이때 정렬 기준은 option 에 따라서 달라집니다. (default 는 쿠폰 많은 순, 생성시간 이른 순) + List coupons; + switch (option) { + case 2 -> { + log.info("[정렬 필터 옵션2번]"); + coupons = couponItemRepository.findByMemberIdOrderByCreatedDateAndStampCount(memberId); + } + case 3 -> { + log.info("[정렬 필터 옵션3번]"); + coupons = couponItemRepository.findByMemberIdOrderByBrandName(memberId); + } + default -> { + log.info("[정렬 필터 옵션1번]"); + coupons = couponItemRepository.findByMemberIdOrderByStampCountAndCreatedDate(memberId); + } + } + List brands = coupons.stream().map(coupon -> { + GetBrandResponse brandInfo = new GetBrandResponse(coupon.getBrand(), coupon.getStampCount()); + return new BrandDto(brandInfo, coupon.getStatus()); + }).toList(); + + Member member = findOneById(memberId); + return new GetMemberCouponBrandsResponse(member, brands); + } + } \ No newline at end of file diff --git a/src/test/java/com/example/couphoneserver/domain/service/MemberServiceTest.java b/src/test/java/com/example/couphoneserver/domain/service/MemberServiceTest.java index bd65ae2..9187f6f 100644 --- a/src/test/java/com/example/couphoneserver/domain/service/MemberServiceTest.java +++ b/src/test/java/com/example/couphoneserver/domain/service/MemberServiceTest.java @@ -3,8 +3,8 @@ import com.example.couphoneserver.common.exception.MemberException; import com.example.couphoneserver.domain.MemberStatus; import com.example.couphoneserver.domain.entity.Member; -import com.example.couphoneserver.dto.member.request.LoginRequestDto; -import com.example.couphoneserver.dto.member.response.LoginResponseDto; +import com.example.couphoneserver.dto.auth.LoginRequestDto; +import com.example.couphoneserver.dto.auth.LoginResponseDto; import com.example.couphoneserver.repository.BrandRepository; import com.example.couphoneserver.repository.MemberRepository; import com.example.couphoneserver.repository.StoreRepository;