Skip to content

Commit 743a924

Browse files
ysw789hodoonjucheonsusi-zerojunjinyun
authored
Release v2.0.0
* fix: 엔드포인트 접근 권한 수정 * refactor: Google API 관련 미사용 컨트롤러 삭제, 패키지 통합 * chore: 패키지 참조 변경 * chore: Docs 노출 숨김 처리 * refactor: 어드민용 관리 API 접근 권한 분리 * refactor: 이메일 발송 비동기식으로 전환 (DASOMBE-13) * refactor: 이메일 전송 비동기 처리 (DASOMBE-13) * refactor: 이메일 전송 오류시 로그 저장 DB생성, 이메일 템플릿 분기처리 (DASOMBE-13) * refactor: 이메일 로그 builder 패턴으로 인스턴스 초기화 완료 (DASOMBE-13) * refactor: 성공여부도 저장 가능하게 수정 (DASOMBE-13) * refactor: 빌더 패턴 EmailLog엔티티 안으로 이동, 에러메시지 저장 로직 try-catch문 밖으로 이동 (DASOMBE-13) * feat: 활동 연혁 CRUD 기능 구현 및 테스트 * feat: 활동 연혁 엔티티 정의 (DASOMBE-15) * feat: 활동 연혁 리포지토리 인터페이스 정의 (DASOMBE-15) * feat: 활동 연혁 DTO 정의 (DASOMBE-15) * feat: 활동 연혁 서비스 로직 구현 (DASOMBE-15) * feat: 활동 연혁 API 컨트롤러 구현 (DASOMBE-15) * test: 활동 연혁 서비스 단위 테스트 추가 (DASOMBE-15) * chore: 활동 연혁 기능 관련 환경 설정 (DASOMBE-15) * refactor: 엔티티 계층 리팩토링 (DASOMBE-15) * refactor: 리포지토리 계층 리팩토링 (DASOMBE-15) * refactor: DTO 계층 리팩토링 및 분리 (DASOMBE-15) * remove: 사용하지 않는 파일 제거 (DASOMBE-15) * refactor: 서비스 계층 리팩토링 (DASOMBE-15) * refactor: 컨트롤러 계층 리팩토링 (DASOMBE-15) * test: 테스트 코드 수정 (DASOMBE-15) * chore: 환경설정 변경 (DASOMBE-15) * rename: Activity로 변경 (DASOMBE-15) * [feat] 조직도 API 추가 * feat: 엔티티 파일 생성 * feat: 회장단 생성 DTO * feat: 회장단 레포지토리 생성 * feat: 회장단 서비스 생성 * feat: 임원진 컨트롤러 생성 * rename: 파일 이름 수정 -> 파일 이름 맨앞 대문자 수정 * feat: 임원진 요청 Dto 생성 및 작성 * feat: 임원진 멤버 생성 서비스 기능 추가 * comment: 주석 수정 * feat: 임원진 멤버 생성 컨트롤러 추가 * feat: 임원진 서비스 테스트 파일 추가 * rename: 스키마 이름 수정 * feat: 임원진 응답 Dto 파일 생성 * rename: 코드 수정 * feat: 임원진 수정 요청 DTO * feat: 임원진 조회 ( 이름, 역할, 깃허브 주소 반환 ) 기능 추가 * feat: 임원진 멤버 조회 테스트 코드 추가 * feat: 임원진 멤버 수정 기능 추가 * feat: 임원진 멤버 수정 테스트 추가 * feat: 임원진 멤버 삭제 기능 추가 * feat: 임원진 멤버 삭제 테스트 추가 * rename: 엔드포인트 네이밍 규칙 적용 * rename: 파일이름 오탈자 수정 * feat: enum 파일 추가 * feat: 커스텀 에러 추가 및 예외처리 적용 * feat: ExecutiveService 인터페이스 구현 및 기존 파일 이름 변경 (DASOMBE-14) * feat: 임원진 전체 조회 기능 추가 (DASOMBE-14) * feat: 생성, 수정, 삭제 어드민 권한 추가 (DASOMBE-14) * rename: 오탈자 수정 (DASOMBE-14) * feat: DTO 요청 데이터 수정 (DASOMBE-14) * feat: 조회 실패 테스트 추가 (DASOMBE-14) * feat: 실패 테스트 케이스 추가 ( 삭제, 생성 ) (DASOMBE-14) * [refactor] 조직도 API 요청 파라미터 수정 (DASOMBE-21) * refactor: 이미지 저장 로직 수정(DASOMBE-19) * refactor: 기수 관리 추가 및 기존 기능과 연동 * feat: 기수관리를 위한 엔티티, 서비스, 레포지토리 생성 및 대응하는 에러코드 추가 (DASOMBE-16) * test: GenerationService의 테스트 코드 작성 (DASOMBE-16) * refactor: 모집일정에 기수를 의미하는 GENERATION 키 생성 (DASOMBE-16) * refactor: 모집일정의 조회, 수정 부분에 기수 반영 및 테스트코드에 반영하고 기수 조회, 수정 코드 하단에 추가 (DASOMBE-16) * refactor: 멤버 엔티티에 기수 추가 및 회원가입 시 GenerationService의 값을 읽어 자동으로 기수 기입하게 구현 및 테스트 코드에 반영 (DASOMBE-16) * feat: 기수 관리(조회,수정) 을 담당하는 AdminGenerationController 추가 (DASOMBE-16) * fix: ErrorCode 에 리베이스 중 누락된 EXECUTIVE_NOT_FOUND 추가 * refactor: 기수 시스템을 모집일정에 통합시키기 위해 Generation 관련 기능 삭제(DASOMBE-16) * refactor: 기수 시스템을 모집일정에 통합 && 코드 정리 및 통합 * refactor: 기수 시스템을 모집일정에 통합함에 따라 멤버의 기수 코드 수정 후 코드 정리 && 회원가입 시 선택적으로 기수를 입력받고 값이 없을경우, 현재 모집일정의 기수를 기입하게 수정 (DASOMBE-16) * refactor: 면접 예약 날짜 변경 기능 추가 (DASOMBE-18) * DASOMBE-18 지원자 학번,이메일 확인후 면접 예약 및 날자 변경 기능(테스트 못해봄.) * DASOMBE-18 면접 지원자 예약 날짜 변경 테스트 * DASOMBE-18 이메일 인증 * DASOMBE-18 <피드백 반영. 추가 설명 표시. 인증코드 템플릿 반영> * [refactor] DASOMBE-18 <에러 코드 명세 추가> * refactor: 면접 예약 날짜 변경 기능 추가(DASOMBE-18) --------- Co-authored-by: DoHoon Yoon <[email protected]> Co-authored-by: hodoon <[email protected]> * refactor: 로그인 관리자와 일반으로 분리 (BACKEND-17) Co-authored-by: DoHoon Yoon <[email protected]> --------- Co-authored-by: DoHoon Yoon <[email protected]> Co-authored-by: JOO <[email protected]> Co-authored-by: Youwol <[email protected]> Co-authored-by: junjinyun <[email protected]> Co-authored-by: YoonJae <[email protected]> Co-authored-by: hodoon <[email protected]>
1 parent eec1b56 commit 743a924

