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
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
@MappedSuperclass
public abstract class SoftDeletableEntity extends BaseEntity {

private LocalDateTime deletedDate;
private LocalDateTime deletedAt;

public void softDelete() {
this.deletedDate = LocalDateTime.now();
this.deletedAt = LocalDateTime.now();
}

public boolean isDeleted() {
return deletedDate != null;
return deletedAt != null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.DeleteMapping;
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 jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import project.flipnote.common.security.dto.UserAuth;
import project.flipnote.user.model.UserRegisterRequest;
import project.flipnote.user.model.UserRegisterResponse;
import project.flipnote.user.service.UserService;
Expand All @@ -25,4 +28,10 @@ public ResponseEntity<UserRegisterResponse> register(@Valid @RequestBody UserReg
UserRegisterResponse res = userService.register(req);
return ResponseEntity.status(HttpStatus.CREATED).body(res);
}

@DeleteMapping
public ResponseEntity<Void> unregister(@AuthenticationPrincipal UserAuth userAuth) {
userService.unregister(userAuth.userId());
return ResponseEntity.noContent().build();
}
}
5 changes: 5 additions & 0 deletions src/main/java/project/flipnote/user/entity/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,9 @@ public User(
this.role = UserRole.USER;
}

@Override
public void softDelete() {
super.softDelete();
this.status = UserStatus.INACTIVE;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ public interface UserRepository extends JpaRepository<User, Long> {
boolean existsByPhone(String phone);

Optional<User> findByEmailAndStatus(String email, UserStatus status);

Optional<User> findByIdAndStatus(Long userId, UserStatus status);
}
13 changes: 13 additions & 0 deletions src/main/java/project/flipnote/user/service/UserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import project.flipnote.auth.service.AuthService;
import project.flipnote.common.exception.BizException;
import project.flipnote.user.entity.User;
import project.flipnote.user.entity.UserStatus;
import project.flipnote.user.exception.UserErrorCode;
import project.flipnote.user.model.UserRegisterRequest;
import project.flipnote.user.model.UserRegisterResponse;
Expand Down Expand Up @@ -50,6 +51,18 @@ public UserRegisterResponse register(UserRegisterRequest req) {
return UserRegisterResponse.from(savedUser.getId());
}

@Transactional
public void unregister(Long userId) {
User user = findActiveUserById(userId);

user.softDelete();
}

private User findActiveUserById(Long userId) {
return userRepository.findByIdAndStatus(userId, UserStatus.ACTIVE)
.orElseThrow(() -> new BizException(UserErrorCode.USER_NOT_FOUND));
}

private void validateEmailDuplicate(String email) {
if (userRepository.existsByEmail(email)) {
throw new BizException(UserErrorCode.DUPLICATE_EMAIL);
Expand Down
51 changes: 34 additions & 17 deletions src/test/java/project/flipnote/user/service/UserServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.BDDMockito.*;

import java.util.Optional;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
Expand All @@ -18,7 +20,9 @@
import project.flipnote.auth.exception.AuthErrorCode;
import project.flipnote.auth.service.AuthService;
import project.flipnote.common.exception.BizException;
import project.flipnote.fixture.UserFixture;
import project.flipnote.user.entity.User;
import project.flipnote.user.entity.UserStatus;
import project.flipnote.user.exception.UserErrorCode;
import project.flipnote.user.model.UserRegisterRequest;
import project.flipnote.user.model.UserRegisterResponse;
Expand All @@ -40,30 +44,14 @@ class UserServiceTest {
@Mock
private PasswordEncoder passwordEncoder;

private User user;

@BeforeEach
void init() {
user = User.builder()
.email("test@test.com")
.password("testPass")
.name("테스트")
.nickname("테스트")
.smsAgree(false)
.phone("010-1234-5678")
.profileImageUrl(null)
.build();

ReflectionTestUtils.setField(user, "id", 1L);
}

@DisplayName("회원가입 테스트")
@Nested
class Register {

@DisplayName("성공")
@Test
void success() {
User user = UserFixture.createActiveUser();
UserRegisterRequest req = new UserRegisterRequest(
"test@test.com", "testPass", "테스트", "테스트", false, "010-1234-5678", ""
);
Expand All @@ -84,6 +72,7 @@ void success() {
@DisplayName("휴대전화 번호가 null일 때 성공")
@Test
void success_ifPhoneIsNull() {
User user = UserFixture.createActiveUser();
UserRegisterRequest req = new UserRegisterRequest(
"test@test.com", "testPass", "테스트", "테스트", false, null, null
);
Expand Down Expand Up @@ -151,4 +140,32 @@ void fail_unverifiedEmail() {
}
}

@DisplayName("회원 탈퇴 테스트")
@Nested
class Unregister {

@DisplayName("성공")
@Test
void success() {
User user = spy(UserFixture.createActiveUser());

given(userRepository.findByIdAndStatus(anyLong(), any(UserStatus.class))).willReturn(Optional.of(user));

userService.unregister(user.getId());

assertThat(user.getStatus()).isEqualTo(UserStatus.INACTIVE);
assertThat(user.getDeletedAt()).isNotNull();

verify(user, times(1)).softDelete();
}

@DisplayName("회원 id가 존재하지 않는 경우 예외 발생")
@Test
void fail_userNotFound() {
given(userRepository.findByIdAndStatus(anyLong(), any(UserStatus.class))).willReturn(Optional.empty());

BizException exception = assertThrows(BizException.class, () -> userService.unregister(1L));
assertThat(exception.getErrorCode()).isEqualTo(UserErrorCode.USER_NOT_FOUND);
}
}
}
Loading