Skip to content

Commit d14ac26

Browse files
authored
Merge branch 'dev' into feat/#38-1
2 parents 99fa912 + 6c6e3f9 commit d14ac26

File tree

20 files changed

+917
-49
lines changed

20 files changed

+917
-49
lines changed

src/main/java/dmu/dasom/api/domain/common/exception/ErrorCode.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ public enum ErrorCode {
2121
EMPTY_RESULT(400, "C012", "조회 결과가 없습니다."),
2222
DUPLICATED_STUDENT_NO(400, "C013", "이미 등록된 학번입니다."),
2323
SEND_EMAIL_FAIL(400, "C014", "이메일 전송에 실패하였습니다."),
24-
MAIL_TYPE_NOT_VALID(400, "C015", "메일 타입이 올바르지 않습니다.")
24+
MAIL_TYPE_NOT_VALID(400, "C015", "메일 타입이 올바르지 않습니다."),
25+
INVALID_DATETIME_FORMAT(400, "C016", "날짜 형식이 올바르지 않습니다."),
26+
INVALID_TIME_FORMAT(400, "C017", "시간 형식이 올바르지 않습니다.")
2527
;
2628

2729
private final int status;

src/main/java/dmu/dasom/api/domain/email/service/EmailService.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public void sendEmail(String to, String name, MailType mailType) throws Messagin
3030
String subject;
3131
String templateName = switch (mailType) {
3232
case DOCUMENT_RESULT -> {
33+
3334
subject = "동양미래대학교 컴퓨터소프트웨어공학과 전공 동아리 DASOM 서류 결과 안내";
3435
yield "document-pass-template";
3536
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package dmu.dasom.api.domain.news.controller;
2+
3+
import dmu.dasom.api.domain.news.dto.NewsRequestDto;
4+
import dmu.dasom.api.domain.news.dto.NewsResponseDto;
5+
import dmu.dasom.api.domain.news.service.NewsService;
6+
import io.swagger.v3.oas.annotations.tags.Tag;
7+
import io.swagger.v3.oas.annotations.Operation;
8+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
9+
import io.swagger.v3.oas.annotations.responses.ApiResponses;
10+
import io.swagger.v3.oas.annotations.media.Content;
11+
import io.swagger.v3.oas.annotations.media.ExampleObject;
12+
import org.springframework.http.ResponseEntity;
13+
import org.springframework.web.bind.annotation.*;
14+
15+
import jakarta.validation.Valid;
16+
import java.util.List;
17+
18+
@Tag(name = "NEWS API", description = "다솜소식 API")
19+
@RestController
20+
@RequestMapping("/api/news")
21+
public class NewsController {
22+
23+
private final NewsService newsService;
24+
25+
public NewsController(NewsService newsService) {
26+
this.newsService = newsService;
27+
}
28+
29+
// 전체 조회
30+
@Operation(summary = "소식 조회", description = "리스트로 조회")
31+
@ApiResponse(responseCode = "200", description = "정상 응답",
32+
content = @Content(mediaType = "application/json"))
33+
@GetMapping
34+
public ResponseEntity<List<NewsResponseDto>> getAllNews() {
35+
List<NewsResponseDto> newsList = newsService.getAllNews();
36+
return ResponseEntity.ok(newsList);
37+
}
38+
39+
// 개별 조회
40+
@Operation(summary = "소식 상세 조회", description = "ID로 특정 소식을 조회")
41+
@ApiResponses({
42+
@ApiResponse(responseCode = "200", description = "조회 성공"),
43+
@ApiResponse(responseCode = "404", description = "소식을 찾을 수 없음")
44+
})
45+
@GetMapping("/{id}")
46+
public ResponseEntity<NewsResponseDto> getNewsById(@PathVariable Long id) {
47+
NewsResponseDto responseDto = newsService.getNewsById(id);
48+
return ResponseEntity.ok(responseDto);
49+
}
50+
51+
// 생성
52+
@Operation(summary = "소식 등록", description = "새로운 소식을 등록")
53+
@ApiResponses(value = {
54+
@ApiResponse(responseCode = "201", description = "생성 완료"),
55+
@ApiResponse(responseCode = "400", description = "유효하지 않은 요청 데이터",
56+
content = @Content(mediaType = "application/json",
57+
examples = @ExampleObject(value = "{ \"errorCode\": \"E003\", \"errorMessage\": \"유효하지 않은 입력입니다.\" }")))
58+
})
59+
@PostMapping
60+
public ResponseEntity<NewsResponseDto> createNews(@Valid @RequestBody NewsRequestDto requestDto) {
61+
NewsResponseDto responseDto = newsService.createNews(requestDto);
62+
return ResponseEntity.status(201).body(responseDto);
63+
}
64+
65+
// 수정
66+
@Operation(summary = "소식 수정", description = "ID로 특정 소식을 수정")
67+
@ApiResponses({
68+
@ApiResponse(responseCode = "200", description = "수정 성공"),
69+
@ApiResponse(responseCode = "404", description = "소식을 찾을 수 없음")
70+
})
71+
@PutMapping("/{id}")
72+
public ResponseEntity<NewsResponseDto> updateNews(@PathVariable Long id, @Valid @RequestBody NewsRequestDto requestDto) {
73+
NewsResponseDto updatedNews = newsService.updateNews(id, requestDto);
74+
return ResponseEntity.ok(updatedNews);
75+
}
76+
77+
// 삭제
78+
@Operation(summary = "소식 삭제", description = "ID로 특정 소식을 삭제")
79+
@ApiResponses({
80+
@ApiResponse(responseCode = "204", description = "삭제 성공"),
81+
@ApiResponse(responseCode = "404", description = "소식을 찾을 수 없음")
82+
})
83+
@DeleteMapping("/{id}")
84+
public ResponseEntity<Void> deleteNews(@PathVariable Long id) {
85+
newsService.deleteNews(id);
86+
return ResponseEntity.noContent().build();
87+
}
88+
89+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package dmu.dasom.api.domain.news.dto;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import jakarta.validation.constraints.NotBlank;
5+
import jakarta.validation.constraints.Size;
6+
import lombok.AllArgsConstructor;
7+
import lombok.Getter;
8+
import lombok.NoArgsConstructor;
9+
10+
@Getter
11+
@NoArgsConstructor
12+
@AllArgsConstructor
13+
@Schema(name = "NewsRequestDto", description = "뉴스 생성 요청 DTO")
14+
public class NewsRequestDto {
15+
16+
@NotBlank
17+
@Size(max = 100)
18+
@Schema(description = "뉴스 제목", example = "새로운 뉴스 제목")
19+
private String title;
20+
21+
@NotBlank
22+
@Schema(description = "뉴스 내용", example = "새로운 뉴스 내용")
23+
private String content;
24+
25+
@Size(max = 255)
26+
@Schema(description = "뉴스 이미지 URL", example = "http://example.com/image.jpg", nullable = true)
27+
private String imageUrl;
28+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package dmu.dasom.api.domain.news.dto;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import lombok.Getter;
5+
6+
import java.time.LocalDateTime;
7+
8+
@Getter
9+
@Schema(name = "NewsResponseDto", description = "뉴스 응답 DTO")
10+
public class NewsResponseDto {
11+
12+
@Schema(description = "소식 ID", example = "1")
13+
private Long id;
14+
15+
@Schema(description = "뉴스 제목", example = "제목")
16+
private String title;
17+
18+
@Schema(description = "뉴스 내용", example = "내용")
19+
private String content;
20+
21+
@Schema(description = "작성일", example = "2025-02-14T12:00:00")
22+
private LocalDateTime createdAt;
23+
24+
@Schema(description = "뉴스 이미지 URL", example = "http://example.com/image.jpg", nullable = true)
25+
private String imageUrl;
26+
27+
public NewsResponseDto(Long id, String title, String content, LocalDateTime createdAt, String imageUrl) {
28+
this.id = id;
29+
this.title = title;
30+
this.content = content;
31+
this.createdAt = createdAt;
32+
this.imageUrl = imageUrl;
33+
}
34+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package dmu.dasom.api.domain.news.entity;
2+
3+
import dmu.dasom.api.domain.common.BaseEntity;
4+
import dmu.dasom.api.domain.common.Status;
5+
import dmu.dasom.api.domain.news.dto.NewsResponseDto;
6+
import io.swagger.v3.oas.annotations.media.Schema;
7+
import jakarta.persistence.*;
8+
import jakarta.validation.constraints.NotBlank;
9+
import jakarta.validation.constraints.NotNull;
10+
import jakarta.validation.constraints.Size;
11+
import lombok.*;
12+
13+
@Getter
14+
@Entity
15+
@Table(name = "news")
16+
@NoArgsConstructor
17+
@AllArgsConstructor
18+
@Builder
19+
@Schema(description = "뉴스 엔티티")
20+
public class NewsEntity extends BaseEntity {
21+
22+
@Id
23+
@GeneratedValue(strategy = GenerationType.IDENTITY)
24+
@Schema(description = "뉴스 ID", example = "1")
25+
private Long id;
26+
27+
@NotNull
28+
@Schema(description = "뉴스 제목", example = "뉴스 예제 제목")
29+
@Column(nullable = false, length = 100)
30+
private String title;
31+
32+
@Lob
33+
@NotNull
34+
@Schema(description = "뉴스 내용", example = "뉴스 예제 내용")
35+
@Column(nullable = false)
36+
private String content;
37+
38+
39+
@Schema(description = "뉴스 이미지 URL", example = "http://example.com/image.jpg")
40+
@Column(length = 255)
41+
private String imageUrl;
42+
43+
// 뉴스 상태 업데이트
44+
public void updateStatus(Status status) {
45+
super.updateStatus(status);
46+
}
47+
48+
// NewsEntity → NewsResponseDto 변환
49+
public NewsResponseDto toResponseDto() {
50+
return new NewsResponseDto(id, title, content, getCreatedAt(), imageUrl);
51+
}
52+
53+
//수정기능
54+
public void update(String title, String content, String imageUrl) {
55+
if (title == null || title.isBlank()) {
56+
throw new IllegalArgumentException("제목은 필수입니다");
57+
}
58+
if (title.length() > 100) {
59+
throw new IllegalArgumentException("제목은 100자까지");
60+
}
61+
if (content == null || content.isBlank()) {
62+
throw new IllegalArgumentException("내용은 필수입니다");
63+
}
64+
if (imageUrl != null && imageUrl.length() > 255) {
65+
throw new IllegalArgumentException("이미지 URL은 255자까지");
66+
}
67+
68+
this.title = title;
69+
this.content = content;
70+
this.imageUrl = imageUrl;
71+
}
72+
73+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package dmu.dasom.api.domain.news.repository;
2+
3+
import dmu.dasom.api.domain.news.entity.NewsEntity;
4+
import org.springframework.data.jpa.repository.JpaRepository;
5+
import org.springframework.stereotype.Repository;
6+
7+
@Repository
8+
public interface NewsRepository extends JpaRepository<NewsEntity, Long> {
9+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package dmu.dasom.api.domain.news.service;
2+
3+
import dmu.dasom.api.domain.news.dto.NewsRequestDto;
4+
import dmu.dasom.api.domain.news.dto.NewsResponseDto;
5+
import dmu.dasom.api.domain.news.entity.NewsEntity;
6+
import dmu.dasom.api.domain.news.repository.NewsRepository;
7+
import org.springframework.stereotype.Service;
8+
import org.springframework.transaction.annotation.Transactional;
9+
import java.util.List;
10+
import java.util.stream.Collectors;
11+
12+
@Service
13+
public class NewsService {
14+
15+
private final NewsRepository newsRepository;
16+
17+
public NewsService(NewsRepository newsRepository) {
18+
this.newsRepository = newsRepository;
19+
}
20+
21+
// 전체 조회
22+
public List<NewsResponseDto> getAllNews() {
23+
return newsRepository.findAll().stream()
24+
.map(NewsEntity::toResponseDto)
25+
.collect(Collectors.toList());
26+
}
27+
28+
// 개별 조회
29+
public NewsResponseDto getNewsById(Long id) {
30+
return newsRepository.findById(id)
31+
.map(NewsEntity::toResponseDto)
32+
.orElseThrow(() -> new IllegalArgumentException("해당 뉴스가 존재하지 않습니다. ID: " + id));
33+
}
34+
35+
// 생성
36+
public NewsResponseDto createNews(NewsRequestDto requestDto) {
37+
NewsEntity news = NewsEntity.builder()
38+
.title(requestDto.getTitle())
39+
.content(requestDto.getContent())
40+
.imageUrl(requestDto.getImageUrl())
41+
.build();
42+
43+
NewsEntity savedNews = newsRepository.save(news);
44+
return savedNews.toResponseDto();
45+
}
46+
47+
// 수정
48+
@Transactional
49+
public NewsResponseDto updateNews(Long id, NewsRequestDto requestDto) {
50+
NewsEntity news = newsRepository.findById(id)
51+
.orElseThrow(() -> new IllegalArgumentException("해당 뉴스가 존재하지 않습니다. ID: " + id));
52+
53+
news.update(requestDto.getTitle(), requestDto.getContent(), requestDto.getImageUrl());
54+
return news.toResponseDto();
55+
}
56+
57+
// 삭제
58+
@Transactional
59+
public void deleteNews(Long id) {
60+
NewsEntity news = newsRepository.findById(id)
61+
.orElseThrow(() -> new IllegalArgumentException("해당 뉴스가 존재하지 않습니다. ID: " + id));
62+
63+
newsRepository.delete(news);
64+
}
65+
}

src/main/java/dmu/dasom/api/domain/recruit/controller/RecruitController.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import dmu.dasom.api.domain.applicant.dto.ApplicantCreateRequestDto;
44
import dmu.dasom.api.domain.applicant.service.ApplicantService;
55
import dmu.dasom.api.domain.common.exception.ErrorResponse;
6+
import dmu.dasom.api.domain.recruit.dto.RecruitConfigResponseDto;
7+
import dmu.dasom.api.domain.recruit.service.RecruitService;
68
import io.swagger.v3.oas.annotations.Operation;
79
import io.swagger.v3.oas.annotations.media.Content;
810
import io.swagger.v3.oas.annotations.media.ExampleObject;
@@ -12,17 +14,17 @@
1214
import jakarta.validation.Valid;
1315
import lombok.RequiredArgsConstructor;
1416
import org.springframework.http.ResponseEntity;
15-
import org.springframework.web.bind.annotation.PostMapping;
16-
import org.springframework.web.bind.annotation.RequestBody;
17-
import org.springframework.web.bind.annotation.RequestMapping;
18-
import org.springframework.web.bind.annotation.RestController;
17+
import org.springframework.web.bind.annotation.*;
18+
19+
import java.util.List;
1920

2021
@RestController
2122
@RequestMapping("/api/recruit")
2223
@RequiredArgsConstructor
2324
public class RecruitController {
2425

2526
private final ApplicantService applicantService;
27+
private final RecruitService recruitService;
2628

2729
// 지원하기
2830
@Operation(summary = "부원 지원하기")
@@ -42,4 +44,12 @@ public ResponseEntity<Void> apply(@Valid @RequestBody final ApplicantCreateReque
4244
return ResponseEntity.ok().build();
4345
}
4446

47+
// 모집 일정 조회
48+
@Operation(summary = "모집 일정 조회")
49+
@ApiResponse(responseCode = "200", description = "모집 일정 조회 성공")
50+
@GetMapping
51+
public ResponseEntity<List<RecruitConfigResponseDto>> getRecruitSchedule() {
52+
return ResponseEntity.ok(recruitService.getRecruitSchedule());
53+
}
54+
4555
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package dmu.dasom.api.domain.recruit.dto;
2+
3+
import dmu.dasom.api.domain.recruit.enums.ConfigKey;
4+
import io.swagger.v3.oas.annotations.media.Schema;
5+
import lombok.Builder;
6+
import lombok.Getter;
7+
8+
@Getter
9+
@Builder
10+
@Schema(name = "RecruitConfigResponseDto", description = "모집 설정 응답 DTO")
11+
public class RecruitConfigResponseDto {
12+
13+
@Schema(description = "모집 설정 키", example = "RECRUITMENT_PERIOD_START")
14+
private ConfigKey key;
15+
16+
@Schema(description = "모집 설정 값 (날짜 혹은 시간 값)", example = "2024-10-01T10:00:00 || 10:00:00")
17+
private String value;
18+
19+
}

0 commit comments

Comments
 (0)