Skip to content

Commit

Permalink
Merge pull request #173 from sopt-makers/sohyeon_#172
Browse files Browse the repository at this point in the history
[FEAT] 알림 어드민 API 개발
  • Loading branch information
thguss authored Oct 26, 2023
2 parents 8f6a1d3 + 6eff718 commit c5e9aef
Show file tree
Hide file tree
Showing 12 changed files with 316 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ public enum ExceptionMessage {
END_LECTURE("이미 종료된 세션입니다."),
NO_SUB_LECTURE_EQUAL_ROUND("해당 라운드와 일치하는 출석 세션이 없습니다."),
FAULT_DATE_FORMATTER("잘못된 날짜 형식입니다."),
DUPLICATED_MEMBER("이미 존재하는 회원입니다.");
DUPLICATED_MEMBER("이미 존재하는 회원입니다."),
INVALID_ALARM("존재하지 않는 알림입니다.");

private final String name;
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,14 @@ public enum ResponseMessage {
SUCCESS_GET_LECTURE("세션 상세 조회 성공"),
SUCCESS_START_ATTENDANCE("출석 시작 성공"),
SUCCESS_GET_MEMBERS("유저 리스트 조회 성공"),
SUCCESS_DELETE_LECTURE("세션 삭제 성공");
SUCCESS_DELETE_LECTURE("세션 삭제 성공"),

/** alarm **/
SUCCESS_CREATE_ALARM("알림 생성 성공"),
SUCCESS_GET_ALARMS("알림 리스트 조회 성공"),
SUCCESS_GET_ALARM("알림 상세 조회 성공"),
SUCCESS_DELETE_ALARM("알림 삭제 성공")
;

private final String message;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package org.sopt.makers.operation.controller.web;

import static org.sopt.makers.operation.common.ApiResponse.*;
import static org.sopt.makers.operation.common.ResponseMessage.*;

import java.net.URI;

import org.sopt.makers.operation.common.ApiResponse;
import org.sopt.makers.operation.dto.alarm.AlarmRequestDTO;
import org.sopt.makers.operation.entity.Part;
import org.sopt.makers.operation.entity.alarm.Status;
import org.sopt.makers.operation.service.AlarmService;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import io.swagger.annotations.ApiOperation;
import lombok.*;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/alarms")
public class AlarmController {
private final AlarmService alarmService;

@ApiOperation("알림 생성")
@PostMapping
public ResponseEntity<ApiResponse> createAlarm(@RequestBody AlarmRequestDTO requestDTO) {
val alarmId = alarmService.createAlarm(requestDTO);
return ResponseEntity
.created(getURI(alarmId))
.body(success(SUCCESS_CREATE_ALARM.getMessage(), alarmId));
}

@ApiOperation("알림 리스트 조회")
@GetMapping
public ResponseEntity<ApiResponse> getAlarms(
@RequestParam(required = false) Integer generation,
@RequestParam(required = false) Part part,
@RequestParam(required = false) Status status,
Pageable pageable
) {
val response = alarmService.getAlarms(generation, part, status, pageable);
return ResponseEntity.ok(success(SUCCESS_GET_ALARMS.getMessage(), response));
}

@ApiOperation("알림 상세 조회")
@GetMapping("/{alarmId}")
public ResponseEntity<ApiResponse> getAlarm(@PathVariable Long alarmId) {
val response = alarmService.getAlarm(alarmId);
return ResponseEntity.ok(success(SUCCESS_GET_ALARM.getMessage(), response));
}

@ApiOperation("알림 삭제")
@DeleteMapping("/{alarmId}")
public ResponseEntity<ApiResponse> deleteAlarm(@PathVariable Long alarmId) {
alarmService.deleteAlarm(alarmId);
return ResponseEntity.ok(success(SUCCESS_DELETE_ALARM.getMessage()));
}

private URI getURI(Long alarmId) {
return ServletUriComponentsBuilder
.fromCurrentRequest()
.path("/{alarmId}")
.buildAndExpand(alarmId)
.toUri();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.List;

import org.sopt.makers.operation.entity.Part;
import org.sopt.makers.operation.entity.alarm.Alarm;
import org.sopt.makers.operation.entity.alarm.Attribute;
import org.sopt.makers.operation.entity.alarm.Status;

Expand All @@ -12,9 +13,11 @@ public record AlarmRequestDTO(
String title,
String content,
String link,
boolean isActive,
Boolean isActive,
Part part,
List<Long> targetList,
Status status
List<Long> targetList
) {
public Alarm toEntity() {
return new Alarm(this);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.sopt.makers.operation.dto.alarm;

import static java.util.Objects.*;

import org.sopt.makers.operation.entity.alarm.Alarm;

import lombok.Builder;

@Builder
public record AlarmResponseDTO(
String attribute,
String part,
Boolean isActive,
String title,
String content,
String link,
String createdAt,
String sendAt
) {
public static AlarmResponseDTO of(Alarm alarm) {
return AlarmResponseDTO.builder()
.attribute(alarm.getAttribute().getName())
.part(alarm.getPart().getName())
.isActive(alarm.getIsActive())
.title(alarm.getTitle())
.content(alarm.getContent())
.link(alarm.getLink())
.createdAt(alarm.getCreatedDate().toString())
.sendAt(nonNull(alarm.getSendAt()) ? alarm.getSendAt().toString() : null)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.sopt.makers.operation.dto.alarm;

import static java.util.Objects.*;

import java.util.List;

import org.sopt.makers.operation.entity.alarm.Alarm;

import lombok.Builder;

public record AlarmsResponseDTO(
List<AlarmVO> alarms
) {
public static AlarmsResponseDTO of(List<Alarm> alarms) {
return new AlarmsResponseDTO(alarms.stream().map(AlarmVO::of).toList());
}

@Builder
record AlarmVO(
Long alarmId,
String part,
String attribute,
String title,
String content,
String sendAt,
String status
) {
static AlarmVO of(Alarm alarm) {
return AlarmVO.builder()
.alarmId(alarm.getId())
.part(nonNull(alarm.getPart()) ? alarm.getPart().getName() : null)
.attribute(alarm.getAttribute().getName())
.title(alarm.getTitle())
.content(alarm.getContent())
.sendAt(nonNull(alarm.getSendAt()) ? alarm.getSendAt().toString() : null)
.status(alarm.getStatus().getName())
.build();
}
}
}
16 changes: 11 additions & 5 deletions src/main/java/org/sopt/makers/operation/entity/alarm/Alarm.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.sopt.makers.operation.entity.alarm;

import static java.util.Objects.*;
import static javax.persistence.EnumType.*;
import static javax.persistence.GenerationType.*;

Expand Down Expand Up @@ -43,14 +44,14 @@ public class Alarm extends BaseEntity {

private String link;

private boolean isActive;
private Boolean isActive;

@Enumerated(value = STRING)
private Part part;

@Column(columnDefinition = "TEXT")
@Convert(converter = LongListConverter.class)
private List<Long> targetList;
private List<String> targetList;

@Column(nullable = false)
@Enumerated(value = STRING)
Expand All @@ -64,8 +65,13 @@ public Alarm(AlarmRequestDTO requestDTO) {
this.title = requestDTO.title();
this.content = requestDTO.content();
this.link = requestDTO.link();
this.isActive = requestDTO.isActive();
this.part = requestDTO.part();
this.targetList = requestDTO.targetList();
if (nonNull(requestDTO.isActive()) && nonNull(requestDTO.part())) {
this.isActive = requestDTO.isActive();
this.part = requestDTO.part();
}
if (nonNull(requestDTO.targetList())) {
this.targetList = requestDTO.targetList();
}
this.status = Status.BEFORE;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.sopt.makers.operation.repository.alarm;

import java.util.List;

import org.sopt.makers.operation.entity.Part;
import org.sopt.makers.operation.entity.alarm.Alarm;
import org.sopt.makers.operation.entity.alarm.Status;
import org.springframework.data.domain.Pageable;

public interface AlarmCustomRepository {
List<Alarm> getAlarms(Integer generation, Part part, Status status, Pageable pageable);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.sopt.makers.operation.repository.alarm;

import org.sopt.makers.operation.entity.alarm.Alarm;
import org.springframework.data.jpa.repository.JpaRepository;

public interface AlarmRepository extends JpaRepository<Alarm, Long>, AlarmCustomRepository {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package org.sopt.makers.operation.repository.alarm;

import static java.util.Objects.*;
import static org.sopt.makers.operation.entity.Part.*;
import static org.sopt.makers.operation.entity.alarm.QAlarm.*;

import java.util.List;

import org.sopt.makers.operation.entity.Part;
import org.sopt.makers.operation.entity.alarm.Alarm;
import org.sopt.makers.operation.entity.alarm.Status;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;

import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;

import lombok.RequiredArgsConstructor;

@Repository
@RequiredArgsConstructor
public class AlarmRepositoryImpl implements AlarmCustomRepository {
private final JPAQueryFactory queryFactory;

@Override
public List<Alarm> getAlarms(Integer generation, Part part, Status status, Pageable pageable) {
return queryFactory
.selectFrom(alarm)
.where(
generationEq(generation),
partEq(part),
statusEq(status)
)
.orderBy(alarm.createdDate.desc())
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
}

private BooleanExpression generationEq(Integer generation) {
return nonNull(generation) ? alarm.generation.eq(generation) : null;
}

private BooleanExpression partEq(Part part) {
return (nonNull(part) && !part.equals(ALL)) ? alarm.part.eq(part) : null;
}

private BooleanExpression statusEq(Status status) {
return nonNull(status) ? alarm.status.eq(status) : null;
}
}
15 changes: 15 additions & 0 deletions src/main/java/org/sopt/makers/operation/service/AlarmService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.sopt.makers.operation.service;

import org.sopt.makers.operation.dto.alarm.AlarmRequestDTO;
import org.sopt.makers.operation.dto.alarm.AlarmResponseDTO;
import org.sopt.makers.operation.dto.alarm.AlarmsResponseDTO;
import org.sopt.makers.operation.entity.Part;
import org.sopt.makers.operation.entity.alarm.Status;
import org.springframework.data.domain.Pageable;

public interface AlarmService {
Long createAlarm(AlarmRequestDTO requestDTO);
AlarmsResponseDTO getAlarms(Integer generation, Part part, Status status, Pageable pageable);
AlarmResponseDTO getAlarm(Long alarmId);
void deleteAlarm(Long alarmId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package org.sopt.makers.operation.service;

import static org.sopt.makers.operation.common.ExceptionMessage.*;

import javax.persistence.EntityNotFoundException;

import org.sopt.makers.operation.dto.alarm.AlarmRequestDTO;
import org.sopt.makers.operation.dto.alarm.AlarmResponseDTO;
import org.sopt.makers.operation.dto.alarm.AlarmsResponseDTO;
import org.sopt.makers.operation.entity.Part;
import org.sopt.makers.operation.entity.alarm.Alarm;
import org.sopt.makers.operation.entity.alarm.Status;
import org.sopt.makers.operation.repository.alarm.AlarmRepository;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import lombok.*;

@Service
@RequiredArgsConstructor
public class AlarmServiceImpl implements AlarmService {
private final AlarmRepository alarmRepository;

@Override
@Transactional
public Long createAlarm(AlarmRequestDTO requestDTO) {
val alarmEntity = requestDTO.toEntity();
val savedAlarm = alarmRepository.save(alarmEntity);
return savedAlarm.getId();
}

@Override
public AlarmsResponseDTO getAlarms(Integer generation, Part part, Status status, Pageable pageable) {
val alarms = alarmRepository.getAlarms(generation, part, status, pageable);
return AlarmsResponseDTO.of(alarms);
}

@Override
public AlarmResponseDTO getAlarm(Long alarmId) {
val alarm = findAlarm(alarmId);
return AlarmResponseDTO.of(alarm);
}

@Override
@Transactional
public void deleteAlarm(Long alarmId) {
val alarm = findAlarm(alarmId);
alarmRepository.delete(alarm);
}

private Alarm findAlarm(Long alarmId) {
return alarmRepository.findById(alarmId)
.orElseThrow(() -> new EntityNotFoundException(INVALID_ALARM.getName()));
}
}

0 comments on commit c5e9aef

Please sign in to comment.