Skip to content

Commit

Permalink
feat: 계정 정보를 수정한다.(봉사자) (#173)
Browse files Browse the repository at this point in the history
* feat: 봉사자 계정정보 수정 서비스 로직을 구현한다.

* feat: 봉사자 계정정보 수정 api를 구현한다.

* test: ImageRemover 인터페이스의 Mock을 테스트 커버리지에서 제외한다.

* docs: 봉사자 계정 정보 수정 api를 문서화한다.

* refactor: 봉사자 이미지 업데이트 도메인 로직을 개선한다.

* refactor: 봉사자 계정 정보 수정 로직에서 엔티티 불변을 제거한다.
  • Loading branch information
hseong3243 authored Nov 11, 2023
1 parent 0b1ff4c commit 096f9bb
Show file tree
Hide file tree
Showing 20 changed files with 597 additions and 43 deletions.
3 changes: 2 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,8 @@ private excludedClassFilesForReport(classDirectories) {
"**/*Config*",
"**/*Response*",
"**/*Request*",
"**/exception/**"
"**/exception/**",
"**/Mock**"
])
})
)
Expand Down
8 changes: 8 additions & 0 deletions src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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. 봉사 모집글
Expand Down
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -13,7 +14,7 @@ private record FindApplicant(
Long applicantId,
String volunteerName,
LocalDate volunteerBirthDate,
String volunteerGender,
VolunteerGender volunteerGender,
String volunteerPhoneNumber,
boolean volunteerAttendance
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -14,7 +15,7 @@ public record FindApplicantResponse(
Long volunteerId,
Long applicantId,
LocalDate volunteerBirthDate,
String volunteerGender,
VolunteerGender volunteerGender,
Integer completedVolunteerCount,
Integer volunteerTemperature,
String applicantStatus
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/com/clova/anifriends/domain/common/ImageRemover.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.clova.anifriends.domain.common;

public interface ImageRemover {

/**
* 원격지에 저장된 이미지를 삭제합니다.
* @param imageUrl 이미지가 저장된 위치를 뜻하는 문자열
*/
void removeImage(String imageUrl);
}
Original file line number Diff line number Diff line change
@@ -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("이미지가 삭제되었습니다.");
}
}
47 changes: 44 additions & 3 deletions src/main/java/com/clova/anifriends/domain/volunteer/Volunteer.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@

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;
import com.clova.anifriends.domain.volunteer.wrapper.VolunteerName;
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;
Expand Down Expand Up @@ -67,7 +69,7 @@ public class Volunteer extends BaseTimeEntity {
@OneToMany(mappedBy = "volunteer", fetch = FetchType.LAZY)
private List<Applicant> applicants = new ArrayList<>();

@OneToOne(mappedBy = "volunteer")
@OneToOne(mappedBy = "volunteer", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private VolunteerImage volunteerImage;

public Volunteer(
Expand Down Expand Up @@ -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()))
Expand All @@ -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() {
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -68,4 +70,18 @@ public ResponseEntity<FindVolunteerProfileResponse> findVolunteerProfile(
)
);
}

@PatchMapping("/volunteers/me")
public ResponseEntity<Void> 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();
}
}
Original file line number Diff line number Diff line change
@@ -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) {

}
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -13,7 +14,7 @@ public record FindVolunteerMyPageResponse(
int volunteerTemperature,
long completedVolunteerCount,
String volunteerImageUrl,
String volunteerGender
VolunteerGender volunteerGender
) {

public static FindVolunteerMyPageResponse from(Volunteer volunteer) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
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;
import com.clova.anifriends.domain.volunteer.dto.response.FindVolunteerProfileResponse;
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;
Expand All @@ -16,6 +19,7 @@
public class VolunteerService {

private final VolunteerRepository volunteerRepository;
private final ImageRemover imageRemover;

@Transactional(readOnly = true)
public CheckDuplicateVolunteerEmailResponse checkDuplicateVolunteerEmail(String email) {
Expand Down Expand Up @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,40 @@
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;

@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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,8 @@ private void validateVolunteerPhoneNumber(String phoneNumber) {
"전화번호 형식이 올바르지 않습니다.");
}
}

public VolunteerPhoneNumber updatePhoneNumber(String phoneNumber) {
return phoneNumber != null ? new VolunteerPhoneNumber(phoneNumber) : this;
}
}
Loading

0 comments on commit 096f9bb

Please sign in to comment.