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
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ dependencies {
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

//이미지 타입 검사 라이브러리
implementation 'org.apache.tika:tika-core:2.5.0'
}

jar.enabled = false
Expand Down
17 changes: 17 additions & 0 deletions src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,23 @@ Content-Type: application/json

---

== 이미지 파일 요청

원본 이미지 url 예시 : https://{cdn-url}/users/default-image.jpg

썸네일 이미지 url 예시 : https://{cdn-url}/users/default-image.jpg?w=100&h=100

==== 썸네일 이미지 요청 Query Parameter

[cols="4,4,4,4,4", options="header"]
|===
| param | description | type | required | 비고
| w | 가로 크기 | int | true |
| h | 세로 크기 | int | true |
| f | 반환 이미지 format | string | false (default : jpg) | jpg, png, jpeg
|===


= **회원**

include::user-api.adoc[]
Expand Down
42 changes: 41 additions & 1 deletion src/docs/asciidoc/user-api.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -121,4 +121,44 @@ include::{snippetsDir}/socialUserSignUp/1/response-fields.adoc[]
실패1.
include::{snippetsDir}/socialUserSignUp/2/http-response.adoc[]
실패 2
include::{snippetsDir}/socialUserSignUp/3/http-response.adoc[]
include::{snippetsDir}/socialUserSignUp/3/http-response.adoc[]


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

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

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

==== 성공 Response
include::{snippetsDir}/userSimpleInfo/1/http-response.adoc[]

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


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

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

==== Request
include::{snippetsDir}/userInfoUpdate/1/curl-request.adoc[]

==== Request Parts
include::{snippetsDir}/userInfoUpdate/1/request-parts.adoc[]

==== Request Parts : **data** - Detail Fields
include::{snippetsDir}/userInfoUpdate/1/request-part-data-fields.adoc[]

==== 성공 Response
include::{snippetsDir}/userInfoUpdate/1/http-response.adoc[]

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

