diff --git a/build.gradle b/build.gradle index 12cdf5c63..b7c572fde 100644 --- a/build.gradle +++ b/build.gradle @@ -158,7 +158,8 @@ private excludedClassFilesForReport(classDirectories) { "**/*Config*", "**/*Response*", "**/*Request*", - "**/exception/**" + "**/exception/**", + "**/Mock**" ]) }) ) diff --git a/src/docs/asciidoc/index.adoc b/src/docs/asciidoc/index.adoc index e66ee6cb2..2c07712c4 100644 --- a/src/docs/asciidoc/index.adoc +++ b/src/docs/asciidoc/index.adoc @@ -129,6 +129,14 @@ operation::recruitment-controller-test/register-recruitment[snippets='http-respo == 1. 봉사자 +=== 봉사자 계정 정보 수정 + +==== Request +operation::volunteer-controller-test/update-volunteer-info[snippets='http-request,request-headers,request-fields'] + +==== Response +operation::volunteer-controller-test/update-volunteer-info[snippets='http-response'] + == 2. 보호소 == 3. 봉사 모집글 diff --git a/src/main/java/com/clova/anifriends/domain/applicant/dto/FindApplicantsApprovedResponse.java b/src/main/java/com/clova/anifriends/domain/applicant/dto/FindApplicantsApprovedResponse.java index e490c8b98..2e59c229e 100644 --- a/src/main/java/com/clova/anifriends/domain/applicant/dto/FindApplicantsApprovedResponse.java +++ b/src/main/java/com/clova/anifriends/domain/applicant/dto/FindApplicantsApprovedResponse.java @@ -1,6 +1,7 @@ package com.clova.anifriends.domain.applicant.dto; import com.clova.anifriends.domain.applicant.Applicant; +import com.clova.anifriends.domain.volunteer.wrapper.VolunteerGender; import java.time.LocalDate; import java.util.List; @@ -13,7 +14,7 @@ private record FindApplicant( Long applicantId, String volunteerName, LocalDate volunteerBirthDate, - String volunteerGender, + VolunteerGender volunteerGender, String volunteerPhoneNumber, boolean volunteerAttendance ) { diff --git a/src/main/java/com/clova/anifriends/domain/applicant/dto/FindApplicantsResponse.java b/src/main/java/com/clova/anifriends/domain/applicant/dto/FindApplicantsResponse.java index b8e46a735..b4da4eaa7 100644 --- a/src/main/java/com/clova/anifriends/domain/applicant/dto/FindApplicantsResponse.java +++ b/src/main/java/com/clova/anifriends/domain/applicant/dto/FindApplicantsResponse.java @@ -2,6 +2,7 @@ import com.clova.anifriends.domain.applicant.Applicant; import com.clova.anifriends.domain.recruitment.Recruitment; +import com.clova.anifriends.domain.volunteer.wrapper.VolunteerGender; import java.time.LocalDate; import java.util.List; @@ -14,7 +15,7 @@ public record FindApplicantResponse( Long volunteerId, Long applicantId, LocalDate volunteerBirthDate, - String volunteerGender, + VolunteerGender volunteerGender, Integer completedVolunteerCount, Integer volunteerTemperature, String applicantStatus diff --git a/src/main/java/com/clova/anifriends/domain/common/ImageRemover.java b/src/main/java/com/clova/anifriends/domain/common/ImageRemover.java new file mode 100644 index 000000000..73f9ae898 --- /dev/null +++ b/src/main/java/com/clova/anifriends/domain/common/ImageRemover.java @@ -0,0 +1,10 @@ +package com.clova.anifriends.domain.common; + +public interface ImageRemover { + + /** + * 원격지에 저장된 이미지를 삭제합니다. + * @param imageUrl 이미지가 저장된 위치를 뜻하는 문자열 + */ + void removeImage(String imageUrl); +} diff --git a/src/main/java/com/clova/anifriends/domain/common/MockImageRemover.java b/src/main/java/com/clova/anifriends/domain/common/MockImageRemover.java new file mode 100644 index 000000000..1405d7439 --- /dev/null +++ b/src/main/java/com/clova/anifriends/domain/common/MockImageRemover.java @@ -0,0 +1,14 @@ +package com.clova.anifriends.domain.common; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +public class MockImageRemover implements ImageRemover { + + @Override + public void removeImage(String imageUrl) { + log.info("이미지가 삭제되었습니다."); + } +} diff --git a/src/main/java/com/clova/anifriends/domain/volunteer/Volunteer.java b/src/main/java/com/clova/anifriends/domain/volunteer/Volunteer.java index 9bb9675e1..e6f819f55 100644 --- a/src/main/java/com/clova/anifriends/domain/volunteer/Volunteer.java +++ b/src/main/java/com/clova/anifriends/domain/volunteer/Volunteer.java @@ -4,6 +4,7 @@ import com.clova.anifriends.domain.applicant.Applicant; import com.clova.anifriends.domain.common.BaseTimeEntity; +import com.clova.anifriends.domain.common.ImageRemover; import com.clova.anifriends.domain.volunteer.exception.VolunteerBadRequestException; import com.clova.anifriends.domain.volunteer.wrapper.VolunteerEmail; import com.clova.anifriends.domain.volunteer.wrapper.VolunteerGender; @@ -11,6 +12,7 @@ import com.clova.anifriends.domain.volunteer.wrapper.VolunteerPassword; import com.clova.anifriends.domain.volunteer.wrapper.VolunteerPhoneNumber; import com.clova.anifriends.domain.volunteer.wrapper.VolunteerTemperature; +import jakarta.persistence.CascadeType; import jakarta.persistence.Column; import jakarta.persistence.Embedded; import jakarta.persistence.Entity; @@ -67,7 +69,7 @@ public class Volunteer extends BaseTimeEntity { @OneToMany(mappedBy = "volunteer", fetch = FetchType.LAZY) private List applicants = new ArrayList<>(); - @OneToOne(mappedBy = "volunteer") + @OneToOne(mappedBy = "volunteer", cascade = CascadeType.ALL, fetch = FetchType.LAZY) private VolunteerImage volunteerImage; public Volunteer( @@ -103,6 +105,45 @@ public void updateVolunteerImage(VolunteerImage volunteerImage) { this.volunteerImage = volunteerImage; } + public void updateVolunteerInfo( + String name, + VolunteerGender gender, + LocalDate birthDate, + String phoneNumber, + String imageUrl, + ImageRemover imageRemover) { + this.name = this.name.updateName(name); + this.gender = updateGender(gender); + this.birthDate = updateBirthDate(birthDate); + this.phoneNumber = this.phoneNumber.updatePhoneNumber(phoneNumber); + this.volunteerImage = updateVolunteerImage(imageUrl, imageRemover); + } + + private LocalDate updateBirthDate(LocalDate birthDate) { + return birthDate != null ? birthDate : this.birthDate; + } + + private VolunteerGender updateGender(VolunteerGender gender) { + return gender != null ? gender : this.gender; + } + + private VolunteerImage updateVolunteerImage(String imageUrl, ImageRemover imageRemover) { + if (Objects.nonNull(volunteerImage) && volunteerImage.isEqualImageUrl(imageUrl)) { + return this.volunteerImage; + } + clearVolunteerImageIfExists(imageRemover); + if(Objects.isNull(imageUrl)) { + return null; + } + return new VolunteerImage(this, imageUrl); + } + + private void clearVolunteerImageIfExists(ImageRemover imageRemover) { + if (Objects.nonNull(volunteerImage)) { + volunteerImage.removeImage(imageRemover); + } + } + public long getReviewCount() { return applicants.stream() .filter(applicant -> Objects.nonNull(applicant.getReview())) @@ -129,8 +170,8 @@ public String getPhoneNumber() { return this.phoneNumber.getPhoneNumber(); } - public String getGender() { - return this.gender.getName(); + public VolunteerGender getGender() { + return this.gender; } public Integer getTemperature() { diff --git a/src/main/java/com/clova/anifriends/domain/volunteer/VolunteerImage.java b/src/main/java/com/clova/anifriends/domain/volunteer/VolunteerImage.java index 69c4ced52..67d989c0a 100644 --- a/src/main/java/com/clova/anifriends/domain/volunteer/VolunteerImage.java +++ b/src/main/java/com/clova/anifriends/domain/volunteer/VolunteerImage.java @@ -1,6 +1,7 @@ package com.clova.anifriends.domain.volunteer; import com.clova.anifriends.domain.common.BaseTimeEntity; +import com.clova.anifriends.domain.common.ImageRemover; import com.clova.anifriends.domain.volunteer.exception.VolunteerBadRequestException; import com.clova.anifriends.global.exception.ErrorCode; import jakarta.persistence.Column; @@ -52,4 +53,12 @@ private void validateImageUrl(String value) { throw new VolunteerBadRequestException(ErrorCode.BAD_REQUEST, "이미지 url은 필수 항목입니다."); } } + + public boolean isEqualImageUrl(String imageUrl) { + return this.imageUrl.equals(imageUrl); + } + + public void removeImage(ImageRemover imageRemover) { + imageRemover.removeImage(imageUrl); + } } diff --git a/src/main/java/com/clova/anifriends/domain/volunteer/controller/VolunteerController.java b/src/main/java/com/clova/anifriends/domain/volunteer/controller/VolunteerController.java index 5b6ad8547..27dce1812 100644 --- a/src/main/java/com/clova/anifriends/domain/volunteer/controller/VolunteerController.java +++ b/src/main/java/com/clova/anifriends/domain/volunteer/controller/VolunteerController.java @@ -3,6 +3,7 @@ import com.clova.anifriends.domain.auth.LoginUser; import com.clova.anifriends.domain.volunteer.dto.request.CheckDuplicateVolunteerEmailRequest; import com.clova.anifriends.domain.volunteer.dto.request.RegisterVolunteerRequest; +import com.clova.anifriends.domain.volunteer.dto.request.UpdateVolunteerInfoRequest; import com.clova.anifriends.domain.volunteer.dto.response.CheckDuplicateVolunteerEmailResponse; import com.clova.anifriends.domain.volunteer.dto.response.FindVolunteerMyPageResponse; import com.clova.anifriends.domain.volunteer.dto.response.FindVolunteerProfileResponse; @@ -12,6 +13,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -68,4 +70,18 @@ public ResponseEntity findVolunteerProfile( ) ); } + + @PatchMapping("/volunteers/me") + public ResponseEntity updateVolunteerInfo( + @LoginUser Long volunteerId, + @RequestBody @Valid UpdateVolunteerInfoRequest updateVolunteerInfoRequest) { + volunteerService.updateVolunteerInfo( + volunteerId, + updateVolunteerInfoRequest.name(), + updateVolunteerInfoRequest.gender(), + updateVolunteerInfoRequest.birthDate(), + updateVolunteerInfoRequest.phoneNumber(), + updateVolunteerInfoRequest.imageUrl()); + return ResponseEntity.noContent().build(); + } } diff --git a/src/main/java/com/clova/anifriends/domain/volunteer/dto/request/UpdateVolunteerInfoRequest.java b/src/main/java/com/clova/anifriends/domain/volunteer/dto/request/UpdateVolunteerInfoRequest.java new file mode 100644 index 000000000..e7c73e279 --- /dev/null +++ b/src/main/java/com/clova/anifriends/domain/volunteer/dto/request/UpdateVolunteerInfoRequest.java @@ -0,0 +1,13 @@ +package com.clova.anifriends.domain.volunteer.dto.request; + +import com.clova.anifriends.domain.volunteer.wrapper.VolunteerGender; +import java.time.LocalDate; + +public record UpdateVolunteerInfoRequest( + String name, + VolunteerGender gender, + LocalDate birthDate, + String phoneNumber, + String imageUrl) { + +} diff --git a/src/main/java/com/clova/anifriends/domain/volunteer/dto/response/FindVolunteerMyPageResponse.java b/src/main/java/com/clova/anifriends/domain/volunteer/dto/response/FindVolunteerMyPageResponse.java index f38d50e98..daa077162 100644 --- a/src/main/java/com/clova/anifriends/domain/volunteer/dto/response/FindVolunteerMyPageResponse.java +++ b/src/main/java/com/clova/anifriends/domain/volunteer/dto/response/FindVolunteerMyPageResponse.java @@ -2,6 +2,7 @@ import com.clova.anifriends.domain.applicant.wrapper.ApplicantStatus; import com.clova.anifriends.domain.volunteer.Volunteer; +import com.clova.anifriends.domain.volunteer.wrapper.VolunteerGender; import java.time.LocalDate; public record FindVolunteerMyPageResponse( @@ -13,7 +14,7 @@ public record FindVolunteerMyPageResponse( int volunteerTemperature, long completedVolunteerCount, String volunteerImageUrl, - String volunteerGender + VolunteerGender volunteerGender ) { public static FindVolunteerMyPageResponse from(Volunteer volunteer) { diff --git a/src/main/java/com/clova/anifriends/domain/volunteer/service/VolunteerService.java b/src/main/java/com/clova/anifriends/domain/volunteer/service/VolunteerService.java index d352fbde2..75b62e76b 100644 --- a/src/main/java/com/clova/anifriends/domain/volunteer/service/VolunteerService.java +++ b/src/main/java/com/clova/anifriends/domain/volunteer/service/VolunteerService.java @@ -1,5 +1,6 @@ package com.clova.anifriends.domain.volunteer.service; +import com.clova.anifriends.domain.common.ImageRemover; import com.clova.anifriends.domain.volunteer.Volunteer; import com.clova.anifriends.domain.volunteer.dto.response.CheckDuplicateVolunteerEmailResponse; import com.clova.anifriends.domain.volunteer.dto.response.FindVolunteerMyPageResponse; @@ -7,6 +8,8 @@ import com.clova.anifriends.domain.volunteer.exception.VolunteerNotFoundException; import com.clova.anifriends.domain.volunteer.repository.VolunteerRepository; import com.clova.anifriends.domain.volunteer.wrapper.VolunteerEmail; +import com.clova.anifriends.domain.volunteer.wrapper.VolunteerGender; +import java.time.LocalDate; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -16,6 +19,7 @@ public class VolunteerService { private final VolunteerRepository volunteerRepository; + private final ImageRemover imageRemover; @Transactional(readOnly = true) public CheckDuplicateVolunteerEmailResponse checkDuplicateVolunteerEmail(String email) { @@ -58,4 +62,16 @@ private Volunteer getVolunteer(Long volunteerId) { return volunteerRepository.findById(volunteerId) .orElseThrow(() -> new VolunteerNotFoundException("존재하지 않는 봉사자입니다.")); } + + @Transactional + public void updateVolunteerInfo( + Long volunteerId, + String name, + VolunteerGender gender, + LocalDate birthDate, + String phoneNumber, + String imageUrl) { + Volunteer volunteer = getVolunteer(volunteerId); + volunteer.updateVolunteerInfo(name, gender, birthDate, phoneNumber, imageUrl, imageRemover); + } } diff --git a/src/main/java/com/clova/anifriends/domain/volunteer/wrapper/VolunteerName.java b/src/main/java/com/clova/anifriends/domain/volunteer/wrapper/VolunteerName.java index f7ea6a920..caa89838a 100644 --- a/src/main/java/com/clova/anifriends/domain/volunteer/wrapper/VolunteerName.java +++ b/src/main/java/com/clova/anifriends/domain/volunteer/wrapper/VolunteerName.java @@ -4,10 +4,14 @@ import com.clova.anifriends.global.exception.ErrorCode; import jakarta.persistence.Column; import jakarta.persistence.Embeddable; +import java.util.Objects; +import lombok.AccessLevel; import lombok.Getter; +import lombok.NoArgsConstructor; @Getter @Embeddable +@NoArgsConstructor(access = AccessLevel.PROTECTED) public class VolunteerName { private static final int MAX_VOLUNTEER_NAME_LENGTH = 10; @@ -15,21 +19,25 @@ public class VolunteerName { @Column(name = "name") private String name; - protected VolunteerName() { - } - public VolunteerName(String value) { - validateVolunteerName(value); + validateNotNull(value); + validateVolunteerNameLength(value); this.name = value; } - private void validateVolunteerName(String name) { - if (name == null || name.isBlank()) { + private void validateNotNull(String name) { + if (Objects.isNull(name) || name.isBlank()) { throw new VolunteerBadRequestException(ErrorCode.BAD_REQUEST, "이름은 필수 항목입니다."); } + } + private void validateVolunteerNameLength(String name) { if (name.length() > MAX_VOLUNTEER_NAME_LENGTH) { throw new VolunteerBadRequestException(ErrorCode.BAD_REQUEST, "이름은 최대 10자입니다."); } } + + public VolunteerName updateName(String name) { + return name != null ? new VolunteerName(name) : this; + } } diff --git a/src/main/java/com/clova/anifriends/domain/volunteer/wrapper/VolunteerPhoneNumber.java b/src/main/java/com/clova/anifriends/domain/volunteer/wrapper/VolunteerPhoneNumber.java index 77368f76f..5b13572d3 100644 --- a/src/main/java/com/clova/anifriends/domain/volunteer/wrapper/VolunteerPhoneNumber.java +++ b/src/main/java/com/clova/anifriends/domain/volunteer/wrapper/VolunteerPhoneNumber.java @@ -33,4 +33,8 @@ private void validateVolunteerPhoneNumber(String phoneNumber) { "전화번호 형식이 올바르지 않습니다."); } } + + public VolunteerPhoneNumber updatePhoneNumber(String phoneNumber) { + return phoneNumber != null ? new VolunteerPhoneNumber(phoneNumber) : this; + } } diff --git a/src/main/resources/static/docs/index.html b/src/main/resources/static/docs/index.html index 169c5e289..f27b61c1c 100644 --- a/src/main/resources/static/docs/index.html +++ b/src/main/resources/static/docs/index.html @@ -493,7 +493,11 @@

API 문서

  • 봉사자