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 .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
HELP.md
.gradle
.history
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
Expand Down
1 change: 0 additions & 1 deletion src/docs/asciidoc/auth-api.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,3 @@ include::{snippetsDir}/sessionValidityCheck/2/http-response.adoc[]
==== Response Body Fields
include::{snippetsDir}/sessionValidityCheck/1/response-fields.adoc[]


44 changes: 34 additions & 10 deletions src/docs/asciidoc/user-api.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ include::{snippetsDir}/emailAuthentication/3/http-response.adoc[]

---

[[email-code-verification]]
=== **3. 이메일 인증 코드 검증 api**

이메일 인증용 코드를 검증하는 api입니다.
Expand All @@ -58,7 +59,30 @@ include::{snippetsDir}/emailCodeVerification/1/response-fields.adoc[]

---

=== **4. 회원가입시 필요한 정보 목록 조회 api**

=== **4. 회원 계정 복구 API**

탈퇴한 회원의 계정 복구 api 입니다. 별도의 인증 없이 요청이 가능합니다.
<<email-code-verification, 이메일 인증 코드 검증 API>>에서 isRecoverable 필드가 true인 경우 요청 가능합니다.

==== Request
include::{snippetsDir}/recoverUserAccount/1/http-request.adoc[]

==== Request Body Fields
include::{snippetsDir}/recoverUserAccount/1/request-fields.adoc[]

==== 성공 Response
성공 1. 유효한 경우
include::{snippetsDir}/recoverUserAccount/1/http-response.adoc[]

성공 2. 유효하지 않은 경우
include::{snippetsDir}/recoverUserAccount/2/http-response.adoc[]

==== Response Body Fields
include::{snippetsDir}/recoverUserAccount/1/response-fields.adoc[]


=== **5. 회원가입시 필요한 정보 목록 조회 api**

회원가입시 사용자에게 입력 받는 연령대 정보와 관심 해시태그 정보 옵션들을 조회합니다.

Expand All @@ -74,7 +98,7 @@ include::{snippetsDir}/userSignupOptions/1/response-fields.adoc[]

---

=== **5. 일반 회원가입 api**
=== **6. 일반 회원가입 api**

일반 회원가입 api입니다.

Expand All @@ -97,7 +121,7 @@ include::{snippetsDir}/generalUserSignUp/2/http-response.adoc[]
include::{snippetsDir}/generalUserSignUp/3/http-response.adoc[]


=== **6. 소셜 회원가입 api**
=== **7. 소셜 회원가입 api**

소셜 회원가입 api입니다.

Expand All @@ -124,7 +148,7 @@ include::{snippetsDir}/socialUserSignUp/2/http-response.adoc[]
include::{snippetsDir}/socialUserSignUp/3/http-response.adoc[]


=== **7. 사용자 정보 간단 조회 api**
=== **8. 사용자 정보 간단 조회 api**

사용자 정보 수정 시 노출되는 정보를 제공

Expand All @@ -138,7 +162,7 @@ include::{snippetsDir}/userSimpleInfo/1/http-response.adoc[]
include::{snippetsDir}/userSimpleInfo/1/response-fields.adoc[]


=== **8. 사용자 정보 수정 api**
=== **9. 사용자 정보 수정 api**

사용자 정보를 수정합니다. 수정이 필요한 항목만 수정을 요청해 주세요.

Expand All @@ -164,7 +188,7 @@ include::{snippetsDir}/userInfoUpdate/2/http-response.adoc[]
include::{snippetsDir}/userInfoUpdate/3/http-response.adoc[]


=== **9. 회원 탈퇴 api**
=== **10. 회원 탈퇴 api**

회원 탈퇴를 진행

Expand All @@ -178,7 +202,7 @@ include::{snippetsDir}/userExit/1/http-response.adoc[]
include::{snippetsDir}/userExit/1/response-fields.adoc[]


=== **10. 북마크 생성 api**
=== **11. 북마크 생성 api**

게시글 북마크를 생성

Expand All @@ -203,7 +227,7 @@ include::{snippetsDir}/createBookmark/1/response-fields.adoc[]
include::{snippetsDir}/createBookmark/3/http-response.adoc[]


=== **11. 내가 작성한 유저픽 게시글 목록 조회**
=== **12. 내가 작성한 유저픽 게시글 목록 조회**

마이페이지 > 작성한 게시글 목록 > 더보기 버튼 클릭 시 내가 작성한 유저픽 게시글 목록을 조회하는 api 입니다. +
무한 스크롤, 더보기 형식으로 조회하여 모든 데이터 개수와 번호를 부여하는 Page 방식이 아닌, +
Expand Down Expand Up @@ -236,7 +260,7 @@ include::{snippetsDir}/loadMyPosts/3/http-response.adoc[]
include::{snippetsDir}/loadMyPosts/4/http-response.adoc[]


=== **12. 내가 북마크한 유저픽 게시글 목록 조회**
=== **13. 내가 북마크한 유저픽 게시글 목록 조회**