==== 실패 Response
실패1.
include::{snippetsDir}/userInfoUpdate/2/http-response.adoc[]
실패 2
include::{snippetsDir}/userInfoUpdate/3/http-response.adoc[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.ftm.server.adapter.in.web.user.controller;

import com.ftm.server.adapter.in.web.user.dto.response.GetUserSimpleInfoResponse;
import com.ftm.server.application.port.in.user.GetUserSimpleInfoUseCase;
import com.ftm.server.application.query.FindByUserIdQuery;
import com.ftm.server.application.vo.user.UserWithImageVo;
import com.ftm.server.common.response.ApiResponse;
import com.ftm.server.common.response.enums.SuccessResponseCode;
import com.ftm.server.infrastructure.security.UserPrincipal;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class GetUserSimpleInfoController {
private final GetUserSimpleInfoUseCase getUserSimpleInfoUseCase;

@GetMapping("/api/users/info/simple")
public ResponseEntity<ApiResponse<GetUserSimpleInfoResponse>> getUserInfo(
@AuthenticationPrincipal UserPrincipal userPrincipal) {
UserWithImageVo userWithImageVo =
getUserSimpleInfoUseCase.execute(FindByUserIdQuery.of(userPrincipal.getId()));
return ResponseEntity.status(HttpStatus.OK)
.body(
ApiResponse.success(
SuccessResponseCode.OK,
GetUserSimpleInfoResponse.from(userWithImageVo)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.ftm.server.adapter.in.web.user.controller;

import com.ftm.server.adapter.in.web.user.dto.request.UpdateUserInfoRequest;
import com.ftm.server.adapter.in.web.user.dto.response.UpdateUserInfoResponse;
import com.ftm.server.application.command.user.UpdateUserCommand;
import com.ftm.server.application.port.in.user.UpdateUserInfoUseCase;
import com.ftm.server.application.vo.user.UserWithImageVo;
import com.ftm.server.common.response.ApiResponse;
import com.ftm.server.common.response.enums.SuccessResponseCode;
import com.ftm.server.infrastructure.security.UserPrincipal;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

@RestController
@RequiredArgsConstructor
@Slf4j
public class UpdateUserInfoController {

private final UpdateUserInfoUseCase updateUserInfoUseCase;

@PatchMapping("api/users/info")
public ResponseEntity<ApiResponse<UpdateUserInfoResponse>> updateUserInfo(
@RequestPart(value = "data") UpdateUserInfoRequest request,
@RequestPart(value = "imageFile", required = false) MultipartFile imageFile,
@AuthenticationPrincipal UserPrincipal userPrincipal) {
UpdateUserCommand updateUserCommand =
UpdateUserCommand.from(userPrincipal.getId(), request, imageFile);
UserWithImageVo userWithImageVo = updateUserInfoUseCase.execute(updateUserCommand);
return ResponseEntity.status(HttpStatus.OK)
.body(
ApiResponse.success(
SuccessResponseCode.OK,
UpdateUserInfoResponse.from(userWithImageVo)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.ftm.server.adapter.in.web.user.dto.request;

import com.ftm.server.domain.enums.AgeGroup;
import com.ftm.server.domain.enums.HashTag;
import lombok.Data;

@Data
public class UpdateUserInfoRequest {
private final String nickname;
private final AgeGroup age;
private final HashTag[] hashtags;
private final String imageAction;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.ftm.server.adapter.in.web.user.dto.response;

import com.ftm.server.application.vo.user.UserWithImageVo;
import com.ftm.server.common.consts.PropertiesHolder;
import com.ftm.server.domain.entity.User;
import com.ftm.server.domain.entity.UserImage;
import com.ftm.server.domain.enums.AgeGroup;
import com.ftm.server.domain.enums.HashTag;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import lombok.Data;

@Data
public class GetUserSimpleInfoResponse {
private final Long userId;
private final String userNickname;
private final String imageUrl;
private final AgeInfo ageInfo;
private final List<HashTagInfo> hashTagInfo;

public static GetUserSimpleInfoResponse from(UserWithImageVo userWithImageVo) {
User user = userWithImageVo.getUser();
UserImage userImage = userWithImageVo.getUserImage();

String imageUrl = PropertiesHolder.CDN_PATH + "/" + userImage.getObjectKey();

HashTag[] userHashTagWithArray = user.getFavoriteHashtags();
List<HashTag> userHashTag =
userHashTagWithArray == null || userHashTagWithArray.length == 0
? new ArrayList<>()
: Arrays.stream(user.getFavoriteHashtags()).toList();
List<HashTagInfo> hashTagInfos = new ArrayList<>();
for (HashTag hashTag : HashTag.values()) {
if (userHashTag.contains(hashTag)) {
hashTagInfos.add(HashTagInfo.from(hashTag, true));
} else {
hashTagInfos.add(HashTagInfo.from(hashTag, false));
}
}
return new GetUserSimpleInfoResponse(
user.getId(),
user.getNickname(),
imageUrl,
AgeInfo.from(user.getAgeGroup()),
hashTagInfos);
}

private record AgeInfo(String value, String description) {
private static AgeInfo from(AgeGroup ageGroup) {
return new AgeInfo(ageGroup.name(), ageGroup.getValue());
}
}

private record HashTagInfo(String value, String description, Boolean isSelected) {
private static HashTagInfo from(HashTag hashTag, Boolean isSelected) {
return new HashTagInfo(hashTag.name(), hashTag.getValue(), isSelected);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.ftm.server.adapter.in.web.user.dto.response;

import com.ftm.server.application.vo.user.UserWithImageVo;
import com.ftm.server.common.consts.PropertiesHolder;
import com.ftm.server.domain.entity.User;
import com.ftm.server.domain.entity.UserImage;
import com.ftm.server.domain.enums.AgeGroup;
import com.ftm.server.domain.enums.HashTag;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import lombok.Data;

@Data
public class UpdateUserInfoResponse {
private final Long userId;
private final String userNickname;
private final String imageUrl;
private final AgeInfo ageInfo;
private final List<HashTagInfo> hashTagInfo;

public static UpdateUserInfoResponse from(UserWithImageVo userWithImageVo) {
User user = userWithImageVo.getUser();
UserImage userImage = userWithImageVo.getUserImage();

String imageUrl = PropertiesHolder.CDN_PATH + "/" + userImage.getObjectKey();
HashTag[] userHashTagWithArray = user.getFavoriteHashtags();
List<HashTag> userHashTag =
userHashTagWithArray == null || userHashTagWithArray.length == 0
? new ArrayList<>()
: Arrays.stream(user.getFavoriteHashtags()).toList();
List<HashTagInfo> hashTagInfos = new ArrayList<>();
for (HashTag hashTag : HashTag.values()) {
if (userHashTag.contains(hashTag)) {
hashTagInfos.add(HashTagInfo.from(hashTag, true));
} else {
hashTagInfos.add(HashTagInfo.from(hashTag, false));
}
}
return new UpdateUserInfoResponse(
user.getId(),
user.getNickname(),
imageUrl,
AgeInfo.from(user.getAgeGroup()),
hashTagInfos);
}

private record AgeInfo(String value, String description) {
private static AgeInfo from(AgeGroup ageGroup) {
return new AgeInfo(ageGroup.name(), ageGroup.getValue());
}
}

private record HashTagInfo(String value, String description, Boolean isSelected) {
private static HashTagInfo from(HashTag hashTag, Boolean isSelected) {
return new HashTagInfo(hashTag.name(), hashTag.getValue(), isSelected);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.ftm.server.adapter.out.persistence.mapper.UserImageMapper;
import com.ftm.server.adapter.out.persistence.mapper.UserMapper;
import com.ftm.server.adapter.out.persistence.model.EmailVerificationLogsJpaEntity;
import com.ftm.server.adapter.out.persistence.model.GroomingLevelJpaEntity;
import com.ftm.server.adapter.out.persistence.model.UserImageJpaEntity;
import com.ftm.server.adapter.out.persistence.model.UserJpaEntity;
import com.ftm.server.adapter.out.persistence.repository.EmailVerificationLogsRepository;
Expand All @@ -14,6 +15,7 @@
import com.ftm.server.application.port.out.persistence.user.*;
import com.ftm.server.application.query.*;
import com.ftm.server.common.annotation.Adapter;
import com.ftm.server.common.exception.CustomException;
import com.ftm.server.domain.entity.EmailVerificationLogs;
import com.ftm.server.domain.entity.User;
import com.ftm.server.domain.entity.UserImage;
Expand All @@ -24,13 +26,17 @@
@Adapter
@RequiredArgsConstructor
@Slf4j
public class UserDomainPersistenceAdapterForAuthForAuth
public class UserDomainPersistenceAdapter
implements LoadEmailVerificationLogPort,
SaveEmailVerificationLogPort,
UpdateEmailVerificationLogPort,
CheckUserPort,
SaveUserPort,
SaveUserImagePort {
SaveUserImagePort,
LoadUserPort,
LoadUserImagePort,
UpdateUserInfoPort,
UpdateUserImagePort {

// repository
private final EmailVerificationLogsRepository emailVerificationLogsRepository;
Expand Down Expand Up @@ -104,4 +110,50 @@ public User saveSocialUser(User user) {
UserJpaEntity savedUser = userRepository.save(userJpaEntity);
return userMapper.toDomainEntity(savedUser);
}

@Override
public User loadUserById(FindByUserIdQuery query) {
UserJpaEntity userJpaEntity =
userRepository
.findById(query.getUserId())
.orElseThrow(() -> CustomException.USER_NOT_FOUND);
return userMapper.toDomainEntity(userJpaEntity);
}

@Override
public UserImage loadUserImageByUserId(FindByUserIdQuery query) {
UserImageJpaEntity userImageJpaEntity =
userImageRepository.findByUserId(query.getUserId()).orElse(null);
if (userImageJpaEntity != null) {
return userImageMapper.toDomainEntity(userImageJpaEntity);
}
return null;
}

@Override
public void updateUserInfo(User user) {
UserJpaEntity savedUser =
userRepository
.findById(user.getId())
.orElseThrow(() -> CustomException.USER_NOT_FOUND);
GroomingLevelJpaEntity groomingLevelJpaEntity =
user.getGroomingLevelId() == null
? null
: groomingLevelRepository.findById(user.getGroomingLevelId()).orElse(null);

savedUser.updateFromDomainEntity(user, groomingLevelJpaEntity);

userRepository.save(savedUser);
}

@Override
public void updateUserImage(UserImage userImage) {
UserImageJpaEntity userImageJpaEntity =
userImageRepository.findByUserId(userImage.getUserId()).orElse(null);
if (userImageJpaEntity == null) {
log.error("[USER_IMAGE_NOT_FOUND] : 사용자의 이미지 data를 찾을 수 없음.");
}

userImageJpaEntity.updateFromDomainEntity(userImage);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,8 @@ public static UserImageJpaEntity from(UserImage userImage, UserJpaEntity userJpa
public static UserImageJpaEntity createUserImage(UserJpaEntity user) {
return UserImageJpaEntity.builder().user(user).objectKey("users/default-image.png").build();
}

public void updateFromDomainEntity(UserImage userImage) {
this.objectKey = userImage.getObjectKey();
}
}
Loading