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: 운영진 관리 기능 구현 완료 #648

Open
wants to merge 14 commits into
base: develop
Choose a base branch
from
Open
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
@@ -0,0 +1,33 @@
package page.clab.api.domain.memberManagement.executive.adapter.in.web;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
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.RestController;
import page.clab.api.domain.memberManagement.executive.application.dto.request.ExecutiveRequestDto;
import page.clab.api.domain.memberManagement.executive.application.port.in.RegisterExecutiveUseCase;
import page.clab.api.global.common.dto.ApiResponse;

@RestController
@RequestMapping("/api/v1/executive")
@RequiredArgsConstructor
@Tag(name = "Member Management - Executive", description = "운영진")
public class ExecutiveRegisterController {

private final RegisterExecutiveUseCase registerExecutiveUseCase;

@Operation(summary = "[A] 운영진 등록", description = "ROLE_ADMIN 이상의 권한이 필요함")
@PreAuthorize("hasRole('ADMIN')")
@PostMapping("")
public ApiResponse<String> registerExecutive(
@Valid @RequestBody ExecutiveRequestDto requestDto
) {
String id = registerExecutiveUseCase.registerExecutive(requestDto);
return ApiResponse.success(id);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package page.clab.api.domain.memberManagement.executive.adapter.in.web;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import page.clab.api.domain.memberManagement.executive.application.port.in.RemoveExecutiveUseCase;
import page.clab.api.global.common.dto.ApiResponse;

@RestController
@RequestMapping("/api/v1/executive")
@RequiredArgsConstructor
@Tag(name = "Member Management - Executive", description = "운영진")
public class ExecutiveRemoveController {

private final RemoveExecutiveUseCase removeExecutiveUseCase;

@Operation(summary = "[A] 운영진 정보 삭제", description = "ROLE_ADMIN 이상의 권한이 필요함")
@PreAuthorize("hasRole('ADMIN')")
@DeleteMapping("/{executiveId}")
public ApiResponse<String> removeMember(
@PathVariable(name = "executiveId") String executiveId
) {
String id = removeExecutiveUseCase.removeExecutive(executiveId);
return ApiResponse.success(id);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package page.clab.api.domain.memberManagement.executive.adapter.in.web;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import page.clab.api.domain.memberManagement.executive.application.dto.response.ExecutiveResponseDto;
import page.clab.api.domain.memberManagement.executive.application.port.in.RetrieveExecutiveUseCase;
import page.clab.api.global.common.dto.ApiResponse;

@RestController
@RequestMapping("/api/v1/executive")
@RequiredArgsConstructor
@Tag(name = "Member Management - Executive", description = "운영진")
public class ExecutiveRetrievalController {

private final RetrieveExecutiveUseCase retrieveExecutiveUseCase;

@Operation(summary = "[G] 운영진 정보 조회", description = "ROLE_GUEST 이상의 권한이 필요함")
@PreAuthorize("hasRole('GUEST')")
@GetMapping("")
public ApiResponse<List<ExecutiveResponseDto>> retrieveExecutives() {
List<ExecutiveResponseDto> executives = retrieveExecutiveUseCase.retrieveExecutives();
return ApiResponse.success(executives);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package page.clab.api.domain.memberManagement.executive.adapter.in.web;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import page.clab.api.domain.memberManagement.executive.application.dto.request.ExecutiveUpdateRequestDto;
import page.clab.api.domain.memberManagement.executive.application.port.in.UpdateExecutiveUseCase;
import page.clab.api.global.common.dto.ApiResponse;

@RestController
@RequestMapping("/api/v1/executive")
@RequiredArgsConstructor
@Tag(name = "Member Management - Executive", description = "운영진")
public class ExecutiveUpdateController {

private final UpdateExecutiveUseCase updateExecutiveUseCase;

@Operation(summary = "[A] 운영진 정보 수정", description = "ROLE_ADMIN 이상의 권한이 필요함")
@PreAuthorize("hasRole('ADMIN')")
@PatchMapping("/{executiveId}")
public ApiResponse<String> updateExecutive(
@PathVariable(name = "executiveId") String executiveId,
@RequestBody ExecutiveUpdateRequestDto requestDto
) {
String id = updateExecutiveUseCase.updateExecutive(executiveId, requestDto);
return ApiResponse.success(id);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package page.clab.api.domain.memberManagement.executive.adapter.out.persistence;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.Size;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.SQLRestriction;
import page.clab.api.global.common.domain.BaseEntity;

@Entity
@Table(name = "executive")
@Getter
@Setter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@SQLDelete(sql = "UPDATE executive SET is_deleted = true WHERE id = ?")
@SQLRestriction("is_deleted = false")
public class ExecutiveJpaEntity extends BaseEntity {

@Id
@Column(nullable = false, updatable = false, unique = true)
@Size(min = 9, max = 9, message = "{size.executive.id}")
private String id;

@Column(nullable = false)
@Size(min = 1, max = 10, message = "{size.executive.name}")
private String name;

@Column(nullable = false)
@Email(message = "{email.executive.email}")
@Size(min = 1, message = "{size.executive.email}")
private String email;

@Column(nullable = false)
private String interests;

private String imageUrl;

@Column(name = "is_deleted", nullable = false)
private Boolean isDeleted;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isDeleted 필드를 사용하는 경우 언제 소프트 딜리트가 되었는지 확인할 수 있는 수단이 되기 때문에 deletedAt 필드도 함께 사용하는 것이 좋아보여요!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deletedAt을 추가함으로써 삭제된지 오래된 데이터들을 영구 삭제하거나, 혹시라도 복구할 때 삭제 시점을 보고 복구하기 위함인가요??
boardEmoji를 제외한 다른 도메인들에는 deletedAt이 없어서 질문드립니다!
deletedAt을 추가하는 도메인에 대한 특이사항이 있을까요?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 현재로서는 updatedAt 필드만으로도 삭제 시점을 충분히 관리할 수 있다고 생각해요. 다만, deletedAt 필드를 추가하게 된다면, 다른 소프트 삭제를 사용하는 테이블에도 동일하게 적용하여 일관성을 유지하는 것이 적절하다고 판단돼요.

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package page.clab.api.domain.memberManagement.executive.adapter.out.persistence;

import org.mapstruct.Mapper;
import page.clab.api.domain.memberManagement.executive.domain.Executive;

@Mapper(componentModel = "spring")
public interface ExecutiveMapper {

ExecutiveJpaEntity toEntity(Executive executive);

Executive toDomain(ExecutiveJpaEntity jpaEntity);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package page.clab.api.domain.memberManagement.executive.adapter.out.persistence;

import java.util.List;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import page.clab.api.domain.memberManagement.executive.application.port.out.RegisterExecutivePort;
import page.clab.api.domain.memberManagement.executive.application.port.out.RetrieveExecutivePort;
import page.clab.api.domain.memberManagement.executive.application.port.out.UpdateExecutivePort;
import page.clab.api.domain.memberManagement.executive.domain.Executive;
import page.clab.api.global.exception.NotFoundException;

@Component
@RequiredArgsConstructor
public class ExecutivePersistenceAdapter implements
RegisterExecutivePort,
RetrieveExecutivePort,
UpdateExecutivePort {

private final ExecutiveMapper executiveMapper;
private final ExecutiveRepository executiveRepository;

@Override
public Executive save(Executive executive) {
ExecutiveJpaEntity jpaEntity = executiveMapper.toEntity(executive);
ExecutiveJpaEntity savedEntity = executiveRepository.save(jpaEntity);
return executiveMapper.toDomain(savedEntity);
}

@Override
public Executive update(Executive executive) {
ExecutiveJpaEntity jpaEntity = executiveMapper.toEntity(executive);
ExecutiveJpaEntity savedEntity = executiveRepository.save(jpaEntity);
return executiveMapper.toDomain(savedEntity);
}

@Override
public List<Executive> findAll() {
List<ExecutiveJpaEntity> jpaEntities = executiveRepository.findAll();
return jpaEntities.stream()
.map(executiveMapper::toDomain)
.collect(Collectors.toList());
}

@Override
public Executive getById(String executiveId) {
ExecutiveJpaEntity jpaEntity = executiveRepository.findById(executiveId)
.orElseThrow(() -> new NotFoundException("[Executive] id: " + executiveId + "에 해당하는 운영진이 존재하지 않습니다."));
return executiveMapper.toDomain(jpaEntity);
}

@Override
public Boolean existsById(String executiveId) {
return executiveRepository.existsById(executiveId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package page.clab.api.domain.memberManagement.executive.adapter.out.persistence;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface ExecutiveRepository extends JpaRepository<ExecutiveJpaEntity, String> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package page.clab.api.domain.memberManagement.executive.application.dto.mapper;

import org.springframework.stereotype.Component;
import page.clab.api.domain.memberManagement.executive.application.dto.request.ExecutiveRequestDto;
import page.clab.api.domain.memberManagement.executive.application.dto.response.ExecutiveResponseDto;
import page.clab.api.domain.memberManagement.executive.domain.Executive;

@Component
public class ExecutiveDtoMapper {

public Executive fromDto(ExecutiveRequestDto requestDto) {
return Executive.builder()
.id(requestDto.getExecutiveId())
.name(requestDto.getName())
.email(requestDto.getEmail())
.interests(requestDto.getInterests())
.imageUrl(requestDto.getImageUrl())
.isDeleted(false)
.build();
}

public ExecutiveResponseDto toDto(Executive executive, String position) {
return ExecutiveResponseDto.builder()
.executiveId(executive.getId())
.name(executive.getName())
.email(executive.getEmail())
.interests(executive.getInterests())
.position(position)
.imageUrl(executive.getImageUrl())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package page.clab.api.domain.memberManagement.executive.application.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class ExecutiveRequestDto {

@NotNull(message = "{notNull.executive.executiveId}")
@Schema(description = "학번", example = "202310000")
private String executiveId;

@NotNull(message = "{notNull.executive.name}")
@Schema(description = "이름", example = "홍길동")
private String name;

@NotNull(message = "{notNull.executive.email}")
@Schema(description = "이메일", example = "[email protected]")
Copy link
Preview

Copilot AI Dec 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a typo in the email example: "[email protected]" should be "[email protected]".

Suggested change
@Schema(description = "이메일", example = "clab.coreteam@gamil.com")
@Schema(description = "이메일", example = "clab.coreteam@gmail.com")

Copilot is powered by AI, so mistakes are possible. Review output carefully before use.

Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
private String email;

@NotNull(message = "{notNull.executive.interests}")
@Schema(description = "분야", example = "Back-End")
private String interests;

@Schema(description = "프로필 이미지", example = "https://www.clab.page/assets/dongmin-860f3a1e.jpeg")
private String imageUrl;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package page.clab.api.domain.memberManagement.executive.application.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class ExecutiveUpdateRequestDto {

@Schema(description = "이름", example = "홍길동")
private String name;

@Schema(description = "이메일", example = "[email protected]")
private String email;

@Schema(description = "분야", example = "Back-End")
private String interests;

@Schema(description = "프로필 이미지", example = "https://www.clab.page/assets/dongmin-860f3a1e.jpeg")
private String imageUrl;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package page.clab.api.domain.memberManagement.executive.application.dto.response;

import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class ExecutiveResponseDto {

private String executiveId;
private String name;
private String email;
private String interests;
private String position;
private String imageUrl;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package page.clab.api.domain.memberManagement.executive.application.exception;

public class ExecutiveRegistrationException extends RuntimeException {

public ExecutiveRegistrationException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package page.clab.api.domain.memberManagement.executive.application.port.in;

import page.clab.api.domain.memberManagement.executive.application.dto.request.ExecutiveRequestDto;

public interface RegisterExecutiveUseCase {
String registerExecutive(ExecutiveRequestDto requestDto);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package page.clab.api.domain.memberManagement.executive.application.port.in;

public interface RemoveExecutiveUseCase {
String removeExecutive(String executiveId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package page.clab.api.domain.memberManagement.executive.application.port.in;

import java.util.List;
import page.clab.api.domain.memberManagement.executive.application.dto.response.ExecutiveResponseDto;

public interface RetrieveExecutiveUseCase {
List<ExecutiveResponseDto> retrieveExecutives();
}
Loading
Loading