Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ dependencies {
// Data Layer: JPA, Redis, Database Drivers
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'org.springframework.boot:spring-boot-starter-validation'
runtimeOnly 'com.h2database:h2'
runtimeOnly 'com.mysql:mysql-connector-j'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
import com.somemore.global.exception.ImageUploadException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ProblemDetail;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
public class GlobalExceptionHandler {

//예시 코드
@ExceptionHandler(BadRequestException.class)
Expand Down Expand Up @@ -45,4 +45,15 @@ ProblemDetail handleDuplicateException(final DuplicateException e) {
return problemDetail;
}

@ExceptionHandler(MethodArgumentNotValidException.class)
ProblemDetail handleMethodArgumentNotValid(final MethodArgumentNotValidException e) {

ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(HttpStatus.BAD_REQUEST, e.getMessage());

problemDetail.setTitle("유효성 예외");
problemDetail.setDetail("입력 데이터 유효성 검사가 실패했습니다. 각 필드를 확인해주세요.");

return problemDetail;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.somemore.volunteer.controller;

import com.somemore.global.common.response.ApiResponse;
import com.somemore.imageupload.dto.ImageUploadRequestDto;
import com.somemore.imageupload.usecase.ImageUploadUseCase;
import com.somemore.volunteer.dto.request.VolunteerProfileUpdateRequestDto;
import com.somemore.volunteer.usecase.UpdateVolunteerProfileUseCase;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.util.UUID;

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

@RestController
@Slf4j
@RequiredArgsConstructor
@RequestMapping("/api/profile")
@Tag(name = "PUT Volunteer", description = "봉사자 프로필 수정")
public class VolunteerProfileCommandController {

private final UpdateVolunteerProfileUseCase updateVolunteerProfileUseCase;
private final ImageUploadUseCase imageUploadUseCase;

@Secured("ROLE_VOLUNTEER")
@Operation(summary = "프로필 수정", description = "현재 로그인된 사용자의 프로필을 수정합니다.")
@PutMapping(consumes = MULTIPART_FORM_DATA_VALUE)
public ApiResponse<String> updateProfile(
@AuthenticationPrincipal String volunteerId,
@Valid @RequestPart("data") VolunteerProfileUpdateRequestDto requestDto,
@RequestPart(value = "img_file", required = false) MultipartFile image) {

String imgUrl = imageUploadUseCase.uploadImage(new ImageUploadRequestDto(image));

updateVolunteerProfileUseCase.update(
UUID.fromString(volunteerId),
requestDto,
imgUrl
);

return ApiResponse.ok("프로필 수정 성공");
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.somemore.volunteer.controller;

import com.somemore.global.common.response.ApiResponse;
import com.somemore.volunteer.dto.response.VolunteerResponseDto;
import com.somemore.volunteer.dto.response.VolunteerProfileResponseDto;
import com.somemore.volunteer.usecase.VolunteerQueryUseCase;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
Expand All @@ -21,14 +21,14 @@
@RequiredArgsConstructor
@RequestMapping("/api/profile")
@Tag(name = "GET Volunteer", description = "봉사자 조회")
public class VolunteerQueryController {
public class VolunteerProfileQueryController {

private final VolunteerQueryUseCase volunteerQueryUseCase;

@Operation(summary = "본인 상세 프로필 조회", description = "현재 로그인된 사용자의 상세 프로필을 조회합니다.")
@Secured("ROLE_VOLUNTEER")
@GetMapping("/me")
public ApiResponse<VolunteerResponseDto> getMyProfile(
public ApiResponse<VolunteerProfileResponseDto> getMyProfile(
@AuthenticationPrincipal String volunteerId) {

return ApiResponse.ok(
Expand All @@ -39,7 +39,7 @@ public ApiResponse<VolunteerResponseDto> getMyProfile(

@GetMapping("/{volunteerId}")
@Operation(summary = "타인 프로필 조회", description = "특정 봉사자의 프로필을 조회합니다. 상세 정보는 포함되지 않습니다.")
public ApiResponse<VolunteerResponseDto> getVolunteerProfile(
public ApiResponse<VolunteerProfileResponseDto> getVolunteerProfile(
@PathVariable UUID volunteerId) {

return ApiResponse.ok(
Expand All @@ -52,7 +52,7 @@ public ApiResponse<VolunteerResponseDto> getVolunteerProfile(
@GetMapping("/{volunteerId}/detailed")
@Secured("ROLE_CENTER")
@Operation(summary = "지원자 상세 프로필 조회", description = "기관이 작성한 모집 글에 지원한 봉사자의 상세 프로필을 조회합니다.")
public ApiResponse<VolunteerResponseDto> getVolunteerDetailedProfile(
public ApiResponse<VolunteerProfileResponseDto> getVolunteerDetailedProfile(
@PathVariable UUID volunteerId,
@AuthenticationPrincipal String centerId) {

Expand Down
7 changes: 7 additions & 0 deletions src/main/java/com/somemore/volunteer/domain/Volunteer.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.somemore.auth.oauth.OAuthProvider;
import com.somemore.global.common.BaseEntity;
import com.somemore.volunteer.dto.request.VolunteerProfileUpdateRequestDto;
import jakarta.persistence.*;
import lombok.*;

Expand Down Expand Up @@ -58,6 +59,12 @@ public static Volunteer createDefault(OAuthProvider oauthProvider, String oauthI
.build();
}

public void updateWith(VolunteerProfileUpdateRequestDto dto, String imgUrl) {
this.nickname = dto.nickname();
this.introduce = dto.introduce();
this.imgUrl = imgUrl;
}

@Builder
private Volunteer(
OAuthProvider oauthProvider,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.somemore.volunteer.dto.request;

import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Builder;

@Builder
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public record VolunteerProfileUpdateRequestDto(

@Schema(description = "봉사자 닉네임", example = "making")
@NotBlank(message = "닉네임은 필수 값입니다.")
@Size(max = 10, message = "닉네임은 최대 10자까지 입력 가능합니다.")
String nickname,

@Schema(description = "봉사자 소개글", example = "저는 다양한 봉사활동에 관심이 많은 봉사자입니다.")
@NotBlank(message = "소개글은 필수 값입니다.")
@Size(max = 100, message = "소개글은 최대 100자까지 입력 가능합니다.")
String introduce
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
import io.swagger.v3.oas.annotations.media.Schema;

@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
@Schema(description = "봉사자 응답 DTO")
public record VolunteerResponseDto(
@Schema(description = "봉사자 프로필 응답 DTO")
public record VolunteerProfileResponseDto(
@Schema(description = "봉사자 ID", example = "123e4567-e89b-12d3-a456-426614174000")
String volunteerId,

Expand All @@ -30,30 +30,30 @@ public record VolunteerResponseDto(
@Schema(description = "총 봉사 횟수", example = "20")
Integer totalVolunteerCount,

@Schema(description = "봉사자 상세 정보", implementation = VolunteerDetailResponseDto.class)
VolunteerDetailResponseDto volunteerDetailResponseDto
@Schema(description = "봉사자 상세 정보", implementation = Detail.class)
Detail detail
) {

public static VolunteerResponseDto from(
public static VolunteerProfileResponseDto from(
Volunteer volunteer,
VolunteerDetail volunteerDetail
) {
return new VolunteerResponseDto(
return new VolunteerProfileResponseDto(
volunteer.getId().toString(),
volunteer.getNickname(),
volunteer.getImgUrl(),
volunteer.getIntroduce(),
volunteer.getTier().name(),
volunteer.getTotalVolunteerHours(),
volunteer.getTotalVolunteerCount(),
VolunteerDetailResponseDto.from(volunteerDetail)
Detail.from(volunteerDetail)
);
}

public static VolunteerResponseDto from(
public static VolunteerProfileResponseDto from(
Volunteer volunteer
) {
return new VolunteerResponseDto(
return new VolunteerProfileResponseDto(
volunteer.getId().toString(),
volunteer.getNickname(),
volunteer.getImgUrl(),
Expand All @@ -66,8 +66,8 @@ public static VolunteerResponseDto from(
}

@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
@Schema(description = "봉사자 상세 응답 DTO")
private record VolunteerDetailResponseDto(
@Schema(description = "봉사자 상세 프로필")
private record Detail(
@Schema(description = "이름", example = "홍길동")
String name,

Expand All @@ -83,10 +83,10 @@ private record VolunteerDetailResponseDto(
@Schema(description = "연락처", example = "010-1234-5678")
String contactNumber
) {
public static VolunteerDetailResponseDto from(
public static Detail from(
VolunteerDetail volunteerDetail
) {
return new VolunteerDetailResponseDto(
return new Detail(
volunteerDetail.getName(),
volunteerDetail.getEmail(),
volunteerDetail.getGender().name(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.somemore.volunteer.service;

import com.somemore.global.exception.BadRequestException;
import com.somemore.volunteer.domain.Volunteer;
import com.somemore.volunteer.dto.request.VolunteerProfileUpdateRequestDto;
import com.somemore.volunteer.repository.VolunteerRepository;
import com.somemore.volunteer.usecase.UpdateVolunteerProfileUseCase;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.UUID;

import static com.somemore.global.exception.ExceptionMessage.NOT_EXISTS_VOLUNTEER;

@Slf4j
@Service
@RequiredArgsConstructor
@Transactional
public class UpdateVolunteerProfileService implements UpdateVolunteerProfileUseCase {

private final VolunteerRepository volunteerRepository;

@Override
public void update(UUID volunteerId, VolunteerProfileUpdateRequestDto requestDto, String imgUrl) {
Volunteer volunteer = volunteerRepository.findById(volunteerId)
.orElseThrow(() -> new BadRequestException(NOT_EXISTS_VOLUNTEER));

volunteer.updateWith(requestDto, imgUrl);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import com.somemore.global.exception.BadRequestException;
import com.somemore.volunteer.domain.Volunteer;
import com.somemore.volunteer.domain.VolunteerDetail;
import com.somemore.volunteer.dto.response.VolunteerResponseDto;
import com.somemore.volunteer.dto.response.VolunteerProfileResponseDto;
import com.somemore.volunteer.repository.VolunteerDetailRepository;
import com.somemore.volunteer.repository.VolunteerRepository;
import com.somemore.volunteer.usecase.VolunteerQueryUseCase;
Expand All @@ -28,27 +28,27 @@ public class VolunteerQueryService implements VolunteerQueryUseCase {
private final VolunteerDetailAccessValidator volunteerDetailAccessValidator;

@Override
public VolunteerResponseDto getMyProfile(UUID volunteerId) {
public VolunteerProfileResponseDto getMyProfile(UUID volunteerId) {

return VolunteerResponseDto.from(
return VolunteerProfileResponseDto.from(
findVolunteer(volunteerId),
findVolunteerDetail(volunteerId)
);
}

@Override
public VolunteerResponseDto getVolunteerProfile(UUID volunteerId) {
public VolunteerProfileResponseDto getVolunteerProfile(UUID volunteerId) {

return VolunteerResponseDto.from(
return VolunteerProfileResponseDto.from(
findVolunteer(volunteerId)
);
}

@Override
public VolunteerResponseDto getVolunteerDetailedProfile(UUID volunteerId, UUID centerId) {
public VolunteerProfileResponseDto getVolunteerDetailedProfile(UUID volunteerId, UUID centerId) {
volunteerDetailAccessValidator.validateByCenterId(centerId, volunteerId);

return VolunteerResponseDto.from(
return VolunteerProfileResponseDto.from(
findVolunteer(volunteerId),
findVolunteerDetail(volunteerId)
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.somemore.volunteer.usecase;

import com.somemore.volunteer.dto.request.VolunteerProfileUpdateRequestDto;

import java.util.UUID;

public interface UpdateVolunteerProfileUseCase {

void update(UUID volunteerId, VolunteerProfileUpdateRequestDto requestDto, String imgUrl);
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package com.somemore.volunteer.usecase;

import com.somemore.volunteer.dto.response.VolunteerResponseDto;
import com.somemore.volunteer.dto.response.VolunteerProfileResponseDto;

import java.util.UUID;

public interface VolunteerQueryUseCase {

VolunteerResponseDto getMyProfile(UUID volunteerId);
VolunteerProfileResponseDto getMyProfile(UUID volunteerId);

VolunteerResponseDto getVolunteerProfile(UUID volunteerId);
VolunteerProfileResponseDto getVolunteerProfile(UUID volunteerId);

VolunteerResponseDto getVolunteerDetailedProfile(UUID volunteerId, UUID centerId);
VolunteerProfileResponseDto getVolunteerDetailedProfile(UUID volunteerId, UUID centerId);

UUID getVolunteerIdByOAuthId(String oAuthId);

Expand Down
Loading