Skip to content

Commit

Permalink
YEL-184 [feat] 유저 탈퇴 사유 저장 v2
Browse files Browse the repository at this point in the history
YEL-184 [feat] 유저 탈퇴 사유 저장 v2
  • Loading branch information
hyeonjeongs authored Jan 29, 2024
2 parents 7fc4149 + 47c1699 commit 0320681
Show file tree
Hide file tree
Showing 12 changed files with 161 additions and 42 deletions.
35 changes: 8 additions & 27 deletions src/docs/asciidoc/user-data-post.adoc
Original file line number Diff line number Diff line change
@@ -1,40 +1,20 @@
:reproducible:
== 탈퇴 사유 저장 (명세)
== 탈퇴 & 사유 저장 v2

=== 요청

[http]
----
POST /api/v1/user/data/withdraw-reason HTTP/1.1
Authorization: Bearer your-access-token
Content-Type: application-json
include::{snippets}/api/v2/user/http-request.adoc[]

{
"value": "오류가 많아서"
}
----
=== 응답

*필드 타입*
include::{snippets}/api/v2/user/http-response.adoc[]

- "tag": "withdraw-reason" | "account-update-at" | "recommended"
- "value": String
* withdraw-reason 자리가 ENUM으로 대체될 예정입니다.

=== 응답
*필드 타입*

[http,json]
----
HTTP/1.1 200 OK
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json
- "value": String
* value는 탈퇴 사유를 보내주시면 됩니다.

{
"status" : 200,
"message" : "탈퇴 사유 정보 저장에 성공하였습니다."
}
----

*필드 타입*

Expand All @@ -46,4 +26,5 @@ Content-Type: application/json

=== CHANGELOG

- 2024.01.27 API 릴리즈
- 2024.01.09 명세 작성
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import static com.yello.server.global.common.SuccessCode.READ_USER_SUCCESS;
import static com.yello.server.global.common.SuccessCode.UPDATE_DEVICE_TOKEN_USER_SUCCESS;

import com.yello.server.domain.user.dto.request.UserDeleteReasonRequest;
import com.yello.server.domain.user.dto.request.UserDeviceTokenRequest;
import com.yello.server.domain.user.dto.response.UserDetailResponse;
import com.yello.server.domain.user.dto.response.UserDetailV2Response;
Expand Down Expand Up @@ -70,4 +71,11 @@ public BaseResponse<UserSubscribeDetailResponse> getUserSubscribe(@AccessTokenUs
val data = userService.getUserSubscribe(user.getId());
return BaseResponse.success(READ_USER_SUBSCRIBE_SUCCESS, data);
}