마이페이지 > 북마크한 게시글 목록 > 더보기 버튼 클릭 시 내가 작성한 유저픽 게시글 목록을 조회하는 api 입니다. +
무한 스크롤, 더보기 형식으로 조회하여 모든 데이터 개수와 번호를 부여하는 Page 방식이 아닌, +
Expand Down Expand Up @@ -268,7 +292,7 @@ include::{snippetsDir}/loadMyBookmarkPosts/3/http-response.adoc[]

include::{snippetsDir}/loadMyBookmarkPosts/4/http-response.adoc[]

=== **13. 북마크 삭제 api**
=== **14. 북마크 삭제 api**

사용자가 등록한 북마크를 삭제합니다.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.ftm.server.adapter.in.web.user.controller;

import com.ftm.server.adapter.in.web.user.dto.request.RecoverUserAccountRequest;
import com.ftm.server.adapter.in.web.user.dto.response.RecoverUserAccountResponse;
import com.ftm.server.application.command.user.RecoverUserAccountCommand;
import com.ftm.server.application.port.in.user.RecoverUserAccountUseCase;
import com.ftm.server.common.response.ApiResponse;
import com.ftm.server.common.response.enums.SuccessResponseCode;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class RecoverUserAccountController {

private final RecoverUserAccountUseCase recoverUserAccountUseCase;

@PostMapping("/api/users/me/recover")
public ResponseEntity<ApiResponse> recoverUserAccount(
@Valid @RequestBody RecoverUserAccountRequest request) {
RecoverUserAccountResponse response =
RecoverUserAccountResponse.from(
recoverUserAccountUseCase.execute(
RecoverUserAccountCommand.of(request.getEmail())));
return ResponseEntity.ok(ApiResponse.success(SuccessResponseCode.OK, response));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.ftm.server.adapter.in.web.user.dto.request;

import jakarta.validation.constraints.Pattern;
import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class RecoverUserAccountRequest {

@Pattern(
regexp = "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$",
message = "이메일 형식이 올바르지 않습니다.")
private final String email;

public static RecoverUserAccountRequest of(String email) {
return new RecoverUserAccountRequest(email);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.ftm.server.adapter.in.web.user.dto.response;

import com.ftm.server.application.vo.user.RecoverUserAccountVo;
import lombok.AllArgsConstructor;
import lombok.Getter;

@AllArgsConstructor
@Getter
public class RecoverUserAccountResponse {
private final Long userId;

public static RecoverUserAccountResponse from(RecoverUserAccountVo vo) {
return new RecoverUserAccountResponse(vo.getUserId());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -307,4 +307,11 @@ public List<PostImage> loadRepresentativeImagesByPostIds(FindByIdsQuery query) {
.map(postImageMapper::toDomainEntity)
.toList();
}

@Override
public List<Post> loadPostListByUsers(FindByUserIdsQuery query) {
return postRepository.findAllByUserIdIn(query.getUserIds()).stream()
.map(postMapper::toDomainEntity)
.toList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,7 @@ public interface PostRepository
void deleteAllByIdInBatch(@Param("postIds") List<Long> postIds);

boolean existsById(Long id);

@Query("SELECT p FROM PostJpaEntity p WHERE p.user.id IN (:userIds)")
List<PostJpaEntity> findAllByUserIdIn(@Param("userIds") List<Long> userIds);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.ftm.server.application.command.user;

import lombok.AllArgsConstructor;
import lombok.Getter;

@AllArgsConstructor
@Getter
public class RecoverUserAccountCommand {
private String email;

public static RecoverUserAccountCommand of(String email) {
return new RecoverUserAccountCommand(email);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.ftm.server.application.port.in.user;

import com.ftm.server.application.command.user.RecoverUserAccountCommand;
import com.ftm.server.application.vo.user.RecoverUserAccountVo;
import com.ftm.server.common.annotation.UseCase;

@UseCase
public interface RecoverUserAccountUseCase {

RecoverUserAccountVo execute(RecoverUserAccountCommand command);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.ftm.server.application.port.out.persistence.post;

import com.ftm.server.application.query.FindByIdQuery;
import com.ftm.server.application.query.FindByUserIdsQuery;
import com.ftm.server.application.query.FindPostByDeleteOptionQuery;
import com.ftm.server.common.annotation.Port;
import com.ftm.server.domain.entity.Post;
Expand All @@ -12,5 +13,7 @@ public interface LoadPostPort {

Optional<Post> loadPost(FindByIdQuery query);

List<Post> loadPostListByUsers(FindByUserIdsQuery query);

List<Post> loadPostsByDeleteOption(FindPostByDeleteOptionQuery query);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.ftm.server.application.query;

import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Getter;

@AllArgsConstructor
@Getter
public class FindByUserIdsQuery {

private final List<Long> userIds;

public static FindByUserIdsQuery of(List<Long> userIds) {
return new FindByUserIdsQuery(userIds);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ public GeneralUserSignupResponse execute(GeneralUserSignupCommand command) {
throw new CustomException(ErrorResponseCode.USER_ALREADY_EXISTS);
}

if (emailVerificationLogs.isEmpty()) { // 이메일 인증이 완료되지 않음.
if (emailVerificationLogs.isEmpty()
|| !emailVerificationLogs.get().getIsVerified()) { // 이메일 인증이 완료되지 않음.
throw new CustomException(ErrorResponseCode.EMAIL_NOT_VERIFIED);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.ftm.server.application.service.user;

import com.ftm.server.application.command.user.RecoverUserAccountCommand;
import com.ftm.server.application.port.in.user.RecoverUserAccountUseCase;
import com.ftm.server.application.port.out.persistence.user.LoadEmailVerificationLogPort;
import com.ftm.server.application.port.out.persistence.user.LoadUserPort;
import com.ftm.server.application.port.out.persistence.user.UpdateUserPort;
import com.ftm.server.application.query.FindByEmailQuery;
import com.ftm.server.application.vo.user.RecoverUserAccountVo;
import com.ftm.server.common.exception.CustomException;
import com.ftm.server.common.response.enums.ErrorResponseCode;
import com.ftm.server.domain.entity.EmailVerificationLogs;
import com.ftm.server.domain.entity.User;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@RequiredArgsConstructor
@Service
@Slf4j
public class RecoverUserAccountService implements RecoverUserAccountUseCase {

private final LoadUserPort loadUserPort;
private final UpdateUserPort updateUserPort;

private final LoadEmailVerificationLogPort loadEmailVerificationLogPort;

@Override
public RecoverUserAccountVo execute(RecoverUserAccountCommand command) {

// 1. 이메일 인증 마쳤는지 검사
Optional<EmailVerificationLogs> optionalEmailVerificationLogs =
loadEmailVerificationLogPort.loadEmailVerificationLogByEmail(
FindByEmailQuery.of(command.getEmail()));
// 검증 진행 전이거나, 검증이 완료되지 않은 경우 복구 불가
if (optionalEmailVerificationLogs.isEmpty()
|| !optionalEmailVerificationLogs.get().getIsVerified()) {
throw new CustomException(ErrorResponseCode.RECOVERING_IS_NOT_AVAILABLE);
}

// 2. soft delete 된 user 조회
Optional<User> optionalUser =
loadUserPort.loadDeletedUserByEmail(FindByEmailQuery.of(command.getEmail()));
// 삭제되지 않은 회원에 대해 복구 시도하는 경우 복구 불가
if (optionalUser.isEmpty()) {
throw new CustomException(ErrorResponseCode.RECOVERING_IS_NOT_AVAILABLE);
}

// 3. soft delete된 user 복구
User user = optionalUser.get();
user.updateIsDeleted(false);
user.updateDeletedAt(null);

updateUserPort.updateUser(user);

return RecoverUserAccountVo.of(user.getId());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,12 @@

import com.ftm.server.application.command.user.DeleteUserByIdCommand;
import com.ftm.server.application.port.in.user.UserExitUseCase;
import com.ftm.server.application.port.out.persistence.user.LoadPostUserDomainPort;
import com.ftm.server.application.port.out.persistence.user.LoadUserPort;
import com.ftm.server.application.port.out.persistence.user.UpdatePostUserDomainPort;
import com.ftm.server.application.port.out.persistence.user.UpdateUserPort;
import com.ftm.server.application.query.FindByUserIdQuery;
import com.ftm.server.application.query.FindUserByRoleQuery;
import com.ftm.server.domain.entity.Post;
import com.ftm.server.domain.entity.User;
import com.ftm.server.domain.enums.UserRole;
import jakarta.transaction.Transactional;
import java.time.LocalDateTime;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
Expand All @@ -24,10 +18,8 @@
public class UserExitService implements UserExitUseCase {

private final LoadUserPort loadUserPort;
private final LoadPostUserDomainPort loadPostPort;

private final UpdateUserPort updateUserPort;
private final UpdatePostUserDomainPort updatePostPort;

@Transactional
@Override
Expand All @@ -38,16 +30,5 @@ public void execute(DeleteUserByIdCommand query) {
user.updateIsDeleted(true);
user.updateDeletedAt(LocalDateTime.now());
updateUserPort.updateUser(user);

// user가 쓴 게시글의 작성자를 익명 사용자로 변경
List<Post> postList = loadPostPort.loadPostListByUser(FindByUserIdQuery.of(user.getId()));
if (!postList.isEmpty()) {
// 익명 사용자 조회
User systemUser = loadUserPort.loadUserByRole(FindUserByRoleQuery.of(UserRole.SYSTEM));
Long systemUserId = systemUser.getId();
// post update
postList.forEach(p -> p.updateUserId(systemUserId));
updatePostPort.updatePostListBySystemUser(postList);
}
}
}
Loading
Loading