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] 어드민 이벤트 수정 기능 구현(#28) #31

Merged
merged 7 commits into from
Aug 6, 2024
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ dependencies {

// sms api
implementation 'net.nurigo:sdk:4.3.0'

// test db
testRuntimeOnly 'com.h2database:h2'
}

tasks.named('test') {
Expand Down
1 change: 1 addition & 0 deletions src/main/java/hyundai/softeer/orange/common/ErrorCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public enum ErrorCode {
EVENT_FRAME_NOT_FOUND(HttpStatus.NOT_FOUND, false, "이벤트 프레임을 찾을 수 없습니다."),
EVENT_USER_NOT_FOUND(HttpStatus.NOT_FOUND, false, "이벤트 사용자를 찾을 수 없습니다."),
COMMENT_NOT_FOUND(HttpStatus.NOT_FOUND, false, "기대평을 찾을 수 없습니다."),
EVENT_NOT_FOUND(HttpStatus.NOT_FOUND, false, "이벤트를 찾을 수 없습니다."),

// 405 Method Not Allowed
METHOD_NOT_ALLOWED(HttpStatus.METHOD_NOT_ALLOWED, false, "허용되지 않은 메서드입니다."),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,9 @@ public ResponseEntity<Map<String, String>> handleInValidRequestException(MethodA
public ResponseEntity<ErrorResponse> handleException(BaseException e) {
return ResponseEntity.status(e.getErrorCode().getHttpStatus()).body(ErrorResponse.from(e.getErrorCode()));
}

@ExceptionHandler({BaseException.class})
public ResponseEntity<ErrorResponse> handleAllBaseException(BaseException e) {
return ResponseEntity.status(e.getErrorCode().getHttpStatus()).body(ErrorResponse.from(e.getErrorCode()));
}
}
3 changes: 3 additions & 0 deletions src/main/java/hyundai/softeer/orange/config/AppConfig.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package hyundai.softeer.orange.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand All @@ -12,6 +13,8 @@ public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
// LocalDateTime파싱이 목적
objectMapper.registerModule(new JavaTimeModule());
// timestamp를 문자열로 전달
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
return objectMapper;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,34 @@
import hyundai.softeer.orange.event.draw.entity.DrawEvent;
import hyundai.softeer.orange.event.draw.entity.DrawEventMetadata;
import hyundai.softeer.orange.event.draw.entity.DrawEventScorePolicy;
import hyundai.softeer.orange.event.draw.repository.DrawEventMetadataRepository;
import hyundai.softeer.orange.event.draw.repository.DrawEventScorePolicyRepository;
import hyundai.softeer.orange.event.dto.EventDto;
import hyundai.softeer.orange.event.dto.draw.DrawEventDto;
import hyundai.softeer.orange.event.dto.draw.DrawEventMetadataDto;
import hyundai.softeer.orange.event.dto.draw.DrawEventScorePolicyDto;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.*;
import java.util.stream.Collectors;

/**
* EventMetadata에 DrawEvent를 주입해주는 매퍼
*/
@RequiredArgsConstructor
@Component
public class DrawEventFieldMapper implements EventFieldMapper {
private final DrawEventMetadataRepository deMetadataRepository;
private final DrawEventScorePolicyRepository deScorePolicyRepository;

@Override
public boolean canHandle(EventType eventType) {
return eventType.equals(EventType.draw);
}

@Override
public void handle(EventMetadata metadata, EventDto eventDto) {
public void fetchToEventEntity(EventMetadata metadata, EventDto eventDto) {
DrawEventDto dto = eventDto.getDraw();
// 비어 있으면 안됨.
if (dto == null) throw new EventException(ErrorCode.INVALID_JSON);
Expand Down Expand Up @@ -52,4 +62,127 @@ public void handle(EventMetadata metadata, EventDto eventDto) {
).toList();
event.setMetadataList(metadataList);
}

@Override
public void fetchToDto(EventMetadata metadata, EventDto eventDto) {
Optional<DrawEvent> drawEventOpt = metadata.getDrawEventList().stream().findFirst();
// drawEvent 정보가 있으면 넣기
drawEventOpt.ifPresent(drawEvent -> {
DrawEventDto drawEventDto = DrawEventDto
.builder()
.id(drawEvent.getId())
.metadata(drawEvent.getMetadataList().stream().map(
it -> DrawEventMetadataDto.builder()
.id(it.getId())
.count(it.getCount())
.grade(it.getGrade())
.prizeInfo(it.getPrizeInfo())
.build()
).toList())
.policies(drawEvent.getPolicyList().stream().map(
it -> DrawEventScorePolicyDto.builder()
.id(it.getId())
.score(it.getScore())
.action(it.getAction())
.build()
).toList())
.build();

eventDto.setDraw(drawEventDto);
});
}

@Override
public void editEventField(EventMetadata metadata, EventDto dto) {
Optional<DrawEvent> drawEventOpt = metadata.getDrawEventList().stream().findFirst();
DrawEvent drawEvent = drawEventOpt.orElseThrow(() -> new EventException(ErrorCode.EVENT_NOT_FOUND));
DrawEventDto drawEventDto = dto.getDraw();

editDrawEventMetadata(drawEvent, drawEventDto);
editDrawEventScorePolicy(drawEvent, drawEventDto);
}

// 나중에 가능하다면 리팩토링 방법 찾아보자. => set 기반 공통 로직 추출
private void editDrawEventMetadata(DrawEvent drawEvent, DrawEventDto drawEventDto) {
List<DrawEventMetadata> deMetadata = drawEvent.getMetadataList();

Map<Boolean, Map<Long, DrawEventMetadataDto>> deMetadataDtos = drawEventDto.getMetadata().stream()
.collect(Collectors.partitioningBy(it -> it.getId() == null, Collectors.toMap(DrawEventMetadataDto::getId, it-> it)));
// true이면 created / false이면 updated
Map<Long, DrawEventMetadataDto> createdDtos = deMetadataDtos.get(true);
Map<Long, DrawEventMetadataDto> updatedDtos = deMetadataDtos.get(false);

Set<Long> updated = new HashSet<>(updatedDtos.keySet());
Set<Long> deleted = deMetadata.stream().map(DrawEventMetadata::getId).collect(Collectors.toSet());
// null은 created
updated.retainAll(deleted); // dto & entity 교집합 => updated
deleted.removeAll(updated); // entity에는 있는데 dto에는 없음 => deleted

for(DrawEventMetadata metadata : deMetadata) {
// update에 있는 경우
if(updated.contains(metadata.getId())) {
DrawEventMetadataDto dto = updatedDtos.get(metadata.getId());
metadata.updateCount(dto.getCount());
metadata.updateGrade(dto.getGrade());
metadata.updatePrizeInfo(dto.getPrizeInfo());
}
// delete에 있는 경우
else if(deleted.contains(metadata.getId())) {
deMetadataRepository.delete(metadata);
}
}
deMetadata.removeIf(it -> deleted.contains(it.getId()));

// 객체 생성 처리
for(DrawEventMetadataDto createdDto : createdDtos.values()) {
DrawEventMetadata drawEventMetadata = DrawEventMetadata.of(
createdDto.getGrade(),
createdDto.getCount(),
createdDto.getPrizeInfo(),
drawEvent
);
deMetadata.add(drawEventMetadata);
}
}

private void editDrawEventScorePolicy(DrawEvent drawEvent, DrawEventDto drawEventDto) {
List<DrawEventScorePolicy> policies = drawEvent.getPolicyList();

Map<Boolean, Map<Long, DrawEventScorePolicyDto>> deMetadataDtos = drawEventDto.getPolicies().stream()
.collect(Collectors.partitioningBy(it -> it.getId() == null, Collectors.toMap(DrawEventScorePolicyDto::getId, it-> it)));
// true이면 created / false이면 updated
Map<Long, DrawEventScorePolicyDto> createdDtos = deMetadataDtos.get(true);
Map<Long, DrawEventScorePolicyDto> updatedDtos = deMetadataDtos.get(false);

Set<Long> updated = new HashSet<>(updatedDtos.keySet());
Set<Long> deleted = policies.stream().map(DrawEventScorePolicy::getId).collect(Collectors.toSet());
// null은 created
updated.retainAll(deleted); // dto & entity 교집합 => updated
deleted.removeAll(updated); // entity에는 있는데 dto에는 없음 => deleted

for(DrawEventScorePolicy policy : policies) {
// update에 있는 경우
if(updated.contains(policy.getId())) {
DrawEventScorePolicyDto dto = updatedDtos.get(policy.getId());
policy.updateAction(dto.getAction());
policy.updateScore(dto.getScore());
}
// delete에 있는 경우
else if(deleted.contains(policy.getId())) {
deScorePolicyRepository.delete(policy);
}
}
policies.removeIf(it -> deleted.contains(it.getId()));

// 객체 생성 처리
for(DrawEventScorePolicyDto createdDto : createdDtos.values()) {
DrawEventScorePolicy policy = DrawEventScorePolicy.of(
createdDto.getAction(),
createdDto.getScore(),
drawEvent
);
policies.add(policy);
}
}

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

public interface EventFieldMapper {
boolean canHandle(EventType eventType);
void handle(EventMetadata metadata, EventDto dto);
void fetchToEventEntity(EventMetadata metadata, EventDto dto);
void fetchToDto(EventMetadata metadata, EventDto dto);
void editEventField(EventMetadata metadata, EventDto dto);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,27 @@
import hyundai.softeer.orange.event.dto.EventDto;
import hyundai.softeer.orange.event.dto.fcfs.FcfsEventDto;
import hyundai.softeer.orange.event.fcfs.entity.FcfsEvent;
import hyundai.softeer.orange.event.fcfs.repository.FcfsEventRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.*;
import java.util.stream.Collectors;

/**
* EventMetadata에 FcfsEvent를 주입해주는 매퍼
*/
@RequiredArgsConstructor
@Component
public class FcfsEventFieldMapper implements EventFieldMapper {
private final FcfsEventRepository fcfsEventRepository;
@Override
public boolean canHandle(EventType eventType) {
return eventType.equals(EventType.fcfs);
}

@Override
public void handle(EventMetadata metadata, EventDto eventDto) {
public void fetchToEventEntity(EventMetadata metadata, EventDto eventDto) {
List<FcfsEventDto> fcfsDtos = eventDto.getFcfs();
// 비어 있으면 안됨
if (fcfsDtos == null || fcfsDtos.isEmpty()) throw new EventException(ErrorCode.INVALID_JSON);
Expand All @@ -39,4 +44,63 @@ public void handle(EventMetadata metadata, EventDto eventDto) {

metadata.addFcfsEvents(fcfsEventList);
}

@Override
public void fetchToDto(EventMetadata metadata, EventDto eventDto) {
List<FcfsEvent> fcfsEvents = metadata.getFcfsEventList();
List<FcfsEventDto> fcfsEventDtos = fcfsEvents.stream().map(it -> FcfsEventDto
.builder()
.id(it.getId())
.startTime(it.getStartTime())
.endTime(it.getEndTime())
.participantCount(it.getParticipantCount())
.prizeInfo(it.getPrizeInfo())
.build()
).toList();
eventDto.setFcfsList(fcfsEventDtos);
}

@Override
public void editEventField(EventMetadata metadata, EventDto eventDto) {
List<FcfsEvent> fcfsEvents = metadata.getFcfsEventList();
// 집합을 이용해서 created / updated / deleted 를 구분
Map<Boolean, Map<Long, FcfsEventDto>> fcfsAllDtos = eventDto.getFcfs().stream()
.collect(Collectors.partitioningBy(it -> it.getId() == null, Collectors.toMap(FcfsEventDto::getId, it-> it)));
// true이면 created / false이면 updated
Map<Long, FcfsEventDto> createdDtos = fcfsAllDtos.get(true);
Map<Long, FcfsEventDto> updatedDtos = fcfsAllDtos.get(false);

Set<Long> updated = new HashSet<>(updatedDtos.keySet());
Set<Long> deleted = fcfsEvents.stream().map(FcfsEvent::getId).collect(Collectors.toSet());
// null은 created
updated.retainAll(deleted); // dto & entity 교집합 => updated
deleted.removeAll(updated); // entity에는 있는데 dto에는 없음 => deleted
for(FcfsEvent event: fcfsEvents) {
// update에 있는 경우
if(updated.contains(event.getId())) {
FcfsEventDto updatedDto = updatedDtos.get(event.getId());
event.updateStartTime(updatedDto.getStartTime());
event.updateEndTime(updatedDto.getEndTime());
event.updateParticipantCount(updatedDto.getParticipantCount());
event.updatePrizeInfo(updatedDto.getPrizeInfo());
}
// delete에 있는 경우
else if(deleted.contains(event.getId())) {
fcfsEventRepository.delete(event);
}
}
fcfsEvents.removeIf(it -> deleted.contains(it.getId()));

// 객체 생성 처리
for(FcfsEventDto createdDto : createdDtos.values()) {
FcfsEvent newEvent = FcfsEvent.of(
createdDto.getStartTime(),
createdDto.getEndTime(),
createdDto.getParticipantCount(),
createdDto.getPrizeInfo(),
metadata
);
fcfsEvents.add(newEvent);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
import hyundai.softeer.orange.event.common.service.EventService;
import hyundai.softeer.orange.event.dto.EventDto;
import hyundai.softeer.orange.event.dto.EventFrameCreateRequest;
import hyundai.softeer.orange.event.dto.group.EventEditGroup;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -21,7 +21,7 @@
*/
@Slf4j
@RequiredArgsConstructor
@RequestMapping("/event")
@RequestMapping("/api/v1/event")
@RestController
public class EventController {
private final EventService eventService;
Expand All @@ -37,6 +37,34 @@ public ResponseEntity<?> createEvent(@Validated @RequestBody EventDto eventDto)
return ResponseEntity.status(HttpStatus.CREATED).build();
}

@Auth({AuthRole.admin})
@GetMapping("/edit")
@Operation(summary = "이벤트 수정 초기 데이터 획득", description = "이벤트 정보 수정을 위해 초기 정보를 받는다", responses = {
@ApiResponse(responseCode = "200", description = "이벤트 정보를 정상적으로 받음"),
@ApiResponse(responseCode = "404", description = "대응되는 이벤트가 존재하지 않음")
})
public ResponseEntity<EventDto> getEventEditData(
@RequestParam("eventId") String eventId
) {
EventDto eventInfo = eventService.getEventInfo(eventId);
return ResponseEntity.ok(eventInfo);
}

@Auth({AuthRole.admin})
@PostMapping("/edit")
@Operation(summary = "이벤트 수정", description = "관리자가 이벤트를 수정한다", responses = {
@ApiResponse(responseCode = "200", description = "이벤트 생성 성공"),
@ApiResponse(responseCode = "4xx", description = "유저 측 실수로 이벤트 생성 실패")
})
public ResponseEntity<?> editEvent(
@Validated({EventEditGroup.class}) @RequestBody EventDto eventDto
) {
eventService.editEvent(eventDto);
return ResponseEntity.ok().build();
}



@Auth({AuthRole.admin})
@PostMapping("/frame")
@Operation(summary = "이벤트 프레임 생성", description = "관리자가 이벤트 프레임을 새롭게 등록한다", responses = {
Expand All @@ -47,4 +75,6 @@ public ResponseEntity<?> createEventFrame(@Valid @RequestBody EventFrameCreateRe
eventService.createEventFrame(req.getName());
return ResponseEntity.status(HttpStatus.CREATED).build();
}


}
Loading
Loading