Skip to content

Commit

Permalink
Merge pull request #33 from Team-UMC/feature/#28/invite-api
Browse files Browse the repository at this point in the history
[FEAT] ์ดˆ๋Œ€ ์ฝ”๋“œ ๊ด€๋ จ API ๊ตฌํ˜„
  • Loading branch information
junseokkim authored Jan 23, 2024
2 parents 89ca2bd + 262039d commit 45b0e41
Show file tree
Hide file tree
Showing 27 changed files with 568 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public Member getMember() {
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority(member.getRole().toString()));
authorities.add(new SimpleGrantedAuthority("ROLE_" + member.getRole().toString()));
return authorities;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.umc.networkingService.domain.invite.controller;

import com.umc.networkingService.config.security.auth.CurrentMember;
import com.umc.networkingService.domain.invite.dto.response.InviteAuthenticateResponse;
import com.umc.networkingService.domain.invite.dto.response.InviteCreateResponse;
import com.umc.networkingService.domain.invite.dto.response.InviteInquiryMineResponse;
import com.umc.networkingService.domain.invite.service.InviteService;
import com.umc.networkingService.domain.member.entity.Member;
import com.umc.networkingService.global.common.base.BaseResponse;
import com.umc.networkingService.global.common.enums.Role;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@Tag(name = "์ดˆ๋Œ€ API", description = "์ดˆ๋Œ€ ๊ด€๋ จ API")
@RestController
@RequiredArgsConstructor
public class InviteController {

private final InviteService inviteService;

@Operation(summary = "์ดˆ๋Œ€ ์ฝ”๋“œ ๋ฐœ๊ธ‰ API", description = "์ผ๋ฐ˜ ๋ถ€์›์šฉ ๋˜๋Š” ์šด์˜์ง„์šฉ ์ดˆ๋Œ€ ์ฝ”๋“œ ๋ฐœ๊ธ‰ API์ž…๋‹ˆ๋‹ค.")
@ApiResponses( value = {
@ApiResponse(responseCode = "COMMON200", description = "์„ฑ๊ณต"),
@ApiResponse(responseCode = "INVITE002", description = "์ƒ์œ„์˜ ์—ญํ• ์— ๋Œ€ํ•ด์„œ ์ดˆ๋Œ€ ์ฝ”๋“œ๋ฅผ ๋ฐœ๊ธ‰ํ•  ๊ฒฝ์šฐ ๋ฐœ์ƒ")
})
@Parameter(name = "role", description = "์ดˆ๋Œ€ ์ฝ”๋“œ์— ๋ถ€์—ฌํ•  ์—ญํ• ์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.(ex. MEMBER, STAFF, CAMPUS_STAFF ...)")
@PostMapping("/staff/invites")
public BaseResponse<InviteCreateResponse> createInviteCode(@CurrentMember Member member,
@RequestParam Role role) {
return BaseResponse.onSuccess(inviteService.createInviteCode(member, role));
}

@Operation(summary = "๋‚˜์˜ ์ดˆ๋Œ€ ์ฝ”๋“œ ์กฐํšŒ API", description = "๋ณธ์ธ์ด ์ƒ์„ฑํ•œ ์ดˆ๋Œ€ ์ฝ”๋“œ ๋ชฉ๋ก ์กฐํšŒ API์ž…๋‹ˆ๋‹ค.")
@ApiResponses( value = {
@ApiResponse(responseCode = "COMMON200", description = "์„ฑ๊ณต")
})
@GetMapping("/staff/invites")
public BaseResponse<List<InviteInquiryMineResponse>> inquiryMyInviteCode(@CurrentMember Member member) {
return BaseResponse.onSuccess(inviteService.inquiryMyInviteCode(member));
}

@Operation(summary = "์ดˆ๋Œ€ ์ฝ”๋“œ ํ™•์ธ API", description = "๋ฐœ๊ธ‰ ๋ฐ›์€ ์ดˆ๋Œ€ ์ฝ”๋“œ๋ฅผ ๊ฒ€์ฆํ•˜๋Š” API์ž…๋‹ˆ๋‹ค.")
@ApiResponses( value = {
@ApiResponse(responseCode = "COMMON200", description = "์„ฑ๊ณต"),
@ApiResponse(responseCode = "INVITE001", description = "๋งŒ๋ฃŒ๋œ ์ดˆ๋Œ€ ์ฝ”๋“œ์ผ ๊ฒฝ์šฐ ๋ฐœ์ƒ")
})
@Parameter(name = "inviteCode", in = ParameterIn.PATH, description = "๋ฐœ๊ธ‰ ๋ฐ›์€ ์ดˆ๋Œ€ ์ฝ”๋“œ๋ฅผ ์ž…๋ ฅํ•˜๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ์ž…๋‹ˆ๋‹ค.")
@PostMapping("/invites/{inviteCode}")
public BaseResponse<InviteAuthenticateResponse> authenticationInviteCode(@CurrentMember Member member,
@PathVariable String inviteCode) {
return BaseResponse.onSuccess(inviteService.authenticateInviteCode(member, inviteCode));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.umc.networkingService.domain.invite.dto.response;

import com.umc.networkingService.global.common.enums.Role;
import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class InviteAuthenticateResponse {
private Role role;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.umc.networkingService.domain.invite.dto.response;

import com.umc.networkingService.global.common.enums.Role;
import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class InviteCreateResponse {
private String inviteCode;
private Role role;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.umc.networkingService.domain.invite.dto.response;

import com.umc.networkingService.global.common.enums.Role;
import lombok.AllArgsConstructor;
import lombok.Getter;

import java.time.LocalDateTime;

@Getter
@AllArgsConstructor
public class InviteInquiryMineResponse {
private String inviteCode;
private Role role;
private LocalDateTime createdAt;
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
package com.umc.networkingService.domain.inviteCode.entity;
package com.umc.networkingService.domain.invite.entity;

import com.umc.networkingService.domain.member.entity.Member;
import com.umc.networkingService.global.common.base.BaseEntity;
import com.umc.networkingService.global.common.enums.Role;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.*;
import org.hibernate.annotations.SQLRestriction;
import org.hibernate.annotations.UuidGenerator;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.UUID;

@Getter
@Entity
@Builder
@AllArgsConstructor
@NoArgsConstructor(access= AccessLevel.PROTECTED)
@SQLRestriction("deleted_at is null")
public class InviteCode extends BaseEntity {
public class Invite extends BaseEntity {
@Id
@UuidGenerator
@Column(name = "invite_code_id")
Expand All @@ -32,4 +34,9 @@ public class InviteCode extends BaseEntity {

@Column(nullable = false)
private String code;

public boolean isExpired() {
LocalDateTime expiresAt = this.getCreatedAt().plus(3, ChronoUnit.DAYS);
return LocalDateTime.now().isAfter(expiresAt);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.umc.networkingService.domain.invite.mapper;

import com.umc.networkingService.domain.invite.dto.response.InviteInquiryMineResponse;
import com.umc.networkingService.domain.invite.entity.Invite;
import org.springframework.stereotype.Component;

@Component
public class InviteMapper {

public InviteInquiryMineResponse toInquiryMineResponse(Invite invite) {
return new InviteInquiryMineResponse(invite.getCode(), invite.getRole(), invite.getCreatedAt());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.umc.networkingService.domain.invite.repository;

import com.umc.networkingService.domain.invite.entity.Invite;
import com.umc.networkingService.domain.member.entity.Member;
import com.umc.networkingService.global.common.enums.Role;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;
import java.util.Optional;
import java.util.UUID;

public interface InviteRepository extends JpaRepository<Invite, UUID> {

Optional<Invite> findByMemberAndRole(Member member, Role role);
Optional<Invite> findByCode(String code);

List<Invite> findAllByMember(Member member);
boolean existsByCode(String code);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.umc.networkingService.domain.invite.service;

import com.umc.networkingService.domain.invite.dto.response.InviteAuthenticateResponse;
import com.umc.networkingService.domain.invite.dto.response.InviteCreateResponse;
import com.umc.networkingService.domain.invite.dto.response.InviteInquiryMineResponse;
import com.umc.networkingService.domain.member.entity.Member;
import com.umc.networkingService.global.common.enums.Role;

import java.util.List;

public interface InviteService {
InviteCreateResponse createInviteCode(Member member, Role role);
InviteAuthenticateResponse authenticateInviteCode(Member member, String inviteCode);
List<InviteInquiryMineResponse> inquiryMyInviteCode(Member member);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package com.umc.networkingService.domain.invite.service;

import com.umc.networkingService.domain.invite.dto.response.InviteAuthenticateResponse;
import com.umc.networkingService.domain.invite.dto.response.InviteCreateResponse;
import com.umc.networkingService.domain.invite.dto.response.InviteInquiryMineResponse;
import com.umc.networkingService.domain.invite.entity.Invite;
import com.umc.networkingService.domain.invite.mapper.InviteMapper;
import com.umc.networkingService.domain.invite.repository.InviteRepository;
import com.umc.networkingService.domain.member.entity.Member;
import com.umc.networkingService.domain.member.service.MemberService;
import com.umc.networkingService.global.common.base.BaseEntity;
import com.umc.networkingService.global.common.enums.Role;
import com.umc.networkingService.global.common.exception.ErrorCode;
import com.umc.networkingService.global.common.exception.RestApiException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;
import java.util.UUID;

@Service
@RequiredArgsConstructor
public class InviteServiceImpl implements InviteService {

private final InviteMapper inviteMapper;

private final InviteRepository inviteRepository;
private final MemberService memberService;

// ์ดˆ๋Œ€ ์ฝ”๋“œ ์ƒ์„ฑ ํ•จ์ˆ˜
@Override
public InviteCreateResponse createInviteCode(Member member, Role role) {
checkRolePriority(member, role);
deleteExistingInvite(member, role);
String inviteCode = generateUniqueInviteCode();
Invite savedInvite = saveInvite(member, role, inviteCode);
return new InviteCreateResponse(savedInvite.getCode(), savedInvite.getRole());
}

// ์ดˆ๋Œ€ ์ฝ”๋“œ ์ธ์ฆ ํ•จ์ˆ˜
@Override
@Transactional
public InviteAuthenticateResponse authenticateInviteCode(Member member, String inviteCode) {
// ์กด์žฌํ•˜์ง€ ์•Š๋Š” ์ดˆ๋Œ€ ์ฝ”๋“œ์ธ ๊ฒฝ์šฐ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ
Invite savedInvite = inviteRepository.findByCode(inviteCode)
.orElseThrow(() -> new RestApiException(ErrorCode.EXPIRED_INVITE_CODE));

// ๋งŒ๋ฃŒ๋œ ์ดˆ๋Œ€ ์ฝ”๋“œ์ธ ๊ฒฝ์šฐ ์‚ญ์ œ ํ›„ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ
if (savedInvite.isExpired()) {
savedInvite.delete();
throw new RestApiException(ErrorCode.EXPIRED_INVITE_CODE);
}

// ์ดˆ๋Œ€ ์ฝ”๋“œ์— ๋ถ€์—ฌ๋œ ์—ญํ•  ๋ถ€์—ฌ
member.updateRole(savedInvite.getRole());
Member savedMember = memberService.saveEntity(member);

return new InviteAuthenticateResponse(savedMember.getRole());
}

// ๋‚˜์˜ ์ดˆ๋Œ€ ์ฝ”๋“œ ์กฐํšŒ ํ•จ์ˆ˜
@Override
public List<InviteInquiryMineResponse> inquiryMyInviteCode(Member member) {
// ๋ณธ์ธ์ด ์ƒ์„ฑํ•œ ์ดˆ๋Œ€ ์ฝ”๋“œ ์กฐํšŒ
List<Invite> savedInvites = inviteRepository.findAllByMember(member);

return savedInvites.stream()
.map(inviteMapper::toInquiryMineResponse)
.toList();
}

// ๋ณธ์ธ์˜ ์—ญํ•  ์ด์ƒ์˜ ์—ญํ•  ๋ถ€์—ฌ๋ฅผ ํ™•์ธํ•˜๋Š” ํ•จ์ˆ˜
private void checkRolePriority(Member member, Role role) {
if (member.getRole().getPriority() >= role.getPriority()) {
throw new RestApiException(ErrorCode.UNAUTHORIZED_CREATE_INVITE);
}
}

// ์กด์žฌํ•˜๋Š” ์ดˆ๋Œ€ ์ฝ”๋“œ ์‚ญ์ œ ํ•จ์ˆ˜
private void deleteExistingInvite(Member member, Role role) {
Optional<Invite> oldInvite = inviteRepository.findByMemberAndRole(member, role);
oldInvite.ifPresent(BaseEntity::delete);
}

// ์ค‘๋ณต๋˜์ง€ ์•Š๋Š” ์ดˆ๋Œ€ ์ฝ”๋“œ ์ƒ์„ฑ ํ•จ์ˆ˜
private String generateUniqueInviteCode() {
String inviteCode;
do {
inviteCode = UUID.randomUUID().toString().replace("-", "").substring(0, 10);
} while (inviteRepository.existsByCode(inviteCode));
return inviteCode;
}

// ์ดˆ๋Œ€ ์ฝ”๋“œ ์ €์žฅ ํ•จ์ˆ˜
private Invite saveInvite(Member member, Role role, String inviteCode) {
return inviteRepository.save(
Invite.builder()
.member(member)
.code(inviteCode)
.role(role)
.build()
);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@ public BaseResponse<MemberInquiryInfoWithPointResponse> inquiryHomeInfo(@Current
@ApiResponse(responseCode = "AUTH008", description = "๊นƒํ—ˆ๋ธŒ ์„œ๋ฒ„์™€ ํ†ต์‹  ์‹คํŒจํ•  ๊ฒฝ์šฐ ๋ฐœ์ƒ")
})
@PostMapping("/github")
public BaseResponse<MemberAuthenticationGithubResponse> authenticationGithub(@CurrentMember Member member,
@RequestParam String code) {
return BaseResponse.onSuccess(memberService.authenticationGithub(member, code));
public BaseResponse<MemberAuthenticateGithubResponse> authenticationGithub(@CurrentMember Member member,
@RequestParam String code) {
return BaseResponse.onSuccess(memberService.authenticateGithub(member, code));
}

@Operation(summary = "๊นƒํ—ˆ๋ธŒ ๋ฐ์ดํ„ฐ ์กฐํšŒ API", description = "๊นƒํ—ˆ๋ธŒ ์ž”๋”” ์ด๋ฏธ์ง€๋ฅผ ์กฐํšŒํ•˜๋Š” API์ž…๋‹ˆ๋‹ค.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@

@Getter
@AllArgsConstructor
public class MemberAuthenticationGithubResponse {
public class MemberAuthenticateGithubResponse {
private String githubNickname;
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,7 @@ public void updatePositions(List<MemberPosition> memberPositions) {
public void updateSemesterParts(List<SemesterPart> semesterParts) {
this.semesterParts = semesterParts;
}

public void authenticationGithub(String gitNickname) {
public void authenticateGithub(String gitNickname) {
this.gitNickname = gitNickname;
}

Expand All @@ -105,7 +104,6 @@ public void updateContributionPoint(Long usedPoint) {
else this.contributionPoint += usedPoint;
}

// ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์šฉ
public void updateRole(Role role) {
this.role = role;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import java.util.UUID;


public interface AuthService extends EntityLoader<Member, UUID> {
MemberLoginResponse socialLogin(final String accessToken, SocialType socialType);
MemberIdResponse signUp(Member member, MemberSignUpRequest request);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
@RequiredArgsConstructor
public class AuthServiceImpl implements AuthService {

private final MemberService memberService;

private final MemberMapper memberMapper;
private final MemberRepository memberRepository;

Expand Down Expand Up @@ -85,7 +87,7 @@ public MemberIdResponse signUp(Member loginMember, MemberSignUpRequest request)
member.setMemberInfo(request.getName(), request.getNickname(),
university, branchUniversityService.findBranchByUniversity(university));

return new MemberIdResponse(memberRepository.save(member).getId());
return new MemberIdResponse(memberService.saveEntity(member).getId());
}

// ์ƒˆ๋กœ์šด ์•ก์„ธ์Šค ํ† ํฐ ๋ฐœ๊ธ‰ ํ•จ์ˆ˜
Expand Down Expand Up @@ -119,6 +121,7 @@ public MemberIdResponse logout(Member loginMember) {
@Override
@Transactional
public MemberIdResponse withdrawal(Member loginMember) {
// ๋ฉค๋ฒ„ soft delete
Member member = loadEntity(loginMember.getId());

// refreshToken ์‚ญ์ œ
Expand Down Expand Up @@ -155,7 +158,8 @@ private MemberLoginResponse loginByKakao(final String accessToken){
return saveNewMember(clientId, SocialType.KAKAO);
}
// 2. ์žˆ์œผ๋ฉด : ์ƒˆ๋กœ์šด ํ† ํฐ ๋ฐ˜ํ™˜
return getNewToken(getMember.get(), true);
boolean isServiceMember = getMember.get().getName() != null;
return getNewToken(getMember.get(), isServiceMember);
}

private MemberLoginResponse loginByNaver(final String accessToken){
Expand Down
Loading

0 comments on commit 45b0e41

Please sign in to comment.