Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
9da1fc9
feat: 엔티티 파일 생성
si-zero Aug 20, 2025
8598930
feat: 회장단 생성 DTO
si-zero Aug 20, 2025
a56f200
feat: 회장단 레포지토리 생성
si-zero Aug 20, 2025
522330e
feat: 회장단 서비스 생성
si-zero Aug 20, 2025
fbdc6e7
feat: 임원진 컨트롤러 생성
si-zero Aug 20, 2025
e3c25e9
rename: 파일 이름 수정 -> 파일 이름 맨앞 대문자 수정
si-zero Aug 20, 2025
961792b
feat: 임원진 요청 Dto 생성 및 작성
si-zero Aug 20, 2025
d07cd59
feat: 임원진 멤버 생성 서비스 기능 추가
si-zero Aug 20, 2025
c539f9d
comment: 주석 수정
si-zero Aug 20, 2025
d58f4c8
feat: 임원진 멤버 생성 컨트롤러 추가
si-zero Aug 20, 2025
016e911
feat: 임원진 서비스 테스트 파일 추가
si-zero Aug 21, 2025
947bb7d
rename: 스키마 이름 수정
si-zero Aug 21, 2025
f2c9d8a
feat: 임원진 응답 Dto 파일 생성
si-zero Aug 21, 2025
0c22ac6
rename: 코드 수정
si-zero Aug 21, 2025
130ad65
feat: 임원진 수정 요청 DTO
si-zero Aug 21, 2025
4168909
feat: 임원진 조회 ( 이름, 역할, 깃허브 주소 반환 ) 기능 추가
si-zero Aug 21, 2025
452fb7e
feat: 임원진 멤버 조회 테스트 코드 추가
si-zero Aug 21, 2025
2f03e1a
feat: 임원진 멤버 수정 기능 추가
si-zero Aug 21, 2025
7a43386
feat: 임원진 멤버 수정 테스트 추가
si-zero Aug 21, 2025
7ea3397
feat: 임원진 멤버 삭제 기능 추가
si-zero Aug 21, 2025
7636878
feat: 임원진 멤버 삭제 테스트 추가
si-zero Aug 21, 2025
436cdab
rename: 엔드포인트 네이밍 규칙 적용
si-zero Aug 22, 2025
f88c066
rename: 파일이름 오탈자 수정
si-zero Aug 22, 2025
b4fa80c
feat: enum 파일 추가
si-zero Aug 22, 2025
615ea4f
feat: 커스텀 에러 추가 및 예외처리 적용
si-zero Aug 22, 2025
eff42a1
feat: ExecutiveService 인터페이스 구현 및 기존 파일 이름 변경 (DASOMBE-14)
si-zero Aug 24, 2025
9078b43
feat: 임원진 전체 조회 기능 추가 (DASOMBE-14)
si-zero Aug 24, 2025
019b074
feat: 생성, 수정, 삭제 어드민 권한 추가 (DASOMBE-14)
si-zero Aug 25, 2025
2971b11
rename: 오탈자 수정 (DASOMBE-14)
si-zero Aug 25, 2025
f89951b
feat: DTO 요청 데이터 수정 (DASOMBE-14)
si-zero Aug 25, 2025
f39471c
feat: 조회 실패 테스트 추가 (DASOMBE-14)
si-zero Aug 25, 2025
3375bcc
feat: 실패 테스트 케이스 추가 ( 삭제, 생성 ) (DASOMBE-14)
si-zero Aug 25, 2025
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 @@ -36,7 +36,8 @@ public enum ErrorCode {
SLOT_NOT_ACTIVE(400, "C027", "해당 슬롯이 비활성화 되었습니다."),
FILE_ENCODE_FAIL(400, "C028", "파일 인코딩에 실패하였습니다."),
RECRUITMENT_NOT_ACTIVE(400, "C029", "모집 기간이 아닙니다."),
NOT_FOUND_PARTICIPANT(400, "C030", "참가자를 찾을 수 없습니다.")
NOT_FOUND_PARTICIPANT(400, "C030", "참가자를 찾을 수 없습니다."),
EXECUTIVE_NOT_FOUND(400, "C031", "임원진을 찾을 수 없습니다."),
;

private final int status;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package dmu.dasom.api.domain.executive.controller;

import dmu.dasom.api.domain.executive.dto.*;
import dmu.dasom.api.domain.executive.service.ExecutiveServiceImpl;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Min;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@Tag(name = "EXECUTIVE API", description = "임원진 API")
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/executives")
public class ExecutiveController {

private final ExecutiveServiceImpl executiveService;

@Operation(summary = "임원진 조회")
@GetMapping("/{id}")
public ResponseEntity<ExecutiveResponseDto> getExecutiveById(@PathVariable @Min(1) Long id) {
return ResponseEntity.ok(executiveService.getExecutiveById(id));
}

@Operation(summary = "임원진 전체 조회")
@GetMapping
public ResponseEntity<List<ExecutiveListResponseDto>> getAllExecutives() {
return ResponseEntity.ok(executiveService.getAllExecutives());
}

@Operation(summary = "임원진 생성")
@PreAuthorize("hasRole('ADMIN')")
@PostMapping
public ResponseEntity<ExecutiveCreationResponseDto> createExecutive(@Valid @RequestBody ExecutiveRequestDto requestDto) {
return ResponseEntity.status(201).body(executiveService.createExecutive(requestDto));
}

@Operation(summary = "임원진 삭제")
@PreAuthorize("hasRole('ADMIN')")
@DeleteMapping("/{id}")
// Void 사용 이유?
// DELETE 요청 같이 성공/실패만 확인하면 되는 경우 사용
public ResponseEntity<Void> deleteExecutive(@PathVariable @Min(1) Long id) {
executiveService.deleteExective(id);
return ResponseEntity.ok().build();
}

@Operation(summary = "임원진 수정")
@PreAuthorize("hasRole('ADMIN')")
@PutMapping("/{id}")
public ResponseEntity<ExecutiveResponseDto> updateExecutive(@PathVariable @Min(1) Long id,
@Valid @RequestBody ExecutiveUpdateRequestDto requestDto) {
return ResponseEntity.ok(executiveService.updateExecutive(id, requestDto));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package dmu.dasom.api.domain.executive.dto;

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

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "ExecutiveCreationResponseDto", description = "회장단 생성 응답 DTO")
public class ExecutiveCreationResponseDto {

@NotNull
@Schema(description = "회장단 ID", example = "1")
private Long id;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package dmu.dasom.api.domain.executive.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Schema(name = "ExecutiveListResponseDto", description = "임원진 목록 응답 Dto")
public class ExecutiveListResponseDto {

@Schema(description = "임원진 ID", example = "1")
private Long id;

@Schema(description = "임원진 이름", example = "김다솜")
private String name;

@Schema(description = "임원진 직책", example = "회장")
private String position;

@Schema(description = "임원진 깃허브 주소", example = "https://github.com/dasom")
private String githubUrl;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package dmu.dasom.api.domain.executive.dto;

import dmu.dasom.api.domain.executive.entity.ExecutiveEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "ExecutiveRequestDto", description = "임원진 요청 DTO")
public class ExecutiveRequestDto {

private Long id;

@NotBlank(message = "임원진 이름은 필수 입력 사항입니다.")
@Size(max = 50, message = "임원진 이름은 최대 50자입니다.")
@Schema(description = "임원진 이름", example = "김다솜")
private String name;

@NotBlank(message = "임원진 역할은 필수 입력 사항입니다.")
@Schema(description = "임원진 역할", example = "회장")
private String position;

private String githubUrl;

public ExecutiveEntity toEntity() {
return ExecutiveEntity.builder()
.name(this.name)
.position(this.position)
.githubUrl(this.githubUrl)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package dmu.dasom.api.domain.executive.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Schema(name = "ExecutiveResponseDto", description = "임원진 응답 DTO")
public class ExecutiveResponseDto {

@Schema(description = "임원진 ID", example = "1")
private Long id;

@Schema(description = "임원진 이름", example = "김다솜")
private String name;

@Schema(description = "임원진 직책", example = "회장")
private String position;

@Schema(description = "임원진 깃허브", example = "https://github.com/dasom")
private String githubUrl;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package dmu.dasom.api.domain.executive.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "ExecutiveUpdateRequestDto", description = "임원진 멤버 수정 요청 DTO")
public class ExecutiveUpdateRequestDto {

@Size(max = 50, message = "임원진 이름은 최대 50자입니다.")
@Schema(description = "수정할 임원진 이름", example = "김다솜", nullable = true)
private String name;

@Schema(description = "수정할 임원진 직책", example = "회장", nullable = true)
private String position;

@Schema(description = "수정할 임원진 깃허브 주소", example = "https://github.com/dasom", nullable = true)
private String githubUrl;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package dmu.dasom.api.domain.executive.entity;

import dmu.dasom.api.domain.common.BaseEntity; // BaseEntity 상속 받음
import dmu.dasom.api.domain.executive.dto.ExecutiveListResponseDto;
import dmu.dasom.api.domain.executive.dto.ExecutiveResponseDto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.*; // JPA 어노테이션 패키지 ( DB 매핑 관련 )
import lombok.*; // 보일러플레이트 코드 자동 생성 라이브러리

@Getter
@Entity
@Table(name = "executive")
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Schema(description = "조직도 엔티티")
public class ExecutiveEntity extends BaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

// 이름
@Column(nullable = false, length = 50)
private String name;

// 직책
@Column(nullable=false, length = 50)
private String position;

// 깃허브 주소
@Column(nullable=false, length = 255)
private String githubUrl;

// 엔티티 업데이트 메소드
public void update(String name, String position, String githubUrl) {
this.name = name;
this.position = position;
this.githubUrl = githubUrl;
}

// 엔티티 -> DTO 변환 책임
public ExecutiveResponseDto toResponseDto() {
return ExecutiveResponseDto.builder()
.id(this.id)
.name(this.name)
.position(this.position)
.githubUrl(this.githubUrl)
.build();
}

// 임원진 전체 목록 조회
public ExecutiveListResponseDto toListResponseDto() {
return ExecutiveListResponseDto.builder()
.id(this.id)
.name(this.name)
.position(this.position)
.githubUrl(this.githubUrl)
.build();
}
}
24 changes: 24 additions & 0 deletions src/main/java/dmu/dasom/api/domain/executive/enums/Role.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package dmu.dasom.api.domain.executive.enums;

import lombok.AllArgsConstructor;
import lombok.Getter;

// enum + 문자열 매핑
@AllArgsConstructor
@Getter
public enum Role {

ROLE_PRESIDENT("PRESIDENT"), // 회장
ROLE_VICE_PRESIDENT("VICE_PRESIDENT"), // 부회장
ROLE_TECHNICAL_MANAGER("TECHNICAL_MANAGER"), // 기술팀장
ROLE_ACADEMIC_MANAGER("ACADEMIC_MANAGER"), // 학술팀장
ROLE_ACADEMIC_SENIOR("ACADEMIC_SENIOR"), // 학술차장
ROLE_PUBLIC_RELATIONS_MANAGER("PUBLIC_RELATIONS_MANAGER"), // 홍보팀장
ROLE_CLERK("CLERK"), // 서기
ROLE_MANAGER("MANAGER"), // 총무
ROLE_SUB_MANAGER("SUB_MANAGER"), // 부총무
;

private String name;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package dmu.dasom.api.domain.executive.repository;

import dmu.dasom.api.domain.executive.entity.ExecutiveEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface ExecutiveRepository extends JpaRepository<ExecutiveEntity, Long> {

// 회장단 레포지토리

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package dmu.dasom.api.domain.executive.service;

import dmu.dasom.api.domain.executive.dto.*;

import java.util.List;

public interface ExecutiveService {

ExecutiveResponseDto getExecutiveById(Long id);

List<ExecutiveListResponseDto> getAllExecutives();

ExecutiveCreationResponseDto createExecutive(ExecutiveRequestDto requestDto);

void deleteExective(Long id);

ExecutiveResponseDto updateExecutive(Long id, ExecutiveUpdateRequestDto requestDto);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package dmu.dasom.api.domain.executive.service;

import dmu.dasom.api.domain.common.exception.CustomException;
import dmu.dasom.api.domain.common.exception.ErrorCode;
import dmu.dasom.api.domain.executive.dto.*;
import dmu.dasom.api.domain.executive.entity.ExecutiveEntity;
import dmu.dasom.api.domain.executive.repository.ExecutiveRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true) // 기본값 : 읽기 전용
public class ExecutiveServiceImpl implements ExecutiveService {

private final ExecutiveRepository executiveRepository;

// 임원진 멤버 조회
// 이름, 직책, 깃허브 주소 검색
public ExecutiveResponseDto getExecutiveById(Long id) {
ExecutiveEntity executive = executiveRepository.findById(id)
.orElseThrow(() -> new CustomException(ErrorCode.EXECUTIVE_NOT_FOUND));

return executive.toResponseDto();
}

// 임원진 전체 조회
// 이름, 직책, 깃허브 주소 출력
public List<ExecutiveListResponseDto> getAllExecutives() {
List<ExecutiveEntity> executives = executiveRepository.findAll();

List<Long> executiveIds = executives.stream()
.map(ExecutiveEntity::getId)
.toList();

return executives.stream()
.map(executiveEntity -> executiveEntity.toListResponseDto())
.toList();
}

// 임원진 멤버 생성
public ExecutiveCreationResponseDto createExecutive(ExecutiveRequestDto requestDto) {
return new ExecutiveCreationResponseDto(executiveRepository.save(requestDto.toEntity()).getId());
}

// 임원진 멤버 삭제
@Transactional
public void deleteExective(Long id) {
ExecutiveEntity executive = executiveRepository.findById(id)
.orElseThrow(() -> new CustomException(ErrorCode.EXECUTIVE_NOT_FOUND));

executiveRepository.delete(executive);
}

// 임원진 멤버 수정
@Transactional
public ExecutiveResponseDto updateExecutive(Long id, ExecutiveUpdateRequestDto requestDto) {
ExecutiveEntity executive = executiveRepository.findById(id)
.orElseThrow(() -> new CustomException(ErrorCode.EXECUTIVE_NOT_FOUND));

executive.update(requestDto.getName(), requestDto.getPosition(), requestDto.getGithubUrl());

return executive.toResponseDto();
}
}
Loading
Loading