File tree

83 files changed

+2714
-386
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

83 files changed

+2714
-386
lines changed

build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ dependencies {
5757
implementation 'io.jsonwebtoken:jjwt-api:0.12.6'
5858
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6'
5959
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.6'
60+
61+
implementation 'software.amazon.awssdk:s3:2.28.23'
6062
}
6163

6264
tasks.named('test') {

src/main/java/dmu/dasom/api/ApiApplication.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
import org.springframework.boot.SpringApplication;
44
import org.springframework.boot.autoconfigure.SpringBootApplication;
55
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
6+
import org.springframework.scheduling.annotation.EnableAsync;
67

78
@EnableJpaAuditing
89
@SpringBootApplication
10+
@EnableAsync
911
public class ApiApplication {
10-
1112
public static void main(String[] args) {
1213
SpringApplication.run(ApiApplication.class, args);
1314
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package dmu.dasom.api.domain.activity.controller;
2+
3+
import dmu.dasom.api.domain.activity.dto.ActivityResponseDto;
4+
import dmu.dasom.api.domain.activity.service.ActivityService;
5+
import dmu.dasom.api.domain.common.exception.ErrorResponse;
6+
import io.swagger.v3.oas.annotations.Operation;
7+
import io.swagger.v3.oas.annotations.media.Content;
8+
import io.swagger.v3.oas.annotations.media.ExampleObject;
9+
import io.swagger.v3.oas.annotations.media.Schema;
10+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
11+
import io.swagger.v3.oas.annotations.responses.ApiResponses;
12+
import io.swagger.v3.oas.annotations.tags.Tag;
13+
import lombok.RequiredArgsConstructor;
14+
import org.springframework.http.ResponseEntity;
15+
import org.springframework.web.bind.annotation.GetMapping;
16+
import org.springframework.web.bind.annotation.RequestMapping;
17+
import org.springframework.web.bind.annotation.RestController;
18+
19+
import java.util.List;
20+
21+
@RestController
22+
@RequestMapping("/api/activities")
23+
@RequiredArgsConstructor
24+
@Tag(name = "Activity API", description = "활동 연혁 조회 API")
25+
public class ActivityController {
26+
27+
private final ActivityService activityService;
28+
29+
@Operation(summary = "활동 연혁 전체 조회", description = "모든 활동 연혁을 연도별, 섹션별로 그룹화하여 조회합니다.")
30+
@ApiResponses(value = {
31+
@ApiResponse(responseCode = "200", description = "활동 연혁 전체 조회 성공"),
32+
@ApiResponse(responseCode = "405", description = "허용되지 않은 요청 방식",
33+
content = @Content(
34+
mediaType = "application/json",
35+
schema = @Schema(implementation = ErrorResponse.class),
36+
examples = {
37+
@ExampleObject(
38+
name = "허용되지 않은 메서드",
39+
value = "{ \"code\": \"C007\", \"message\": \"허용되지 않은 요청 방식입니다.\" }"
40+
)
41+
}
42+
)),
43+
@ApiResponse(responseCode = "500", description = "서버 내부 오류",
44+
content = @Content(
45+
mediaType = "application/json",
46+
schema = @Schema(implementation = ErrorResponse.class),
47+
examples = {
48+
@ExampleObject(
49+
name = "서버 문제 발생",
50+
value = "{ \"code\": \"C009\", \"message\": \"서버에 문제가 발생하였습니다.\" }"
51+
)
52+
}
53+
))
54+
})
55+
@GetMapping
56+
public ResponseEntity<List<ActivityResponseDto>> getActivities() {
57+
return ResponseEntity.ok(activityService.getActivities());
58+
}
59+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package dmu.dasom.api.domain.activity.controller;
2+
3+
import dmu.dasom.api.domain.activity.dto.ActivityRequestDto;
4+
import dmu.dasom.api.domain.activity.service.ActivityService;
5+
import dmu.dasom.api.domain.common.exception.ErrorResponse;
6+
import io.swagger.v3.oas.annotations.Operation;
7+
import io.swagger.v3.oas.annotations.media.Content;
8+
import io.swagger.v3.oas.annotations.media.ExampleObject;
9+
import io.swagger.v3.oas.annotations.media.Schema;
10+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
11+
import io.swagger.v3.oas.annotations.responses.ApiResponses;
12+
import io.swagger.v3.oas.annotations.tags.Tag;
13+
import jakarta.validation.Valid;
14+
import jakarta.validation.constraints.Min;
15+
import lombok.RequiredArgsConstructor;
16+
import org.springframework.http.ResponseEntity;
17+
import org.springframework.web.bind.annotation.*;
18+
19+
@RestController
20+
@RequestMapping("/api/admin/activities")
21+
@RequiredArgsConstructor
22+
@Tag(name = "ADMIN - Activity API", description = "어드민 활동 연혁 관리 API")
23+
public class AdminActivityController {
24+
25+
private final ActivityService activityService;
26+
27+
@Operation(summary = "활동 연혁 생성")
28+
@ApiResponses(value = {
29+
@ApiResponse(responseCode = "201", description = "활동 연혁 생성 성공"),
30+
@ApiResponse(responseCode = "400", description = "요청 값 유효성 검사 실패", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class), examples = @ExampleObject(name = "유효성 검사 실패", value = "{ \"code\": \"C007\", \"message\": \"요청한 값이 올바르지 않습니다.\" }"))),
31+
@ApiResponse(responseCode = "401", description = "인증되지 않은 사용자", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class), examples = @ExampleObject(name = "인증 실패", value = "{ \"code\": \"C001\", \"message\": \"인증되지 않은 사용자입니다.\" }"))),
32+
@ApiResponse(responseCode = "403", description = "접근 권한 없음 (ADMIN이 아님)", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class), examples = @ExampleObject(name = "권한 없음", value = "{ \"code\": \"C002\", \"message\": \"접근 권한이 없습니다.\" }"))),
33+
@ApiResponse(responseCode = "500", description = "서버 내부 오류", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class), examples = @ExampleObject(name = "서버 문제 발생", value = "{ \"code\": \"C009\", \"message\": \"서버에 문제가 발생하였습니다.\" }")))
34+
})
35+
@PostMapping
36+
public ResponseEntity<Void> createActivity(@Valid @RequestBody ActivityRequestDto requestDto) {
37+
activityService.createActivity(requestDto);
38+
return ResponseEntity.status(201).build();
39+
}
40+
41+
@Operation(summary = "활동 연혁 수정")
42+
@ApiResponses(value = {
43+
@ApiResponse(responseCode = "200", description = "활동 연혁 수정 성공"),
44+
@ApiResponse(responseCode = "400", description = "요청 값 유효성 검사 실패", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class), examples = @ExampleObject(name = "유효성 검사 실패", value = "{ \"code\": \"C007\", \"message\": \"요청한 값이 올바르지 않습니다.\" }"))),
45+
@ApiResponse(responseCode = "401", description = "인증되지 않은 사용자", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class), examples = @ExampleObject(name = "인증 실패", value = "{ \"code\": \"C001\", \"message\": \"인증되지 않은 사용자입니다.\" }"))),
46+
@ApiResponse(responseCode = "403", description = "접근 권한 없음 (ADMIN이 아님)", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class), examples = @ExampleObject(name = "권한 없음", value = "{ \"code\": \"C002\", \"message\": \"접근 권한이 없습니다.\" }"))),
47+
@ApiResponse(responseCode = "404", description = "수정할 리소스를 찾을 수 없음", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class), examples = @ExampleObject(name = "조회 결과 없음", value = "{ \"code\": \"C010\", \"message\": \"해당 리소스를 찾을 수 없습니다.\" }"))),
48+
@ApiResponse(responseCode = "500", description = "서버 내부 오류", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class), examples = @ExampleObject(name = "서버 문제 발생", value = "{ \"code\": \"C009\", \"message\": \"서버에 문제가 발생하였습니다.\" }")))
49+
})
50+
@PutMapping("/{activityId}")
51+
public ResponseEntity<Void> updateActivity(
52+
@PathVariable @Min(1) Long activityId,
53+
@Valid @RequestBody ActivityRequestDto requestDto
54+
) {
55+
activityService.updateActivity(activityId, requestDto);
56+
return ResponseEntity.ok().build();
57+
}
58+
59+
@Operation(summary = "활동 연혁 삭제")
60+
@ApiResponses(value = {
61+
@ApiResponse(responseCode = "204", description = "활동 연혁 삭제 성공"),
62+
@ApiResponse(responseCode = "401", description = "인증되지 않은 사용자", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class), examples = @ExampleObject(name = "인증 실패", value = "{ \"code\": \"C001\", \"message\": \"인증되지 않은 사용자입니다.\" }"))),
63+
@ApiResponse(responseCode = "403", description = "접근 권한 없음 (ADMIN이 아님)", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class), examples = @ExampleObject(name = "권한 없음", value = "{ \"code\": \"C002\", \"message\": \"접근 권한이 없습니다.\" }"))),
64+
@ApiResponse(responseCode = "404", description = "삭제할 리소스를 찾을 수 없음", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class), examples = @ExampleObject(name = "조회 결과 없음", value = "{ \"code\": \"C010\", \"message\": \"해당 리소스를 찾을 수 없습니다.\" }"))),
65+
@ApiResponse(responseCode = "500", description = "서버 내부 오류", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class), examples = @ExampleObject(name = "서버 문제 발생", value = "{ \"code\": \"C009\", \"message\": \"서버에 문제가 발생하였습니다.\" }")))
66+
})
67+
@DeleteMapping("/{activityId}")
68+
public ResponseEntity<Void> deleteActivity(@PathVariable Long activityId) {
69+
activityService.deleteActivity(activityId);
70+
return ResponseEntity.noContent().build();
71+
}
72+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package dmu.dasom.api.domain.activity.dto;
2+
3+
import dmu.dasom.api.domain.activity.entity.Activity;
4+
import io.swagger.v3.oas.annotations.media.Schema;
5+
import lombok.Builder;
6+
import lombok.Getter;
7+
import java.time.format.DateTimeFormatter;
8+
9+
@Getter
10+
@Builder
11+
@Schema(name = "ActivityItemDto", description = "개별 활동 목록")
12+
public class ActivityItemDto {
13+
14+
@Schema(description = "활동 고유 ID", example = "1")
15+
private final Long id;
16+
17+
@Schema(description = "활동 날짜", example = "05.10")
18+
private final String monthDay; // 날짜 필드 추가
19+
20+
@Schema(description = "활동 제목", example = "컴퓨터 공학부 경진대회")
21+
private final String title;
22+
23+
@Schema(description = "수상 내역", example = "최우수상")
24+
private final String award;
25+
26+
public static ActivityItemDto of(Activity activity) {
27+
String formattedMonthDay = activity.getActivityDate()
28+
.format(DateTimeFormatter.ofPattern("MM.dd"));
29+
30+
return ActivityItemDto.builder()
31+
.id(activity.getId())
32+
.monthDay(formattedMonthDay)
33+
.title(activity.getTitle())
34+
.award(activity.getAward())
35+
.build();
36+
}
37+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package dmu.dasom.api.domain.activity.dto;
2+
3+
import dmu.dasom.api.domain.activity.entity.Activity;
4+
import dmu.dasom.api.domain.activity.entity.Section;
5+
import io.swagger.v3.oas.annotations.media.Schema;
6+
import jakarta.validation.constraints.NotBlank;
7+
import jakarta.validation.constraints.NotNull;
8+
import jakarta.validation.constraints.Size;
9+
import lombok.Builder;
10+
import lombok.Getter;
11+
12+
import java.time.LocalDate;
13+
14+
@Getter
15+
@Builder
16+
@Schema(name = "ActivityHistoryRequestDto", description = "활동 연혁 생성/수정 요청 DTO")
17+
public class ActivityRequestDto {
18+
19+
@NotNull(message = "활동 날짜는 필수입니다.")
20+
@Schema(description = "활동 날짜", example = "2024-08-21")
21+
private LocalDate activityDate;
22+
23+
@Schema(description = "활동 섹션", example = "교내 경진대회", maxLength = 50)
24+
private String section;
25+
26+
@NotBlank(message = "활동 제목은 필수입니다.")
27+
@Size(max = 50, message = "제목은 50자 이내로 입력해주세요.")
28+
@Schema(description = "활동 제목", example = "컴퓨터 공학부 경진대회", maxLength = 50)
29+
private String title;
30+
31+
@Size(max = 50, message = "수상 내역은 50자 이내로 입력해주세요.")
32+
@Schema(description = "수상 내역 (선택 사항)", example = "최우수상", maxLength = 50)
33+
private String award;
34+
35+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package dmu.dasom.api.domain.activity.dto;
2+
3+
import dmu.dasom.api.domain.activity.entity.Activity;
4+
import dmu.dasom.api.domain.activity.entity.Section;
5+
import lombok.Builder;
6+
import lombok.Getter;
7+
8+
import java.util.Comparator;
9+
import java.util.List;
10+
import java.util.Map;
11+
import java.util.stream.Collectors;
12+
13+
@Getter
14+
@Builder
15+
public class ActivityResponseDto {
16+
17+
private final int year;
18+
private final List<SectionItemDto> sections;
19+
20+
public static List<ActivityResponseDto> of(List<Activity> activities) {
21+
return activities.stream()
22+
.collect(Collectors.groupingBy(activity -> activity.getActivityDate().getYear()))
23+
.entrySet().stream()
24+
.sorted(Map.Entry.comparingByKey()) // 1. 연도 오름차순 정렬
25+
.map(entryByYear -> {
26+
List<SectionItemDto> sectionDtos = groupAndSortSections(entryByYear.getValue());
27+
return ActivityResponseDto.builder()
28+
.year(entryByYear.getKey())
29+
.sections(sectionDtos)
30+
.build();
31+
})
32+
.collect(Collectors.toList());
33+
}
34+
35+
private static List<SectionItemDto> groupAndSortSections(List<Activity> activitiesForYear) {
36+
return activitiesForYear.stream()
37+
.collect(Collectors.groupingBy(Activity::getSection))
38+
.entrySet().stream()
39+
.map(entryBySection -> {
40+
Section section = entryBySection.getKey();
41+
List<ActivityItemDto> activityDtos = mapAndSortActivities(entryBySection.getValue());
42+
return SectionItemDto.builder()
43+
.id(section.getId())
44+
.section(section.getName())
45+
.activities(activityDtos)
46+
.build();
47+
})
48+
// 2. 섹션 ID 오름차순 정렬
49+
.sorted(Comparator.comparing(SectionItemDto::getId))
50+
.collect(Collectors.toList());
51+
}
52+
53+
private static List<ActivityItemDto> mapAndSortActivities(List<Activity> activitiesForSection) {
54+
return activitiesForSection.stream()
55+
// 3. 활동 날짜 오름차순 정렬
56+
.sorted(Comparator.comparing(Activity::getActivityDate))
57+
.map(ActivityItemDto::of)
58+
.collect(Collectors.toList());
59+
}
60+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package dmu.dasom.api.domain.activity.dto;
2+
3+
import dmu.dasom.api.domain.activity.entity.Section;
4+
import io.swagger.v3.oas.annotations.media.Schema;
5+
import lombok.Builder;
6+
import lombok.Getter;
7+
8+
import java.util.List;
9+
10+
@Getter
11+
@Builder
12+
@Schema(name = "SectionItemDto", description = "섹션별 활동 목록")
13+
public class SectionItemDto {
14+
15+
@Schema(description = "활동 섹션 고유 ID", example = "1")
16+
private final Long id;
17+
18+
@Schema(description = "활동 섹션", example = "교내 경진대회")
19+
private final String section;
20+
21+
@Schema(description = "활동 목록")
22+
private final List<ActivityItemDto> activities;
23+
24+
public static SectionItemDto of(Section section, List<ActivityItemDto> activities) {
25+
return SectionItemDto.builder()
26+
.id(section.getId())
27+
.section(section.getName())
28+
.activities(activities)
29+
.build();
30+
}
31+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package dmu.dasom.api.domain.activity.entity;
2+
3+
import jakarta.persistence.*;
4+
import lombok.*;
5+
import java.time.LocalDate;
6+
7+
@Entity
8+
@Getter
9+
@Builder
10+
@AllArgsConstructor
11+
@NoArgsConstructor(access = AccessLevel.PROTECTED)
12+
@Table(name = "activities")
13+
public class Activity {
14+
15+
@Id
16+
@GeneratedValue(strategy = GenerationType.IDENTITY)
17+
private Long id;
18+
19+
@ManyToOne(fetch = FetchType.LAZY)
20+
@JoinColumn(name = "section_id", nullable = false)
21+
private Section section;
22+
23+
@Column(nullable = false)
24+
private LocalDate activityDate;
25+
26+
@Column(length = 50, nullable = false)
27+
private String title;
28+
29+
@Column(length = 50)
30+
private String award;
31+
32+
public static Activity create(Section section, LocalDate activityDate, String title, String award) {
33+
return Activity.builder()
34+
.section(section)
35+
.activityDate(activityDate)
36+
.title(title)
37+
.award(award)
38+
.build();
39+
}
40+
41+
public void update(Section section, LocalDate activityDate, String title, String award) {
42+
this.section = section;
43+
this.activityDate = activityDate;
44+
this.title = title;
45+
this.award = award;
46+
}
47+
}

0 commit comments

Comments
 (0)