Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feat/186] 매칭 우선순위 API 구현 #189

Merged
merged 28 commits into from
Feb 4, 2025
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e8d4ae1
:sparkles: [Feat] 매칭 API 클래스 구조 설계 및 계산 클래스 구현
rimi3226 Jan 22, 2025
c56a307
:sparkles: [Feat] 각 게임모드마다 달라지는 계산을 구현, MatchingPriorityCalculateSer…
rimi3226 Jan 22, 2025
374883e
:sparkles: [Feat] Mike ENUM 값 변경 및, 매칭 우선순위 계산 로직 작성
rimi3226 Jan 23, 2025
00b4ee8
:sparkles: [Feat] 빠른 대전 제외, MatchingPriorityCalculateService 매칭 타입, 게…
rimi3226 Jan 23, 2025
bb8e081
:sparkles: [Feat] 매칭 우선순위 API DTO 생성
rimi3226 Jan 25, 2025
b4f2e94
:sparkles: [Feat] 매칭 우선순위 API DTO 생성
rimi3226 Jan 25, 2025
14b3e7f
:sparkles: [Feat] GameStyle 변경하는 로직 MemberFacadeService가 아닌 MemberSer…
rimi3226 Jan 25, 2025
d6f06e7
:sparkles: [Feat] setGameStyle Service 수정
rimi3226 Jan 25, 2025
e2996c9
:sparkles: [Feat] 매칭 정보로 Member 업데이트
rimi3226 Jan 25, 2025
3e9ca64
:sparkles: [Feat] 매칭 우선순위 계산 Facade Service 구현
rimi3226 Jan 26, 2025
3c2b9db
:sparkles: [Feat] 제한 조건 추가
rimi3226 Jan 26, 2025
87d435b
:bug: [fix] 우선순위 API 구현 안되는거 해결
rimi3226 Jan 26, 2025
d2c828c
:bug: [fix] PENDING->Pending, MemberService, MemberGameStyleService 분리
rimi3226 Jan 26, 2025
52ce6d9
:sparkles: [Feat] 겜구매칭 DB 조회할 때, 조건 제외하고 조회하도록 수정
rimi3226 Feb 1, 2025
2ed159f
:sparkles: [Feat] 주석 추가
rimi3226 Feb 1, 2025
6cf005d
:sparkles: [Feat] 주석 추가
rimi3226 Feb 2, 2025
ea3641b
:white_check_mark: [Test] MatchingScoreCalculatorTest 테스트 구현
rimi3226 Feb 2, 2025
bca80d4
:white_check_mark: [Test] MatchingStrategyProcessorTest 테스트 구현
rimi3226 Feb 2, 2025
2b95339
:white_check_mark: [Test] 필드명 수정
rimi3226 Feb 2, 2025
ebba37a
:white_check_mark: [Test] MatchingService 매칭 리스트 조회 테스트 구현
rimi3226 Feb 4, 2025
e4749ff
:white_check_mark: [Test] MatchingService 매칭 게임모드, 매칭타입에 따른 우선순위 계산 테…
rimi3226 Feb 4, 2025
2caa5aa
:white_check_mark: [Test] MatchingServiceTest 대기 중인 매칭 리스트 조회 구현
rimi3226 Feb 4, 2025
721f2c7
:white_check_mark: [Test] MatchingServiceTest 매칭 기록 생성 테스트 구현
rimi3226 Feb 4, 2025
a069368
:sparkles: [Feat] 특정 사용자의 가장 최근 matchingRecord 조회
rimi3226 Feb 4, 2025
d4e7dc0
:white_check_mark: [Test] MatchingFacadeServiceTest 구현
rimi3226 Feb 4, 2025
e10de4b
:bug: [Fix] 불필요한 JPA 삭제
rimi3226 Feb 4, 2025
812aced
:white_check_mark: [Test] MatchingFacadeServiceTest 수정
rimi3226 Feb 4, 2025
93002d1
:recycle: [refactor] MatchingScoreCalculator static 변경
rimi3226 Feb 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -178,4 +178,11 @@ public Double updateMannerRank(Double mannerRank) {
return this.mannerRank;
}

