Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -18,7 +18,9 @@ public class RedisPassCodeRegistry implements PassCodeRegistry{
public String generateAndGetPassCode(String runId) {
String passCode;
int retries = 0;

if(getRunIdByPassCode(runId).isPresent()) {
throw RunningException.of(RunningErrorCode.LIVE_RUNNING_ALREADY_CREATED);
}
// 최대 재시도 횟수를 넘어가면 예외를 발생시킨다.
// 초당 방 생성 요청 62개이상, 활성화된 입장코드가 68만개 이상일때
// 10번 재시도 실패확률이 1%이상으로 예상되며 이때 예외를 발생시킨다.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.run_us.server.domains.running.live.service.model;
package com.run_us.server.domains.running.common;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;
Expand All @@ -7,6 +7,8 @@
public final class RunningConst {
public static final long UPDATE_INTERVAL = 1000; // 전체 참가자의 위치 업데이트 주기(1초)
public static final double SIGNIFICANT_DISTANCE = 4; // 즉시 업데이트 중요 이벤트 기준(N미터 이상 이동시)
public static final int MAX_LIVE_SESSION_CREATION_TIME = 10; // 라이브세션 생성 최대 대기 시간(10분 까지)
public static final int LIVE_SESSION_ALLOW_ALL_TIME = 5; // 방장 외 라이브세션 생성 가능 시간 (5분 이후)

public static final String RUNNING_PREFIX = "running:";
public static final String STATUS_SUFFIX = ":status";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ public enum RunningErrorCode implements CustomResponseCode {
RUNNING_SESSION_NOT_MODIFIABLE(
"REH4006", "Running Session Not Modifiable", "Running Session Not Modifiable", HttpStatus.BAD_REQUEST),
RUNNING_NOT_JOINABLE(
"REH4007", "Running Not Joinable", "Running Not Joinable", HttpStatus.BAD_REQUEST),;
"REH4007", "Running Not Joinable", "Running Not Joinable", HttpStatus.BAD_REQUEST),
LIVE_RUNNING_ALREADY_CREATED(
"REH4008", "Live Running Already Created", "Live Running Already Created", HttpStatus.BAD_REQUEST),
LIVE_RUNNING_CREATION_TIME_OVER(
"REH4009", "Live Running Creation Time Over", "Live Running Creation Time Over", HttpStatus.BAD_REQUEST),;

private final String code;
private final String clientMessage;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.run_us.server.domains.running.live.controller;

import com.run_us.server.domains.running.live.service.model.LiveRunningCreateResponse;
import com.run_us.server.domains.running.live.service.usecase.CreateLiveRunningUseCase;
import com.run_us.server.global.common.SuccessResponse;
import com.run_us.server.global.security.annotation.CurrentUser;
import com.run_us.server.global.security.principal.UserPrincipal;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
@RequestMapping("/runnings/live")
@RequiredArgsConstructor
public class LiveRunningController {

private final CreateLiveRunningUseCase createLiveRunningUseCase;

@PostMapping()
public ResponseEntity<SuccessResponse<LiveRunningCreateResponse>> createLiveRunning(
@RequestParam("runPublicId") String runPublicId,
@CurrentUser UserPrincipal userPrincipal) {
SuccessResponse<LiveRunningCreateResponse> response =
createLiveRunningUseCase.createLiveRunning(runPublicId, userPrincipal.getInternalId());
return ResponseEntity.ok(response);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import com.run_us.server.domains.running.live.controller.model.RunningSocketResponse;
import com.run_us.server.domains.running.live.controller.model.RunningSocketResponseCode;
import com.run_us.server.domains.running.live.service.RunningLiveService;
import com.run_us.server.domains.running.live.service.model.RunningConst;
import com.run_us.server.domains.running.common.RunningConst;
import com.run_us.server.global.common.SuccessResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.run_us.server.domains.running.live.controller.model;

import static com.run_us.server.domains.running.live.service.model.RunningConst.RUNNING_WS_SUBSCRIBE_PATH;
import static com.run_us.server.domains.running.common.RunningConst.RUNNING_WS_SUBSCRIBE_PATH;
import static com.run_us.server.global.common.SocketConst.USER_WS_SUBSCRIBE_PATH;

import lombok.Getter;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.run_us.server.domains.running.live.service.model.LocationData;
import com.run_us.server.domains.running.live.service.model.ParticipantStatus;
import com.run_us.server.domains.running.live.service.model.RunningConst;
import com.run_us.server.domains.running.common.RunningConst;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.run_us.server.domains.running.live.service;

import static com.run_us.server.domains.running.live.service.model.RunningConst.RUNNING_PREFIX;
import static com.run_us.server.domains.running.common.RunningConst.RUNNING_PREFIX;
import static com.run_us.server.domains.running.live.service.util.RunningKeyUtil.createLiveKey;

import com.run_us.server.domains.running.live.controller.model.RunningSocketResponse;
Expand All @@ -9,7 +9,7 @@
import com.run_us.server.domains.running.live.repository.UpdateLocationRepository;
import com.run_us.server.domains.running.live.service.model.LocationData;
import com.run_us.server.domains.running.live.service.model.ParticipantStatus;
import com.run_us.server.domains.running.live.service.model.RunningConst;
import com.run_us.server.domains.running.common.RunningConst;
import com.run_us.server.domains.running.run.domain.Run;
import com.run_us.server.domains.running.run.service.ParticipantService;
import com.run_us.server.domains.running.run.service.RunCommandService;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.run_us.server.domains.running.live.service.model;

import com.run_us.server.domains.running.run.domain.Run;
import com.run_us.server.domains.running.run.service.model.ParticipantInfo;
import lombok.Getter;

import java.util.List;

@Getter
public class LiveRunningCreateResponse {
private final String runPublicId;
private final String passcode;
private final List<ParticipantInfo> participantInfos;

public LiveRunningCreateResponse(String runPublicId, String passcode, List<ParticipantInfo> participantInfos) {
this.runPublicId = runPublicId;
this.passcode = passcode;
this.participantInfos = participantInfos;
}
public static LiveRunningCreateResponse from (Run run, String passcode, List<ParticipantInfo> participantInfos) {
return new LiveRunningCreateResponse(run.getPublicId(), passcode, participantInfos);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.run_us.server.domains.running.live.service.usecase;

import com.run_us.server.domains.running.live.service.model.LiveRunningCreateResponse;
import com.run_us.server.global.common.SuccessResponse;

public interface CreateLiveRunningUseCase {
SuccessResponse<LiveRunningCreateResponse> createLiveRunning(String runPublicId, Integer userId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.run_us.server.domains.running.live.service.usecase;

import com.run_us.server.domains.running.common.PassCodeRegistry;
import com.run_us.server.domains.running.live.service.model.LiveRunningCreateResponse;
import com.run_us.server.domains.running.run.controller.model.RunningHttpResponseCode;
import com.run_us.server.domains.running.run.domain.Run;
import com.run_us.server.domains.running.run.service.ParticipantService;
import com.run_us.server.domains.running.run.service.RunQueryService;
import com.run_us.server.domains.running.run.service.RunValidator;
import com.run_us.server.domains.running.run.service.model.ParticipantInfo;
import com.run_us.server.global.common.SuccessResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@RequiredArgsConstructor
public class CreateLiveRunningUseCaseImpl implements CreateLiveRunningUseCase {

private final RunQueryService runQueryService;
private final RunValidator runValidator;
private final ParticipantService participantService;
private final PassCodeRegistry passCodeRegistry;

@Override
@Transactional
public SuccessResponse<LiveRunningCreateResponse> createLiveRunning(String runPublicId, Integer userId) {
Run selectedRun = runQueryService.findByRunPublicId(runPublicId);
runValidator.validateCurrentUserCanStartRun(userId, selectedRun);
participantService.joinLiveRunning(userId, selectedRun);
String passcode = passCodeRegistry.generateAndGetPassCode(runPublicId);
selectedRun.openLiveSession(userId);
List<ParticipantInfo> participantInfos = participantService.getParticipants(selectedRun);
return SuccessResponse.of(RunningHttpResponseCode.LIVE_ROOM_CREATED, LiveRunningCreateResponse.from(selectedRun, passcode, participantInfos));
}


}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.run_us.server.domains.running.live.service.util;

import com.run_us.server.domains.running.live.service.model.RunningConst;
import com.run_us.server.domains.running.common.RunningConst;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.time.ZonedDateTime;
import java.util.List;

import static com.run_us.server.domains.running.common.RunningConst.LIVE_SESSION_ALLOW_ALL_TIME;
import static com.run_us.server.domains.running.common.RunningConst.MAX_LIVE_SESSION_CREATION_TIME;

@Entity
@Table(name = "run")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
Expand All @@ -22,8 +26,12 @@ public class Run extends CreationTimeAudit {
@Column(name = "run_id")
private Integer id;

@Column(name = "run_host_id")
private Integer hostId;

@Column(name = "session_host_id")
private Integer sessionHostId;

private String publicId;

private Integer crewId;
Expand All @@ -41,11 +49,13 @@ public class Run extends CreationTimeAudit {
// 생성
public Run(Integer hostId) {
this.hostId = hostId;
this.sessionHostId = hostId;
this.status = RunStatus.WAITING;
}

public Run (Integer hostId, Integer crewId) {
this.hostId = hostId;
this.sessionHostId = hostId;
this.crewId = crewId;
}

Expand Down Expand Up @@ -77,6 +87,18 @@ public boolean isHost(int userId) {
return this.hostId.equals(userId);
}

public boolean isCreationTimeOver() {
return this.preview.getBeginTime()
.plusMinutes(MAX_LIVE_SESSION_CREATION_TIME)
.isBefore(ZonedDateTime.now());
}

public void openLiveSession(Integer userId) {
validateRunModifiable();
this.sessionHostId = userId;
this.status = RunStatus.RUNNING;
}

public void modifyPaceInfo(List<RunPace> runPaces) {
this.paceCategories = runPaces;
}
Expand All @@ -86,6 +108,14 @@ public void exposeToCrew(Integer crewPublicId) {
this.crewId = crewPublicId;
}

public boolean isLiveSessionCreatableByHost() {
return ZonedDateTime.now().isAfter(this.preview.getBeginTime().minusMinutes(10));
}

public boolean isLiveSessionCreatableByAnyone() {
return ZonedDateTime.now().isAfter(this.getPreview().getBeginTime().plusMinutes(LIVE_SESSION_ALLOW_ALL_TIME));
}

private void validateRunModifiable() {
if (this.status == RunStatus.FINISHED)
throw RunningException.of(RunningErrorCode.RUNNING_ALREADY_FINISHED);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,6 @@ public interface ParticipantRepository extends JpaRepository<Participant, Long>
+ "join User u on p.userId = u.id "
+ "WHERE p.run.id = :runId")
List<ParticipantInfo> findByRunId(Integer runId);

boolean existsByUserIdAndRunId(Integer userId, Integer runId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,8 @@ public void cancelParticipant(Integer userId, Run run) {
public List<ParticipantInfo> getParticipants(Run run) {
return participantRepository.findByRunId(run.getId());
}

public boolean isRegistered(Integer userId, Run selectedRun) {
return participantRepository.existsByUserIdAndRunId(userId, selectedRun.getId());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@
import com.run_us.server.domains.running.run.domain.SessionAccessLevel;
import com.run_us.server.domains.running.run.domain.Run;
import com.run_us.server.domains.running.run.service.model.RunCreateDto;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public final class RunValidator {

private final ParticipantService participantService;

public void validateRunDeletable(Integer userId, Run run) {
validateIsRunOwner(userId, run);
if (!run.isDeletable()) {
Expand Down Expand Up @@ -37,4 +41,20 @@ public boolean isCrewRunCreateCommand(RunCreateDto createDto) {
// ONLY_CREW + CREW ID or PUBLIC + CREW ID
return createDto.getAccessLevel() == SessionAccessLevel.ONLY_CREW || createDto.getCrewPublicId() != null;
}

public void validateCurrentUserCanStartRun(Integer userId, Run run) {
if(run.isCreationTimeOver()) {
throw RunningException.of(RunningErrorCode.LIVE_RUNNING_CREATION_TIME_OVER);
}
if(!run.isJoinable()) {
throw RunningException.of(RunningErrorCode.RUNNING_NOT_JOINABLE);
}

if(!participantService.isRegistered(userId, run)) {
throw RunningException.of(RunningErrorCode.USER_NOT_JOINED);
}
if(!run.isLiveSessionCreatableByAnyone() && run.isLiveSessionCreatableByHost()) {
validateIsRunOwner(userId, run);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.test.context.ActiveProfiles;

import java.time.ZonedDateTime;

Expand All @@ -18,6 +19,7 @@
import static org.junit.jupiter.api.Assertions.assertNotNull;

@DataJpaTest
@ActiveProfiles("test")
public class CrewJoinRequestRepositoryTest {

@Autowired
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.ActiveProfiles;

import java.time.ZonedDateTime;
import java.util.Optional;
Expand All @@ -20,6 +21,7 @@
import static org.mockito.Mockito.when;

@SpringBootTest
@ActiveProfiles("test")
class CrewValidatorTest {

@MockBean
Expand Down
Loading