@DeleteMapping("/v2/user")
public BaseResponse deleteUserWithReason(@AccessTokenUser User user, @RequestBody
UserDeleteReasonRequest request) {
userService.deleteUserWithReason(user.getId(), request);
return BaseResponse.success(DELETE_USER_SUCCESS);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.yello.server.domain.user.dto.request;

import lombok.Builder;

@Builder
public record UserDeleteReasonRequest(
String value
) {

}
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,12 @@ public class UserData {

@Column(nullable = false)
private String value;

public static UserData of(UserDataType tag, String value, User user) {
return UserData.builder()
.tag(tag)
.user(user)
.value(value)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.yello.server.domain.user.repository;

import com.yello.server.domain.user.entity.UserData;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserDataJpaRepository extends JpaRepository<UserData, Long> {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.yello.server.domain.user.repository;

import com.yello.server.domain.user.entity.UserData;

public interface UserDataRepository {

UserData save(UserData userData);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.yello.server.domain.user.repository;


import com.querydsl.jpa.impl.JPAQueryFactory;
import com.yello.server.domain.user.entity.UserData;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

@Repository
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class UserDataRepositoryImpl implements UserDataRepository{

private final UserDataJpaRepository userDataJpaRepository;
private final JPAQueryFactory jpaQueryFactory;
@Override
public UserData save(UserData userData) {
return userDataJpaRepository.save(userData);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.yello.server.domain.user.service;

import static com.yello.server.domain.user.entity.UserDataType.*;
import static com.yello.server.global.common.ErrorCode.DEVICE_TOKEN_CONFLICT_USER_EXCEPTION;

import com.yello.server.domain.admin.repository.UserAdminRepository;
Expand All @@ -10,13 +11,17 @@
import com.yello.server.domain.group.repository.UserGroupRepository;
import com.yello.server.domain.purchase.entity.Purchase;
import com.yello.server.domain.purchase.repository.PurchaseRepository;
import com.yello.server.domain.user.dto.request.UserDeleteReasonRequest;
import com.yello.server.domain.user.dto.request.UserDeviceTokenRequest;
import com.yello.server.domain.user.dto.response.UserDetailResponse;
import com.yello.server.domain.user.dto.response.UserDetailV2Response;
import com.yello.server.domain.user.dto.response.UserResponse;
import com.yello.server.domain.user.dto.response.UserSubscribeDetailResponse;
import com.yello.server.domain.user.entity.User;
import com.yello.server.domain.user.entity.UserData;
import com.yello.server.domain.user.entity.UserDataType;
import com.yello.server.domain.user.exception.UserConflictException;
import com.yello.server.domain.user.repository.UserDataRepository;
import com.yello.server.domain.user.repository.UserRepository;
import com.yello.server.domain.vote.repository.VoteRepository;
import com.yello.server.global.common.dto.EmptyObject;
Expand All @@ -40,6 +45,7 @@ public class UserService {
private final UserGroupRepository userGroupRepository;
private final UserRepository userRepository;
private final VoteRepository voteRepository;
private final UserDataRepository userDataRepository;

public UserDetailResponse findMyProfile(Long userId) {
final User user = userRepository.getById(userId);
Expand Down Expand Up @@ -98,4 +104,22 @@ public UserSubscribeDetailResponse getUserSubscribe(Long userId) {

return UserSubscribeDetailResponse.of(purchase);
}

@Transactional
public void deleteUserWithReason(Long userId, UserDeleteReasonRequest request) {
final User target = userRepository.getById(userId);
target.delete();

friendRepository.findAllByUserId(target.getId())
.forEach(Friend::delete);

friendRepository.findAllByTargetId(target.getId())
.forEach(Friend::delete);

cooldownRepository.findByUserId(target.getId())
.ifPresent(Cooldown::delete);

userDataRepository.save(UserData.of(WITHDRAW_REASON, request.value(), target));

}
}
2 changes: 1 addition & 1 deletion src/main/resources/static/docs/check-vote-available.html
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ <h3 id="_응답">응답</h3>
"data" : {
"isPossible" : false,
"point" : 200,
"createdAt" : "2024-01-27 15:32:41",
"createdAt" : "2024-01-27 17:42:50",
"friendStatus" : 1
}
}</code></pre>
Expand Down
10 changes: 10 additions & 0 deletions src/main/resources/static/docs/find-notice.html
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,16 @@ <h3 id="_응답">응답</h3>
<li>
<p>"isAvailable": Boolean</p>
</li>
<li>
<p>"type" : String</p>
<div class="ulist">
<ul>
<li>
<p>ENUM 값 &#8594; "NOTICE" | "BANNER"</p>
</li>
</ul>
</div>
</li>
</ul>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/static/docs/google.html
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,7 @@ <h3 id="_응답">응답</h3>
"message" : "구글 구독 결제 검증 및 반영에 성공하였습니다.",
"data" : {
"productId" : "productId",
"expiredAt" : "2024-01-27T15:32:31.145173"
"expiredAt" : "2024-01-27T17:42:38.846267"
}
}</code></pre>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.doNothing;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.removeHeaders;
Expand All @@ -17,6 +18,7 @@
import com.yello.server.domain.authorization.filter.JwtFilter;
import com.yello.server.domain.group.entity.UserGroupType;
import com.yello.server.domain.user.controller.UserController;
import com.yello.server.domain.user.dto.request.UserDeleteReasonRequest;
import com.yello.server.domain.user.dto.request.UserDeviceTokenRequest;
import com.yello.server.domain.user.dto.response.UserDetailResponse;
import com.yello.server.domain.user.dto.response.UserDetailV2Response;
Expand Down Expand Up @@ -63,8 +65,9 @@
class UserControllerTest {

final String[] excludeRequestHeaders = {"X-CSRF-TOKEN", "Host"};
final String[] excludeResponseHeaders = {"X-Content-Type-Options", "X-XSS-Protection", "Cache-Control", "Pragma",
"Expires", "X-Frame-Options", "Content-Length"};
final String[] excludeResponseHeaders =
{"X-Content-Type-Options", "X-XSS-Protection", "Cache-Control", "Pragma",
"Expires", "X-Frame-Options", "Content-Length"};

@Autowired
private MockMvc mockMvc;
Expand Down Expand Up @@ -107,8 +110,10 @@ void init() {
)
.andDo(print())
.andDo(document("api/v1/user/findUser",
Preprocessors.preprocessRequest(prettyPrint(), removeHeaders(excludeRequestHeaders)),
Preprocessors.preprocessResponse(prettyPrint(), removeHeaders(excludeResponseHeaders)))
Preprocessors.preprocessRequest(prettyPrint(),
removeHeaders(excludeRequestHeaders)),
Preprocessors.preprocessResponse(prettyPrint(),
removeHeaders(excludeResponseHeaders)))
)
.andExpect(MockMvcResultMatchers.status().isOk());
}
Expand All @@ -132,8 +137,10 @@ void init() {
)
.andDo(print())
.andDo(document("api/v2/user",
Preprocessors.preprocessRequest(prettyPrint(), removeHeaders(excludeRequestHeaders)),
Preprocessors.preprocessResponse(prettyPrint(), removeHeaders(excludeResponseHeaders)))
Preprocessors.preprocessRequest(prettyPrint(),
removeHeaders(excludeRequestHeaders)),
Preprocessors.preprocessResponse(prettyPrint(),
removeHeaders(excludeResponseHeaders)))
)
.andExpect(MockMvcResultMatchers.status().isOk());
}
Expand Down Expand Up @@ -162,8 +169,10 @@ void init() {
)
.andDo(print())
.andDo(document("api/v1/user/findUserById",
Preprocessors.preprocessRequest(prettyPrint(), removeHeaders(excludeRequestHeaders)),
Preprocessors.preprocessResponse(prettyPrint(), removeHeaders(excludeResponseHeaders)),
Preprocessors.preprocessRequest(prettyPrint(),
removeHeaders(excludeRequestHeaders)),
Preprocessors.preprocessResponse(prettyPrint(),
removeHeaders(excludeResponseHeaders)),
pathParameters(parameterWithName("userId").description("유저 아이디 값")))
)
.andExpect(MockMvcResultMatchers.status().isOk());
Expand All @@ -189,8 +198,10 @@ void init() {
.content(objectMapper.writeValueAsString(userDeviceTokenRequest)))
.andDo(print())
.andDo(document("api/v1/user/updateUserDeviceToken",
Preprocessors.preprocessRequest(prettyPrint(), removeHeaders(excludeRequestHeaders)),
Preprocessors.preprocessResponse(prettyPrint(), removeHeaders(excludeResponseHeaders)))
Preprocessors.preprocessRequest(prettyPrint(),
removeHeaders(excludeRequestHeaders)),
Preprocessors.preprocessResponse(prettyPrint(),
removeHeaders(excludeResponseHeaders)))
)
.andExpect(MockMvcResultMatchers.status().isOk());
}
Expand All @@ -206,8 +217,10 @@ void init() {
)
.andDo(print())
.andDo(document("api/v1/user/deleteUser",
Preprocessors.preprocessRequest(prettyPrint(), removeHeaders(excludeRequestHeaders)),
Preprocessors.preprocessResponse(prettyPrint(), removeHeaders(excludeResponseHeaders)))
Preprocessors.preprocessRequest(prettyPrint(),
removeHeaders(excludeRequestHeaders)),
Preprocessors.preprocessResponse(prettyPrint(),
removeHeaders(excludeResponseHeaders)))
)
.andExpect(MockMvcResultMatchers.status().isOk());
}
Expand All @@ -216,7 +229,8 @@ void init() {
void 유저_구독_정보_조회에_성공합니다() throws Exception {
// given

final UserSubscribeDetailResponse userSubscribeDetailResponse = UserSubscribeDetailResponse.of(testDataUtil.generatePurchase(1L, user));
final UserSubscribeDetailResponse userSubscribeDetailResponse =
UserSubscribeDetailResponse.of(testDataUtil.generatePurchase(1L, user));
// when
given(userService.getUserSubscribe(anyLong()))
.willReturn(userSubscribeDetailResponse);
Expand All @@ -237,4 +251,30 @@ void init() {

}

@Test
void 유저_탈퇴_v2_성공합니다() throws Exception {
// given
final UserDeleteReasonRequest request =
UserDeleteReasonRequest.builder().value("오류가 많아서").build();
/*
doNothing()
.when(userService)
.deleteUserWithReason(anyLong(), request);*/
// when
// then
mockMvc.perform(RestDocumentationRequestBuilders.delete("/api/v2/user")
.with(csrf().asHeader())
.header(HttpHeaders.AUTHORIZATION, "Bearer your-access-token")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andDo(print())
.andDo(document("api/v2/user",
Preprocessors.preprocessRequest(prettyPrint(),
removeHeaders(excludeRequestHeaders)),
Preprocessors.preprocessResponse(prettyPrint(),
removeHeaders(excludeResponseHeaders)))
)
.andExpect(MockMvcResultMatchers.status().isOk());
}

}

0 comments on commit 0320681

Please sign in to comment.