-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #43 from softeerbootcamp4th/feature/40-draw-event-…
…logic [feat] 추첨 로직 구현(#40)
- Loading branch information
Showing
39 changed files
with
1,188 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
services: | ||
mysql: | ||
image: mysql | ||
ports: | ||
- 3306:3306 | ||
environment: | ||
MYSQL_ROOT_PASSWORD: test | ||
redis: | ||
image: redis | ||
ports: | ||
- 6379:6379 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
6 changes: 6 additions & 0 deletions
6
src/main/java/hyundai/softeer/orange/comment/dto/WriteCommentCountDto.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package hyundai.softeer.orange.comment.dto; | ||
|
||
public interface WriteCommentCountDto { | ||
Long getEventUserId(); | ||
Long getCount(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
36 changes: 36 additions & 0 deletions
36
src/main/java/hyundai/softeer/orange/event/draw/DrawEventConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package hyundai.softeer.orange.event.draw; | ||
|
||
import hyundai.softeer.orange.event.draw.component.score.actionHandler.ActionHandler; | ||
import hyundai.softeer.orange.event.draw.enums.DrawEventAction; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
|
||
import java.util.Collections; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.regex.Matcher; | ||
import java.util.regex.Pattern; | ||
|
||
@Configuration | ||
public class DrawEventConfig { | ||
private static final String NamePattern = "(.+?)_ActionHandler"; | ||
|
||
// actionHandler을 Action 타입으로 가져올 수 있도록 설정 | ||
@Bean(name = "actionHandlerMap") | ||
public Map<DrawEventAction, ActionHandler> actionHandlerMap(Map<String, ActionHandler> handlers) { | ||
Map<DrawEventAction, ActionHandler> actionHandlerMap = new HashMap<>(); | ||
Pattern pattern = Pattern.compile(NamePattern); | ||
|
||
for(var entry: handlers.entrySet()) { | ||
Matcher matcher = pattern.matcher(entry.getKey()); | ||
if (!matcher.find()) throw new RuntimeException("no matched action"); | ||
|
||
String key = matcher.group(1); | ||
ActionHandler actionHandler = entry.getValue(); | ||
|
||
actionHandlerMap.put(DrawEventAction.valueOf(key), actionHandler); | ||
} | ||
// 런타임에 변경 못하게 제한 | ||
return Collections.unmodifiableMap(actionHandlerMap); | ||
} | ||
} |
124 changes: 124 additions & 0 deletions
124
...main/java/hyundai/softeer/orange/event/draw/component/picker/AccSumBasedWinnerPicker.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
package hyundai.softeer.orange.event.draw.component.picker; | ||
|
||
import org.springframework.context.annotation.Primary; | ||
import org.springframework.stereotype.Component; | ||
|
||
import java.util.*; | ||
|
||
@Primary | ||
@Component | ||
public class AccSumBasedWinnerPicker implements WinnerPicker { | ||
|
||
@Override | ||
public List<PickTarget> pick(List<PickTarget> items, long count) { | ||
long maxPickCount = Math.min(items.size(), count); | ||
// TODO: 둘 중 하나를 선택하는 최적 조건 분석하기. | ||
if (count - maxPickCount <= 10 || // 두 값 차이가 작을 때 | ||
((double) count / maxPickCount) <= 1.1 ) { | ||
return pickMany(items, maxPickCount); | ||
} else { | ||
return pickManyUsingSet(items, maxPickCount); | ||
} | ||
} | ||
|
||
protected List<PickTarget> pickMany(List<PickTarget> targets, long count) { | ||
List<PickTarget> pickedTargets = new ArrayList<>(); | ||
// 추첨에 참여하는 객체들이 존재하는 set | ||
Set<PickTarget> targetSet = new HashSet<>(targets); | ||
|
||
for(int i = 0; i < count; i++) { | ||
RandomItem[] items = getAccumulatedItems(targetSet); | ||
long bound = items[items.length - 1].score; | ||
long targetScore = new Random().nextLong(1, bound + 1); | ||
int pickedIdx = binarySearch(items, targetScore); | ||
PickTarget pickedTarget = items[pickedIdx].target; | ||
|
||
targetSet.remove(pickedTarget); | ||
pickedTargets.add(pickedTarget); | ||
} | ||
|
||
return pickedTargets; | ||
} | ||
|
||
/** | ||
* set 자료구조를 이용하여 가중합을 1번만 계산하는 타겟 선택 방식 | ||
* @param targets 선택 대상이 되는 배열 | ||
* @param count | ||
* @return | ||
*/ | ||
protected List<PickTarget> pickManyUsingSet(List<PickTarget> targets, long count) { | ||
// 가중합 배열 | ||
RandomItem[] items = getAccumulatedItems(targets); | ||
List<PickTarget> pickedTargets = new ArrayList<>(); | ||
long bound = items[items.length - 1].score; | ||
// 이미 선택된 대상이 존재하는 공간 | ||
Set<Integer> pickedIdxSet = new HashSet<>(); | ||
|
||
for(int i = 0; i < count; i++) { | ||
int pickedIdx; | ||
do { | ||
long targetScore = new Random().nextLong(1, bound + 1); | ||
pickedIdx = binarySearch(items, targetScore); | ||
}while(pickedIdxSet.contains(pickedIdx)); | ||
// 방문했다고 마킹 | ||
pickedIdxSet.add(pickedIdx); | ||
pickedTargets.add(targets.get(pickedIdx)); | ||
} | ||
|
||
return pickedTargets; | ||
} | ||
|
||
|
||
|
||
/** | ||
* 누적합 배열을 반환한다 | ||
* @param targets 추첨 대상들 | ||
* @return 누적합 형태로 표현된 RandomItem 배열 | ||
*/ | ||
protected RandomItem[] getAccumulatedItems(Collection<PickTarget> targets) { | ||
RandomItem[] items = new RandomItem[targets.size()]; | ||
long score = 0; | ||
|
||
int idx = 0; | ||
for (PickTarget target : targets) { | ||
score += target.score(); | ||
items[idx++] = new RandomItem(target, score); | ||
} | ||
|
||
return items; | ||
} | ||
|
||
/** | ||
* 이진 탐색을 이용하여 누적합 배열에서 대상 인덱스를 얻는다. | ||
* @param items 누적합 배열 | ||
* @param target 대상 점수 | ||
* @return 선택된 인덱스 값 | ||
*/ | ||
protected int binarySearch(RandomItem[] items, long target) { | ||
int low = 0; | ||
int high = items.length - 1; | ||
|
||
while(low <= high) { | ||
// >>>은 비트열 오른쪽으로 이동, 빈 공간은 0으로 채움 | ||
// 2로 나누는 로직을 >>>으로 처리 | ||
int mid = (low + high) >>> 1; | ||
|
||
if(items[mid].score > target) { | ||
high = mid - 1; | ||
} else if(items[mid].score < target) { | ||
low = mid + 1; | ||
} else { | ||
break; | ||
} | ||
} | ||
|
||
return low; | ||
} | ||
|
||
/** | ||
* 가중합을 기록해두는 객체 | ||
* @param target 추첨 대상 | ||
* @param score 누적합 점수 | ||
*/ | ||
protected record RandomItem(PickTarget target, long score) {} | ||
} |
3 changes: 3 additions & 0 deletions
3
src/main/java/hyundai/softeer/orange/event/draw/component/picker/PickTarget.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
package hyundai.softeer.orange.event.draw.component.picker; | ||
|
||
public record PickTarget(Long key, long score) {} |
10 changes: 10 additions & 0 deletions
10
src/main/java/hyundai/softeer/orange/event/draw/component/picker/WinnerPicker.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package hyundai.softeer.orange.event.draw.component.picker; | ||
|
||
import java.util.List; | ||
|
||
/** | ||
* "추첨" 작업을 진행하는 서비스. 여러 추첨 방식이 존재할 수 있으므로, 추첨 과정은 별도 인터페이스로 분리. | ||
*/ | ||
public interface WinnerPicker { | ||
List<PickTarget> pick(List<PickTarget> items,long count); | ||
} |
29 changes: 29 additions & 0 deletions
29
src/main/java/hyundai/softeer/orange/event/draw/component/score/ScoreCalculator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package hyundai.softeer.orange.event.draw.component.score; | ||
|
||
import hyundai.softeer.orange.event.draw.component.score.actionHandler.ActionHandler; | ||
import hyundai.softeer.orange.event.draw.entity.DrawEventScorePolicy; | ||
import hyundai.softeer.orange.event.draw.enums.DrawEventAction; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Component; | ||
|
||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
/** | ||
* 특정 추첨 이벤트에 대한 점수를 계산하는 클래스. 저장은 안한다. | ||
*/ | ||
@RequiredArgsConstructor | ||
@Component | ||
public class ScoreCalculator { | ||
private final Map<DrawEventAction, ActionHandler> handlerMap; | ||
|
||
public Map<Long, Long> calculate(long eventId, List<DrawEventScorePolicy> policies) { | ||
Map<Long, Long> scoreMap = new HashMap<>(); | ||
for (var policy : policies) { | ||
ActionHandler handler = handlerMap.get(policy.getAction()); | ||
handler.handle(scoreMap, eventId, policy.getScore()); | ||
} | ||
return scoreMap; | ||
} | ||
} |
12 changes: 12 additions & 0 deletions
12
...n/java/hyundai/softeer/orange/event/draw/component/score/actionHandler/ActionHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package hyundai.softeer.orange.event.draw.component.score.actionHandler; | ||
|
||
import java.util.Map; | ||
|
||
/** | ||
* 채점 정책에 대한 동작을 처리한다. | ||
* <p>component 이름은 action 이름 + ActionHandler 형식을 띄어야 한다.</p> | ||
* <p>액션 = {@link hyundai.softeer.orange.event.draw.enums.DrawEventAction}</p> | ||
*/ | ||
public interface ActionHandler { | ||
void handle(Map<Long, Long> scoreMap, long eventRawId, long score); | ||
} |
22 changes: 22 additions & 0 deletions
22
...ofteer/orange/event/draw/component/score/actionHandler/ParticipateEventActionHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package hyundai.softeer.orange.event.draw.component.score.actionHandler; | ||
|
||
import hyundai.softeer.orange.event.draw.repository.EventParticipationInfoRepository; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Component; | ||
|
||
import java.util.Map; | ||
|
||
@RequiredArgsConstructor | ||
@Component("ParticipateEvent_ActionHandler") | ||
public class ParticipateEventActionHandler implements ActionHandler { | ||
private final EventParticipationInfoRepository repo; | ||
|
||
@Override | ||
public void handle(Map<Long, Long> scoreMap, long eventRawId, long score) { | ||
var participateCounts = repo.countPerEventUserByEventId(eventRawId); | ||
for (var p : participateCounts) { | ||
long beforeScore = scoreMap.getOrDefault(p.getEventUserId(),0L); | ||
scoreMap.put(p.getEventUserId(), beforeScore + p.getCount() * score); | ||
} | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
...ai/softeer/orange/event/draw/component/score/actionHandler/WriteCommentActionHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package hyundai.softeer.orange.event.draw.component.score.actionHandler; | ||
|
||
import hyundai.softeer.orange.comment.repository.CommentRepository; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Component; | ||
|
||
import java.util.Map; | ||
|
||
@RequiredArgsConstructor | ||
@Component("WriteComment_ActionHandler") | ||
public class WriteCommentActionHandler implements ActionHandler { | ||
private final CommentRepository repo; | ||
|
||
@Override | ||
public void handle(Map<Long, Long> scoreMap, long eventRawId, long score) { | ||
var commentCounts = repo.countPerEventUserByEventId(eventRawId); | ||
for (var c : commentCounts) { | ||
long beforeScore = scoreMap.getOrDefault(c.getEventUserId(), 0L); | ||
scoreMap.put(c.getEventUserId(), beforeScore + c.getCount() * score); | ||
} | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
src/main/java/hyundai/softeer/orange/event/draw/dto/DrawEventWinningInfoBulkInsertDto.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package hyundai.softeer.orange.event.draw.dto; | ||
|
||
import lombok.Getter; | ||
|
||
@Getter | ||
public class DrawEventWinningInfoBulkInsertDto { | ||
private long eventUserId; | ||
private long ranking; | ||
private long drawEventId; | ||
|
||
public static DrawEventWinningInfoBulkInsertDto of(long eventUserId, long ranking, long drawEventId) { | ||
var dto = new DrawEventWinningInfoBulkInsertDto(); | ||
dto.eventUserId = eventUserId; | ||
dto.ranking = ranking; | ||
dto.drawEventId = drawEventId; | ||
return dto; | ||
} | ||
} |
6 changes: 6 additions & 0 deletions
6
src/main/java/hyundai/softeer/orange/event/draw/dto/EventParticipateCountDto.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package hyundai.softeer.orange.event.draw.dto; | ||
|
||
public interface EventParticipateCountDto { | ||
long getEventUserId(); | ||
long getCount(); | ||
} |
Oops, something went wrong.