public void updateMemberByMatchingRecord(Mike mike, Position mainP, Position subP, Position wantP) {
this.mike = mike;
this.mainPosition = mainP;
this.subPosition = subP;
this.wantPosition = wantP;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@

public enum Mike {
UNAVAILABLE,
ONLY_LISTEN,
AVAILABLE,
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,18 @@
package com.gamegoo.gamegoo_v2.account.member.service;

import com.gamegoo.gamegoo_v2.account.member.domain.Member;
import com.gamegoo.gamegoo_v2.account.member.domain.MemberGameStyle;
import com.gamegoo.gamegoo_v2.account.member.dto.request.GameStyleRequest;
import com.gamegoo.gamegoo_v2.account.member.dto.request.IsMikeRequest;
import com.gamegoo.gamegoo_v2.account.member.dto.request.PositionRequest;
import com.gamegoo.gamegoo_v2.account.member.dto.request.ProfileImageRequest;
import com.gamegoo.gamegoo_v2.account.member.dto.response.MyProfileResponse;
import com.gamegoo.gamegoo_v2.account.member.dto.response.OtherProfileResponse;
import com.gamegoo.gamegoo_v2.game.domain.GameStyle;
import com.gamegoo.gamegoo_v2.social.block.service.BlockService;
import com.gamegoo.gamegoo_v2.social.friend.service.FriendService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
Expand Down Expand Up @@ -114,18 +110,7 @@ public String setPosition(Member member, PositionRequest request) {
*/
@Transactional
public String setGameStyle(Member member, GameStyleRequest request) {
// request의 Gamestyle 조회
List<GameStyle> requestGameStyleList = memberService.findRequestGameStyle(request);

// 현재 DB의 GameStyle 조회
List<MemberGameStyle> currentMemberGameStyleList = memberService.findCurrentMemberGameStyleList(member);

// request에 없고, DB에 있는 GameStyle 삭제
memberService.removeUnnecessaryGameStyles(member, requestGameStyleList, currentMemberGameStyleList);

// request에 있고, DB에 없는 GameStyle 추가
memberService.addNewGameStyles(member, requestGameStyleList, currentMemberGameStyleList);

memberService.updateGameStyle(member, request.getGameStyleIdList());
return "게임 스타일 수정이 완료되었습니다";
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package com.gamegoo.gamegoo_v2.account.member.service;

import com.gamegoo.gamegoo_v2.account.member.domain.Member;
import com.gamegoo.gamegoo_v2.account.member.domain.MemberGameStyle;
import com.gamegoo.gamegoo_v2.account.member.repository.MemberGameStyleRepository;
import com.gamegoo.gamegoo_v2.core.exception.MemberException;
import com.gamegoo.gamegoo_v2.core.exception.common.ErrorCode;
import com.gamegoo.gamegoo_v2.game.domain.GameStyle;
import com.gamegoo.gamegoo_v2.game.repository.GameStyleRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

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

private final GameStyleRepository gameStyleRepository;
private final MemberGameStyleRepository memberGameStyleRepository;


/**
* request id로 GameStyle Entity 조회
*
* @return request의 GamestyleList
*/
public List<GameStyle> findRequestGameStyle(List<Long> gameStyleIdList) {
return gameStyleIdList.stream()
.map(id -> gameStyleRepository.findById(id).orElseThrow(() -> new MemberException(ErrorCode.GAMESTYLE_NOT_FOUND)))
.toList();
}

/**
* 현재 DB의 MemberGameStyle List 조회
*
* @return MemberGameStyleList
*/
public List<MemberGameStyle> findCurrentMemberGameStyleList(Member member) {
return new ArrayList<>(member.getMemberGameStyleList());
}

/**
* 불필요한 GameStyle 제거
*
* @param member 회원
* @param requestedGameStyles 새로운 GameStyle
* @param currentMemberGameStyles 현재 Gamestyle
*/
@Transactional
public void removeUnnecessaryGameStyles(Member member, List<GameStyle> requestedGameStyles,
List<MemberGameStyle> currentMemberGameStyles) {
currentMemberGameStyles.stream()
.filter(mgs -> !requestedGameStyles.contains(mgs.getGameStyle()))
.forEach(mgs -> {
mgs.removeMember(member);
memberGameStyleRepository.delete(mgs);
});
}

/**
* 새로운 GameStyle 추가
*
* @param member 사용자
* @param requestedGameStyles 변경 후 게임스타일
* @param currentMemberGameStyles 변경 전 게임스타일
*/
@Transactional
public void addNewGameStyles(Member member, List<GameStyle> requestedGameStyles,
List<MemberGameStyle> currentMemberGameStyles) {
List<GameStyle> currentGameStyles = currentMemberGameStyles.stream()
.map(MemberGameStyle::getGameStyle)
.toList();

requestedGameStyles.stream()
.filter(gs -> !currentGameStyles.contains(gs))
.forEach(gs -> {
MemberGameStyle newMemberGameStyle = MemberGameStyle.create(gs, member);
memberGameStyleRepository.save(newMemberGameStyle);
});
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,15 @@
import com.gamegoo.gamegoo_v2.account.member.domain.Mike;
import com.gamegoo.gamegoo_v2.account.member.domain.Position;
import com.gamegoo.gamegoo_v2.account.member.domain.Tier;
import com.gamegoo.gamegoo_v2.account.member.dto.request.GameStyleRequest;
import com.gamegoo.gamegoo_v2.account.member.repository.MemberGameStyleRepository;
import com.gamegoo.gamegoo_v2.account.member.repository.MemberRepository;
import com.gamegoo.gamegoo_v2.core.exception.MemberException;
import com.gamegoo.gamegoo_v2.core.exception.common.ErrorCode;
import com.gamegoo.gamegoo_v2.game.domain.GameStyle;
import com.gamegoo.gamegoo_v2.game.repository.GameStyleRepository;
import com.gamegoo.gamegoo_v2.utils.PasswordUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

@Service
Expand All @@ -27,8 +23,7 @@
public class MemberService {

private final MemberRepository memberRepository;
private final GameStyleRepository gameStyleRepository;
private final MemberGameStyleRepository memberGameStyleRepository;
private final MemberGameStyleService memberGameStyleService;

/**
* Member 생성 메소드
Expand Down Expand Up @@ -131,63 +126,29 @@ public void setPosition(Member member, Position mainPosition, Position subPositi
}

/**
* request id로 GameStyle Entity 조회
*
* @return request의 GamestyleList
* @param member 회원
* @param gameStyleIdList 수정할 게임 스타일 리스트
*/
public List<GameStyle> findRequestGameStyle(GameStyleRequest request) {
return request.getGameStyleIdList().stream()
.map(id -> gameStyleRepository.findById(id).orElseThrow(() -> new MemberException(ErrorCode.GAMESTYLE_NOT_FOUND)))
.toList();
}
@Transactional
public void updateGameStyle(Member member, List<Long> gameStyleIdList) {
// request의 Gamestyle 조회
List<GameStyle> requestGameStyleList = memberGameStyleService.findRequestGameStyle(gameStyleIdList);

/**
* 현재 DB의 MemberGameStyle List 조회
*
* @return MemberGameStyleList
*/
public List<MemberGameStyle> findCurrentMemberGameStyleList(Member member) {
return new ArrayList<>(member.getMemberGameStyleList());
}
// 현재 DB의 GameStyle 조회
List<MemberGameStyle> currentMemberGameStyleList =
memberGameStyleService.findCurrentMemberGameStyleList(member);

/**
* 불필요한 GameStyle 제거
*
* @param member 회원
* @param requestedGameStyles 새로운 GameStyle
* @param currentMemberGameStyles 현재 Gamestyle
*/
@Transactional
public void removeUnnecessaryGameStyles(Member member, List<GameStyle> requestedGameStyles,
List<MemberGameStyle> currentMemberGameStyles) {
currentMemberGameStyles.stream()
.filter(mgs -> !requestedGameStyles.contains(mgs.getGameStyle()))
.forEach(mgs -> {
mgs.removeMember(member); // Remove bidirectional relationship
memberGameStyleRepository.delete(mgs);
});
// request에 없고, DB에 있는 GameStyle 삭제
memberGameStyleService.removeUnnecessaryGameStyles(member, requestGameStyleList, currentMemberGameStyleList);

// request에 있고, DB에 없는 GameStyle 추가
memberGameStyleService.addNewGameStyles(member, requestGameStyleList, currentMemberGameStyleList);
}

/**
* 새로운 GameStyle 추가
*
* @param member 사용자
* @param requestedGameStyles 변경 후 게임스타일
* @param currentMemberGameStyles 변경 전 게임스타일
*/
@Transactional
public void addNewGameStyles(Member member, List<GameStyle> requestedGameStyles,
List<MemberGameStyle> currentMemberGameStyles) {
List<GameStyle> currentGameStyles = currentMemberGameStyles.stream()
.map(MemberGameStyle::getGameStyle)
.toList();

requestedGameStyles.stream()
.filter(gs -> !currentGameStyles.contains(gs))
.forEach(gs -> {
MemberGameStyle newMemberGameStyle = MemberGameStyle.create(gs, member);
memberGameStyleRepository.save(newMemberGameStyle);
});
public void updateMikePosition(Member member, Mike mike, Position mainP, Position subP, Position wantP) {
// 마이크, 포지션 수정
member.updateMemberByMatchingRecord(mike, mainP, subP, wantP);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.gamegoo.gamegoo_v2.matching.Controller;

import com.gamegoo.gamegoo_v2.account.auth.annotation.AuthMember;
import com.gamegoo.gamegoo_v2.account.member.domain.Member;
import com.gamegoo.gamegoo_v2.core.common.ApiResponse;
import com.gamegoo.gamegoo_v2.matching.dto.request.InitializingMatchingRequest;
import com.gamegoo.gamegoo_v2.matching.dto.response.PriorityListResponse;
import com.gamegoo.gamegoo_v2.matching.service.MatchingFacadeService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Tag(name = "Matching", description = "매칭 정보 관련 API")
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v2/internal/matching")
@Validated
public class MatchingController {

private final MatchingFacadeService matchingFacadeService;

@Operation(summary = "매칭 우선순위 계산 및 기록 저장 API", description = """
API for calculating and recording matching
gameMode: FAST, SOLO, FREE, ARAM string 을 넣어주세요.\s
mike: UNAVAILABLE 또는 AVAILABLE 를 넣어주세요.\s
matchingType: "BASIC" 또는 "PRECISE"를 넣어주세요. \s
mainP: ANY, TOP, JUNGLE, MID, ADC, SUP string 을 넣어주세요.\s
subP: ANY, TOP, JUNGLE, MID, ADC, SUP string 을 넣어주세요.\s
wantP: ANY, TOP, JUNGLE, MID, ADC, SUP string 을 넣어주세요.\s
gameStyleList: 1 ~ 17 int 를 넣어주세요.""")
@GetMapping
public ApiResponse<PriorityListResponse> InitializeMatching(@AuthMember Member member,
InitializingMatchingRequest request) {
return ApiResponse.ok(matchingFacadeService.calculatePriorityAndRecording(member, request));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Index;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
Expand All @@ -23,6 +25,13 @@
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(
indexes = {
@Index(name = "idx_matching_record_created_at_status_game_mode", columnList = "createdAt, status, " +
"gameMode"),
@Index(name = "idx_matching_record_member_id", columnList = "member_id")
}
)
public class MatchingRecord extends BaseDateTimeEntity {

@Id
Expand Down Expand Up @@ -93,24 +102,21 @@ public class MatchingRecord extends BaseDateTimeEntity {
private Member targetMember;

// MatchingRecord 생성 메서드
public static MatchingRecord create(GameMode gameMode, Position mainPosition, Position subPosition,
Position wantPosition, Mike mike, Tier soloTier, int soloRank,
double soloWinRate, Tier freeTier, int freeRank, double freeWinRate,
MatchingType matchingType, int mannerLevel, Member member) {
public static MatchingRecord create(GameMode gameMode, MatchingType matchingType, Member member) {
return MatchingRecord.builder()
.gameMode(gameMode)
.mainPosition(mainPosition)
.subPosition(subPosition)
.wantPosition(wantPosition)
.mike(mike)
.soloTier(soloTier)
.soloRank(soloRank)
.soloWinRate(soloWinRate)
.freeTier(freeTier)
.freeRank(freeRank)
.freeWinRate(freeWinRate)
.mainPosition(member.getMainPosition())
.subPosition(member.getSubPosition())
.wantPosition(member.getWantPosition())
.mike(member.getMike())
.soloTier(member.getTier()) // TODO:
.soloRank(member.getGameRank()) // TODO:
.soloWinRate(member.getWinRate()) // TODO:
.freeTier(member.getTier()) // TODO:
.freeRank(member.getGameRank()) // TODO:
.freeWinRate(member.getWinRate()) // TODO:
.matchingType(matchingType)
.mannerLevel(mannerLevel)
.mannerLevel(member.getMannerLevel())
.member(member)
.build();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.gamegoo.gamegoo_v2.matching.dto;

import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class PriorityValue {

Long memberId;
String matchingUuid;
int priorityValue;

public static PriorityValue of(Long memberId, String matchingUuid, int priorityValue) {
return PriorityValue.builder()
.memberId(memberId)
.matchingUuid(matchingUuid)
.priorityValue(priorityValue)
.build();
}

}
Loading
Loading