Skip to content

Commit 881c958

Browse files
committed
Merge branch 'feat/#45-1' of https://github.com/DASOM-GitHub/dasom-web-backend into feat/#45-1
2 parents 79fb59c + 83a5012 commit 881c958

File tree

22 files changed

+799
-209
lines changed

22 files changed

+799
-209
lines changed

build.gradle

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,6 @@ dependencies {
5454
implementation 'com.google.apis:google-api-services-sheets:v4-rev516-1.23.0'
5555
implementation 'com.google.auth:google-auth-library-oauth2-http:0.20.0'
5656

57-
58-
59-
60-
6157
implementation 'io.jsonwebtoken:jjwt-api:0.12.6'
6258
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6'
6359
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.6'

src/main/java/dmu/dasom/api/domain/applicant/service/ApplicantServiceImpl.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ public class ApplicantServiceImpl implements ApplicantService {
3838
private final EmailService emailService;
3939
private final GoogleApiService googleApiService;
4040

41+
@Value("${google.spreadsheet.id}")
42+
private String spreadSheetId;
43+
4144
// 지원자 저장
4245
@Override
4346
public void apply(final ApplicantCreateRequestDto request) {
@@ -82,9 +85,20 @@ public ApplicantDetailsResponseDto getApplicant(final Long id) {
8285
@Override
8386
public ApplicantDetailsResponseDto updateApplicantStatus(final Long id, final ApplicantStatusUpdateRequestDto request) {
8487
final Applicant applicant = findById(id);
85-
88+
// 지원자 상태 변경
8689
applicant.updateStatus(request.getStatus());
87-
googleApiService.updateSheet(List.of(applicant));
90+
91+
// Google Sheets에서 학번(Student No)을 기준으로 사용자 존재 여부 확인
92+
int rowIndex = googleApiService.findRowIndexByStudentNo(spreadSheetId, "Sheet1", applicant.getStudentNo());
93+
if (rowIndex == -1) {
94+
// Google Sheets에 사용자 추가
95+
googleApiService.appendToSheet(List.of(applicant));
96+
log.info("지원자가 Google Sheets에 없어서 새로 추가되었습니다: {}", applicant.getStudentNo());
97+
} else {
98+
// Google Sheets에서 사용자 상태 업데이트
99+
googleApiService.updateSheet(List.of(applicant));
100+
log.info("Google Sheets에서 지원자 상태가 업데이트되었습니다: {}", applicant.getStudentNo());
101+
}
88102

89103
return applicant.toApplicantDetailsResponse();
90104
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public enum ErrorCode {
3434
SLOT_FULL(400, "C025", "해당 슬롯이 가득 찼습니다."),
3535
RESERVATION_NOT_FOUND(400, "C026", "예약을 찾을 수 없습니다."),
3636
SLOT_NOT_ACTIVE(400, "C027", "해당 슬롯이 비활성화 되었습니다."),
37+
FILE_ENCODE_FAIL(400, "C028", "파일 인코딩에 실패하였습니다.")
3738
;
3839

3940
private final int status;

src/main/java/dmu/dasom/api/domain/google/service/GoogleApiService.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,13 @@ public void updateSheet(List<Applicant> applicants) {
7878
processSheetsUpdate(applicants, false);
7979
}
8080

81-
private int findRowIndexByStudentNo(String spreadSheetId, String sheetName, String studentNo){
81+
public int findRowIndexByStudentNo(String spreadSheetId, String sheetName, String studentNo){
8282
try {
8383
List<List<Object>> rows = readSheet(spreadSheetId, sheetName + "!A:L"); // A열부터 L열까지 읽기
8484

8585
for (int i = 0; i < rows.size(); i++){
8686
List<Object> row = rows.get(i);
87-
if(!row.isEmpty() && row.get(2).equals(studentNo)){
87+
if(!row.isEmpty() && row.get(2).equals(studentNo)){ // 학번(Student No)이 3번째 열(A=0 기준)
8888
return i + 1;
8989
}
9090
}
@@ -175,4 +175,6 @@ public void processSheetsUpdate(List<Applicant> applicants, boolean isAppend) {
175175
}
176176
}
177177

178+
179+
178180
}
Lines changed: 22 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,55 @@
11
package dmu.dasom.api.domain.news.controller;
22

3-
import dmu.dasom.api.domain.news.dto.NewsRequestDto;
4-
import dmu.dasom.api.domain.news.dto.NewsResponseDto;
3+
import dmu.dasom.api.domain.news.dto.*;
54
import dmu.dasom.api.domain.news.service.NewsService;
65
import io.swagger.v3.oas.annotations.tags.Tag;
76
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;
7+
import jakarta.validation.constraints.Min;
8+
import lombok.RequiredArgsConstructor;
129
import org.springframework.http.ResponseEntity;
1310
import org.springframework.web.bind.annotation.*;
1411

1512
import jakarta.validation.Valid;
1613
import java.util.List;
1714

18-
@Tag(name = "NEWS API", description = "다솜소식 API")
15+
@Tag(name = "NEWS API", description = "뉴스 API")
1916
@RestController
17+
@RequiredArgsConstructor
2018
@RequestMapping("/api/news")
2119
public class NewsController {
2220

2321
private final NewsService newsService;
2422

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"))
23+
@Operation(summary = "전체 뉴스 조회 (썸네일 포함)")
3324
@GetMapping
34-
public ResponseEntity<List<NewsResponseDto>> getAllNews() {
35-
List<NewsResponseDto> newsList = newsService.getAllNews();
36-
return ResponseEntity.ok(newsList);
25+
public ResponseEntity<List<NewsListResponseDto>> getAllNews() {
26+
return ResponseEntity.ok(newsService.getAllNews());
3727
}
3828

39-
// 개별 조회
40-
@Operation(summary = "소식 상세 조회", description = "ID로 특정 소식을 조회")
41-
@ApiResponses({
42-
@ApiResponse(responseCode = "200", description = "조회 성공"),
43-
@ApiResponse(responseCode = "404", description = "소식을 찾을 수 없음")
44-
})
29+
@Operation(summary = "뉴스 상세 조회")
4530
@GetMapping("/{id}")
46-
public ResponseEntity<NewsResponseDto> getNewsById(@PathVariable Long id) {
47-
NewsResponseDto responseDto = newsService.getNewsById(id);
48-
return ResponseEntity.ok(responseDto);
31+
public ResponseEntity<NewsResponseDto> getNewsById(@PathVariable @Min(1) Long id) {
32+
return ResponseEntity.ok(newsService.getNewsById(id));
4933
}
5034

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-
})
35+
@Operation(summary = "뉴스 등록")
5936
@PostMapping
60-
public ResponseEntity<NewsResponseDto> createNews(@Valid @RequestBody NewsRequestDto requestDto) {
61-
NewsResponseDto responseDto = newsService.createNews(requestDto);
62-
return ResponseEntity.status(201).body(responseDto);
37+
public ResponseEntity<NewsCreationResponseDto> createNews(@Valid @RequestBody NewsRequestDto requestDto) {
38+
return ResponseEntity.status(201).body(newsService.createNews(requestDto));
6339
}
6440

65-
// 수정
66-
@Operation(summary = "소식 수정", description = "ID로 특정 소식을 수정")
67-
@ApiResponses({
68-
@ApiResponse(responseCode = "200", description = "수정 성공"),
69-
@ApiResponse(responseCode = "404", description = "소식을 찾을 수 없음")
70-
})
41+
@Operation(summary = "뉴스 수정")
7142
@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);
43+
public ResponseEntity<NewsResponseDto> updateNews(@PathVariable @Min(1) Long id,
44+
@Valid @RequestBody NewsUpdateRequestDto requestDto) {
45+
return ResponseEntity.ok(newsService.updateNews(id, requestDto));
7546
}
7647

77-
// 삭제
78-
@Operation(summary = "소식 삭제", description = "ID로 특정 소식을 삭제")
79-
@ApiResponses({
80-
@ApiResponse(responseCode = "204", description = "삭제 성공"),
81-
@ApiResponse(responseCode = "404", description = "소식을 찾을 수 없음")
82-
})
48+
@Operation(summary = "뉴스 삭제")
8349
@DeleteMapping("/{id}")
8450
public ResponseEntity<Void> deleteNews(@PathVariable Long id) {
8551
newsService.deleteNews(id);
86-
return ResponseEntity.noContent().build();
52+
return ResponseEntity.ok().build();
8753
}
88-
89-
}
54+
55+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package dmu.dasom.api.domain.news.dto;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import jakarta.validation.constraints.NotNull;
5+
import lombok.AllArgsConstructor;
6+
import lombok.Getter;
7+
import lombok.NoArgsConstructor;
8+
9+
@Getter
10+
@NoArgsConstructor
11+
@AllArgsConstructor
12+
@Schema(name = "NewsCreationResponseDto", description = "뉴스 생성 응답 DTO")
13+
public class NewsCreationResponseDto {
14+
15+
@NotNull
16+
@Schema(description = "뉴스 ID", example = "1")
17+
private Long id;
18+
19+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package dmu.dasom.api.domain.news.dto;
2+
3+
import dmu.dasom.api.global.file.dto.FileResponseDto;
4+
import io.swagger.v3.oas.annotations.media.Schema;
5+
import lombok.AllArgsConstructor;
6+
import lombok.Builder;
7+
import lombok.Getter;
8+
import lombok.NoArgsConstructor;
9+
10+
import java.time.LocalDateTime;
11+
12+
@Getter
13+
@NoArgsConstructor
14+
@AllArgsConstructor
15+
@Builder
16+
@Schema(name = "NewsListResponseDto", description = "뉴스 리스트 응답 DTO")
17+
public class NewsListResponseDto {
18+
19+
@Schema(description = "소식 ID", example = "1")
20+
private Long id;
21+
22+
@Schema(description = "뉴스 제목", example = "제목")
23+
private String title;
24+
25+
@Schema(description = "작성일", example = "2025-02-14T12:00:00")
26+
private LocalDateTime createdAt;
27+
28+
@Schema(description = "인코딩된 이미지", example = "asdf", nullable = true)
29+
private FileResponseDto image;
30+
31+
}
Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package dmu.dasom.api.domain.news.dto;
22

3+
import dmu.dasom.api.domain.news.entity.NewsEntity;
34
import io.swagger.v3.oas.annotations.media.Schema;
45
import jakarta.validation.constraints.NotBlank;
56
import jakarta.validation.constraints.Size;
@@ -13,16 +14,20 @@
1314
@Schema(name = "NewsRequestDto", description = "뉴스 생성 요청 DTO")
1415
public class NewsRequestDto {
1516

16-
@NotBlank
17-
@Size(max = 100)
17+
@NotBlank(message = "뉴스 제목은 필수입니다.")
18+
@Size(max = 100, message = "뉴스 제목은 최대 100자입니다.")
1819
@Schema(description = "뉴스 제목", example = "새로운 뉴스 제목")
1920
private String title;
2021

21-
@NotBlank
22+
@NotBlank(message = "뉴스 내용은 필수입니다.")
2223
@Schema(description = "뉴스 내용", example = "새로운 뉴스 내용")
2324
private String content;
2425

25-
@Size(max = 255)
26-
@Schema(description = "뉴스 이미지 URL", example = "http://example.com/image.jpg", nullable = true)
27-
private String imageUrl;
28-
}
26+
public NewsEntity toEntity() {
27+
return NewsEntity.builder()
28+
.title(this.title)
29+
.content(this.content)
30+
.build();
31+
}
32+
33+
}
Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
package dmu.dasom.api.domain.news.dto;
22

3+
import dmu.dasom.api.global.file.dto.FileResponseDto;
34
import io.swagger.v3.oas.annotations.media.Schema;
5+
import lombok.AllArgsConstructor;
6+
import lombok.Builder;
47
import lombok.Getter;
8+
import lombok.NoArgsConstructor;
59

610
import java.time.LocalDateTime;
11+
import java.util.List;
712

813
@Getter
14+
@NoArgsConstructor
15+
@AllArgsConstructor
16+
@Builder
917
@Schema(name = "NewsResponseDto", description = "뉴스 응답 DTO")
1018
public class NewsResponseDto {
1119

@@ -21,14 +29,7 @@ public class NewsResponseDto {
2129
@Schema(description = "작성일", example = "2025-02-14T12:00:00")
2230
private LocalDateTime createdAt;
2331

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-
}
32+
@Schema(description = "인코딩된 이미지", nullable = true)
33+
private List<FileResponseDto> images;
34+
35+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package dmu.dasom.api.domain.news.dto;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import jakarta.validation.constraints.Size;
5+
import lombok.AllArgsConstructor;
6+
import lombok.Getter;
7+
import lombok.NoArgsConstructor;
8+
9+
import java.util.List;
10+
11+
@Getter
12+
@NoArgsConstructor
13+
@AllArgsConstructor
14+
@Schema(name = "NewsUpdateRequestDto", description = "뉴스 수정 요청 DTO")
15+
public class NewsUpdateRequestDto {
16+
17+
@Size(max = 100, message = "뉴스 제목은 최대 100자입니다.")
18+
@Schema(description = "수정할 뉴스 제목", example = "뉴스 제목", nullable = true)
19+
private String title;
20+
21+
@Schema(description = "수정할 뉴스 내용", example = "뉴스 내용", nullable = true)
22+
private String content;
23+
24+
@Schema(description = "삭제할 이미지 ID 목록", example = "[1, 2, 3]", nullable = true)
25+
private List<Long> deleteImageIds;
26+
27+
}

0 commit comments

Comments
 (0)