Skip to content
14 changes: 14 additions & 0 deletions src/main/java/com/somemore/auth/annotation/CurrentUser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.somemore.auth.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.security.core.annotation.AuthenticationPrincipal;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null :T(java.util.UUID).fromString(#this)")
public @interface CurrentUser {

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.somemore.center.controller;

import com.somemore.auth.signout.usecase.SignOutUseCase;
import com.somemore.center.dto.request.CenterSignRequestDto;
import com.somemore.global.common.response.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
Expand All @@ -9,17 +10,30 @@
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Slf4j
@RequiredArgsConstructor
@RequestMapping("/api/center")
@Tag(name = "center Sign API", description = "기관 로그아웃")
@Tag(name = "center Sign API", description = "기관 로그인, 로그아웃")
public class CenterSignController {

private final SignOutUseCase signOutUseCase;

/*
* 기관 로그인 엔드포인트
* 실제 로그인 절차는 필터에서 처리됩니다.
* 이 엔드포인트는 로그인 요청을 받아 필터에 의한 인증 절차를 수행합니다.
*/
@PostMapping("/sign-in")
public ApiResponse<String> signIn(
@RequestParam CenterSignRequestDto requestDto
) {
return ApiResponse.ok("로그인되었습니다.");
}

@PostMapping("/sign-out")
public ApiResponse<String> signOut(
HttpServletResponse response,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
package com.somemore.center.controller;

import com.somemore.auth.annotation.CurrentUser;
import com.somemore.center.dto.request.PreferItemCreateRequestDto;
import com.somemore.center.dto.response.PreferItemCreateResponseDto;
import com.somemore.center.usecase.command.CreatePreferItemUseCase;
import com.somemore.global.common.response.ApiResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
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 java.util.UUID;

@RequiredArgsConstructor
@RestController
@RequestMapping("/api/preferItem")
Expand All @@ -28,10 +27,12 @@ public class PreferItemCommandApiController {
@Secured("ROLE_CENTER")
@Operation(summary = "기관 선호물품 등록 API")
@PostMapping()
public ApiResponse<PreferItemCreateResponseDto> registerPreferItem(@Valid @RequestBody PreferItemCreateRequestDto requestDto,
@AuthenticationPrincipal String userId) {
public ApiResponse<PreferItemCreateResponseDto> registerPreferItem(
@Valid @RequestBody PreferItemCreateRequestDto requestDto,
@CurrentUser UUID userId) {

PreferItemCreateResponseDto responseDto = createPreferItemUseCase.createPreferItem(UUID.fromString(userId), requestDto);
PreferItemCreateResponseDto responseDto = createPreferItemUseCase.createPreferItem(userId,
requestDto);

return ApiResponse.ok(200, responseDto, "관심 기관 등록 성공");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.somemore.center.dto.request;

import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;

@JsonNaming(SnakeCaseStrategy.class)
@Builder
public record CenterSignRequestDto(
@Schema(description = "기관 아이디", example = "somemore")
String accountId,
@Schema(description = "기관 패스워드", example = "password1234")
String accountPassword
) {

}
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
package com.somemore.global.common.response;

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

@NoArgsConstructor
@Getter
public class ApiResponse<T> {

@Schema(description = "응답 상태 코드. 성공 시 200, 실패 시 오류 코드", example = "200")
private int code;
@Schema(description = "응답에 대한 메시지. 요청 성공/실패에 대한 설명", example = "요청 성공")
private String message;
@Schema(description = "API 요청 처리 결과로 반환되는 데이터", example = "{ name : 손모아 }")
private T data;

public static <T> ApiResponse<T> ok(int status, T data, String message) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
package com.somemore.interestcenter.controller;

import com.somemore.auth.annotation.CurrentUser;
import com.somemore.global.common.response.ApiResponse;
import com.somemore.interestcenter.dto.response.InterestCentersResponseDto;
import com.somemore.interestcenter.usecase.InterestCenterQueryUseCase;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.List;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.UUID;

@RequiredArgsConstructor
@RestController
@Tag(name = "Interest Center Query API", description = "관심 기관의 조회 API를 제공합니다")
Expand All @@ -22,9 +21,11 @@ public class InterestCenterQueryApiController {

@Operation(summary = "관심기관 목록 조회 API")
@GetMapping("/api/interest-centers")
public ApiResponse<List<InterestCentersResponseDto>> getInterestCenters(@AuthenticationPrincipal String volunteerId) {
public ApiResponse<List<InterestCentersResponseDto>> getInterestCenters(
@CurrentUser UUID volunteerId) {

List<InterestCentersResponseDto> responseDtos = interestCenterQueryUseCase.getInterestCenters(UUID.fromString(volunteerId));
List<InterestCentersResponseDto> responseDtos = interestCenterQueryUseCase.getInterestCenters(
volunteerId);

return ApiResponse.ok(200, responseDtos, "관심기관 조회 성공");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE;

import com.somemore.auth.annotation.CurrentUser;
import com.somemore.global.common.response.ApiResponse;
import com.somemore.imageupload.dto.ImageUploadRequestDto;
import com.somemore.imageupload.usecase.ImageUploadUseCase;
Expand All @@ -20,7 +21,6 @@
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
Expand All @@ -47,16 +47,15 @@ public class RecruitBoardCommandApiController {
@Operation(summary = "봉사 활동 모집글 등록", description = "봉사 활동 모집글을 등록합니다.")
@PostMapping(value = "/recruit-board", consumes = MULTIPART_FORM_DATA_VALUE)
public ApiResponse<Long> createRecruitBoard(
@AuthenticationPrincipal String userId,
@CurrentUser UUID userId,
@Valid @RequestPart("data") RecruitBoardCreateRequestDto requestDto,
@RequestPart(value = "img_file", required = false) MultipartFile image
) {

String imgUrl = imageUploadUseCase.uploadImage(new ImageUploadRequestDto(image));
return ApiResponse.ok(
201,
createRecruitBoardUseCase.createRecruitBoard(requestDto, getCenterId(userId),
imgUrl),
createRecruitBoardUseCase.createRecruitBoard(requestDto, userId, imgUrl),
"봉사 활동 모집글 등록 성공"
);
}
Expand All @@ -65,13 +64,13 @@ public ApiResponse<Long> createRecruitBoard(
@Operation(summary = "봉사 활동 모집글 수정", description = "봉사 활동 모집글을 수정합니다.")
@PutMapping(value = "/recruit-board/{id}", consumes = MULTIPART_FORM_DATA_VALUE)
public ApiResponse<String> updateRecruitBoard(
@AuthenticationPrincipal String userId,
@CurrentUser UUID userId,
@PathVariable Long id,
@Valid @RequestPart("data") RecruitBoardUpdateRequestDto requestDto,
@RequestPart(value = "img_file", required = false) MultipartFile image
) {
String imgUrl = imageUploadUseCase.uploadImage(new ImageUploadRequestDto(image));
updateRecruitBoardUseCase.updateRecruitBoard(requestDto, id, getCenterId(userId), imgUrl);
updateRecruitBoardUseCase.updateRecruitBoard(requestDto, id, userId, imgUrl);

return ApiResponse.ok("봉사 활동 모집글 수정 성공");
}
Expand All @@ -80,27 +79,25 @@ public ApiResponse<String> updateRecruitBoard(
@Operation(summary = "봉사 활동 모집글 위치 수정", description = "봉사 활동 모집글의 위치를 수정합니다.")
@PutMapping(value = "/recruit-board/{id}/location")
public ApiResponse<String> updateRecruitBoardLocation(
@AuthenticationPrincipal String userId,
@CurrentUser UUID userId,
@PathVariable Long id,
@Valid @RequestBody RecruitBoardLocationUpdateRequestDto requestDto
) {

updateRecruitBoardUseCase.updateRecruitBoardLocation(requestDto, id, getCenterId(userId));
updateRecruitBoardUseCase.updateRecruitBoardLocation(requestDto, id, userId);
return ApiResponse.ok("봉사 활동 모집글 위치 수정 성공");
}

@Secured("ROLE_CENTER")
@Operation(summary = "봉사 활동 모집글 상태 수정", description = "봉사 활동 모집글의 상태를 수정합니다.")
@PatchMapping(value = "/recruit-board/{id}")
public ApiResponse<String> updateRecruitBoardStatus(
@AuthenticationPrincipal String userId,
@CurrentUser UUID userId,
@PathVariable Long id,
@RequestBody RecruitBoardStatusUpdateRequestDto requestDto
) {
LocalDateTime now = LocalDateTime.now();
updateRecruitBoardUseCase.updateRecruitBoardStatus(requestDto.status(), id,
getCenterId(userId),
now);
updateRecruitBoardUseCase.updateRecruitBoardStatus(requestDto.status(), id, userId, now);

return ApiResponse.ok("봉사 활동 모집글 상태 수정 성공");
}
Expand All @@ -109,15 +106,11 @@ public ApiResponse<String> updateRecruitBoardStatus(
@Operation(summary = "봉사 활동 모집글 삭제", description = "봉사 활동 모집글을 삭제합니다.")
@DeleteMapping(value = "/recruit-board/{id}")
public ApiResponse<String> deleteRecruitBoard(
@AuthenticationPrincipal String userId,
@CurrentUser UUID userId,
@PathVariable Long id
) {
deleteRecruitBoardUseCase.deleteRecruitBoard(getCenterId(userId), id);
deleteRecruitBoardUseCase.deleteRecruitBoard(userId, id);
return ApiResponse.ok("봉사 활동 모집글 삭제 성공");
}

private static UUID getCenterId(String userId) {
return UUID.fromString(userId);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,51 +16,51 @@
@JsonNaming(SnakeCaseStrategy.class)
@Builder
public record RecruitBoardCreateRequestDto(
@Schema(description = "봉사 모집글 제목", example = "서울 청계천 환경 미화 봉사 모집")
@NotBlank(message = "모집글 제목은 필수 값입니다.")
String title,
@Schema(description = "봉사 모집글 내용", example = "서울 청계천 주변 환경 미화 봉사 모집합니다. <br>")
@NotBlank(message = "모집글 내용은 필수 값입니다.")
String content,
@Schema(description = "봉사 지역", example = "서울")
@NotBlank(message = "봉사 지역은 필수 값입니다.")
String region,
@Schema(description = "예상 모집 인원", example = "4")
@NotNull(message = "예상 모집 인원은 필수 값입니다.")
Integer recruitmentCount,
@Schema(description = "봉사 시작 일시", example = "2024-11-20T10:00:00")
@NotNull(message = "봉사 시작 일시는 필수 값입니다.")
LocalDateTime volunteerStartDateTime,
@Schema(description = "봉사 종료 일시", example = "2024-11-20T12:00:00")
@NotNull(message = "봉사 종료 일시는 필수 값입니다.")
LocalDateTime volunteerEndDateTime,
@Schema(description = "봉사 활동 유형", example = "ENVIRONMENTAL_PROTECTION")
@NotNull(message = "봉사 활동 유형은 필수 값입니다.")
VolunteerCategory volunteerCategory,
@Schema(description = "봉사 시간 인정 여부", example = "true")
@NotNull(message = "시간 인정 여부는 필수 값입니다.")
Boolean admitted,
@NotNull(message = "위치 정보는 필수 값입니다.")
LocationCreateRequestDto location
@Schema(description = "봉사 모집글 제목", example = "서울 청계천 환경 미화 봉사 모집")
@NotBlank(message = "모집글 제목은 필수 값입니다.")
String title,
@Schema(description = "봉사 모집글 내용", example = "서울 청계천 주변 환경 미화 봉사 모집합니다. <br>")
@NotBlank(message = "모집글 내용은 필수 값입니다.")
String content,
@Schema(description = "봉사 지역", example = "서울")
@NotBlank(message = "봉사 지역은 필수 값입니다.")
String region,
@Schema(description = "예상 모집 인원", example = "4")
@NotNull(message = "예상 모집 인원은 필수 값입니다.")
Integer recruitmentCount,
@Schema(description = "봉사 시작 일시", example = "2024-11-20T10:00:00", type = "string")
@NotNull(message = "봉사 시작 일시는 필수 값입니다.")
LocalDateTime volunteerStartDateTime,
@Schema(description = "봉사 종료 일시", example = "2024-11-20T12:00:00", type = "string")
@NotNull(message = "봉사 종료 일시는 필수 값입니다.")
LocalDateTime volunteerEndDateTime,
@Schema(description = "봉사 활동 유형", example = "ENVIRONMENTAL_PROTECTION")
@NotNull(message = "봉사 활동 유형은 필수 값입니다.")
VolunteerCategory volunteerCategory,
@Schema(description = "봉사 시간 인정 여부", example = "true")
@NotNull(message = "시간 인정 여부는 필수 값입니다.")
Boolean admitted,
@NotNull(message = "위치 정보는 필수 값입니다.")
LocationCreateRequestDto location
) {

public RecruitBoard toEntity(UUID centerId, Long locationId, String imgUrl) {
RecruitmentInfo recruitmentInfo = RecruitmentInfo.builder()
.region(region)
.recruitmentCount(recruitmentCount)
.volunteerStartDateTime(volunteerStartDateTime)
.volunteerEndDateTime(volunteerEndDateTime)
.volunteerCategory(volunteerCategory)
.admitted(admitted)
.build();
.region(region)
.recruitmentCount(recruitmentCount)
.volunteerStartDateTime(volunteerStartDateTime)
.volunteerEndDateTime(volunteerEndDateTime)
.volunteerCategory(volunteerCategory)
.admitted(admitted)
.build();

return RecruitBoard.builder()
.centerId(centerId)
.locationId(locationId)
.title(title)
.content(content)
.imgUrl(imgUrl)
.recruitmentInfo(recruitmentInfo)
.build();
.centerId(centerId)
.locationId(locationId)
.title(title)
.content(content)
.imgUrl(imgUrl)
.recruitmentInfo(recruitmentInfo)
.build();
}
}
Loading
Loading