Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨Feat: UserController 구현 (#22) #23

Merged
merged 4 commits into from
Oct 13, 2023
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
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ dependencies {
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'

// Swagger dependency
implementation 'io.springfox:springfox-boot-starter:3.0.0'
implementation 'io.springfox:springfox-swagger-ui:3.0.0'
}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,91 @@
package com.diareat.diareat.user.controller;

import com.diareat.diareat.user.dto.*;
import com.diareat.diareat.user.service.UserService;
import com.diareat.diareat.util.api.ApiResponse;
import com.diareat.diareat.util.api.ResponseCode;
import io.swagger.annotations.Api;
import io.swagger.v3.oas.annotations.Operation;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.RestController;
import net.bytebuddy.implementation.bind.annotation.Empty;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@Api(tags = "1. User")
@RequiredArgsConstructor
@RestController
@RequestMapping("/api/user")
public class UserController {

private final UserService userService;

// 회원정보 저장
@Operation(summary = "[회원가입] 회원정보 저장", description = "신규 회원정보를 저장합니다.")
@PostMapping("/save")
public ApiResponse<Long> saveUser(CreateUserDto createUserDto) {
return ApiResponse.success(userService.saveUser(createUserDto), ResponseCode.USER_CREATE_SUCCESS.getMessage());
}

// 회원 기본정보 조회
@Operation(summary = "[프로필] 회원 기본정보 조회", description = "회원 기본정보를 조회합니다.")
@GetMapping("{userId}/info/simple/")
public ApiResponse<ResponseSimpleUserDto> getSimpleUserInfo(@PathVariable Long userId) {
return ApiResponse.success(userService.getSimpleUserInfo(userId), ResponseCode.USER_CREATE_SUCCESS.getMessage());
}

// 회원정보 조회
@Operation(summary = "[프로필] 회원 정보 조회", description = "회원 정보를 조회합니다.")
@GetMapping("{userId}/info")
public ApiResponse<ResponseUserDto> getUserInfo(@PathVariable Long userId) {
return ApiResponse.success(userService.getUserInfo(userId), ResponseCode.USER_CREATE_SUCCESS.getMessage());
}

// 회원정보 수정
@Operation(summary = "[프로필] 회원 정보 수정", description = "회원 정보를 수정합니다.")
@PutMapping("/update")
public ApiResponse<Void> updateUserInfo(UpdateUserDto updateUserDto) {
userService.updateUserInfo(updateUserDto);
return ApiResponse.success(null, ResponseCode.USER_UPDATE_SUCCESS.getMessage());
}

// 회원 기준섭취량 조회
@Operation(summary = "[프로필] 회원 기준섭취량 조회", description = "회원 기준섭취량을 조회합니다.")
@GetMapping("{userId}/nutrition")
public ApiResponse<ResponseUserNutritionDto> getUserNutrition(@PathVariable Long userId) {
return ApiResponse.success(userService.getUserNutrition(userId), ResponseCode.USER_READ_SUCCESS.getMessage());
}

// 회원 기준섭취량 직접 수정
@Operation(summary = "[프로필] 회원 기준섭취량 직접 수정", description = "회원 기준섭취량을 직접 수정합니다.")
@PutMapping("{userId}/nutrition")
public ApiResponse<Void> updateUserNutrition(UpdateUserNutritionDto updateUserNutritionDto) {
userService.updateBaseNutrition(updateUserNutritionDto);
return ApiResponse.success(null, ResponseCode.USER_UPDATE_SUCCESS.getMessage());
}

// 회원의 친구 검색 결과 조회
@Operation(summary = "[주간 랭킹] 회원의 친구 검색 결과 조회", description = "회원의 친구 검색 결과를 조회합니다.")
@GetMapping("{userId}/search/{name}")
public ApiResponse<List<ResponseSearchUserDto>> searchUser(@PathVariable Long userId, @RequestParam String name) {
return ApiResponse.success(userService.searchUser(userId, name), ResponseCode.USER_SEARCH_SUCCESS.getMessage());
}

// 실제 팔로잉 유저들의 점수 계산하여 랭킹 형태로 반환하는 API: FoodService에서 계산할지 UserService에서 FoodRepository를 콜해서 처리할지 협의 필요

// 회원이 특정 회원 팔로우
@Operation(summary = "[주간 랭킹] 회원이 특정 회원 팔로우", description = "회원이 특정 회원을 팔로우합니다.")
@PostMapping("{userId}/follow/{followId}")
public ApiResponse<Void> followUser(@PathVariable Long userId, @PathVariable Long followId) {
userService.followUser(userId, followId);
return ApiResponse.success(null, ResponseCode.USER_UPDATE_SUCCESS.getMessage());
}

// 회원이 특정 회원 팔로우 취소
@Operation(summary = "[주간 랭킹] 회원이 특정 회원 팔로우 취소", description = "회원이 특정 회원을 팔로우 취소합니다.")
@DeleteMapping("{userId}/follow/{followId}")
public ApiResponse<Void> unfollowUser(@PathVariable Long userId, @PathVariable Long followId) {
userService.unfollowUser(userId, followId);
return ApiResponse.success(null, ResponseCode.USER_UPDATE_SUCCESS.getMessage());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@

public interface UserRepository extends JpaRepository<User, Long> {
List<User> findAllByNameContaining(String name); // 회원이 팔로우를 위해 검색한 유저 목록 조회
boolean existsByName(String name); // 회원가입 시 닉네임 중복 확인
}
19 changes: 18 additions & 1 deletion src/main/java/com/diareat/diareat/user/service/UserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ public class UserService {
public Long saveUser(CreateUserDto createUserDto) {
BaseNutrition baseNutrition = BaseNutrition.createNutrition(2000, 300, 80, 80);
// BaseNutrition baseNutrition = BaseNutrition.createNutrition(createUserDto.getGender(), createUserDto.getAge(), createUserDto.getHeight(), createUserDto.getWeight());
if (userRepository.existsByName(createUserDto.getName()))
throw new UserException(ResponseCode.USER_ALREADY_EXIST);
User user = User.createUser(createUserDto.getName(), createUserDto.getKeyCode(), createUserDto.getHeight(), createUserDto.getWeight(), createUserDto.getGender(), createUserDto.getAge(), baseNutrition);
return userRepository.save(user).getId();
}
Expand Down Expand Up @@ -89,6 +91,9 @@ public List<ResponseSearchUserDto> searchUser(Long hostId, String name) {
public void followUser(Long userId, Long followId) {
validateUser(userId);
validateUser(followId);
// 이미 팔로우 중인 경우
if (followRepository.existsByFromUserAndToUser(userId, followId))
throw new UserException(ResponseCode.FOLLOWED_ALREADY);
followRepository.save(Follow.makeFollow(userId, followId));
}

Expand All @@ -97,11 +102,23 @@ public void followUser(Long userId, Long followId) {
public void unfollowUser(Long userId, Long unfollowId) {
validateUser(userId);
validateUser(unfollowId);
// 이미 팔로우 취소한 경우
if (followRepository.existsByFromUserAndToUser(userId, unfollowId))
throw new UserException(ResponseCode.UNFOLLOWED_ALREADY);
followRepository.delete(Follow.makeFollow(userId, unfollowId));
}

// 회원의 팔로우 목록 조회 (현재 외부 Dto 변환은 Food에서 위임받아 진행할지 협의하지 않았기에 일단 User 리스트로 반환)
@Transactional(readOnly = true)
public List<User> getFollowList(Long userId) {
validateUser(userId);
List<User> users = followRepository.findAllByFromUser(userId);
users.add(getUserById(userId)); // 자기 자신도 랭킹에 포함
return users;
}

private void validateUser(Long userId) {
if(!userRepository.existsById(userId))
if (!userRepository.existsById(userId))
throw new UserException(ResponseCode.USER_NOT_FOUND);
}

Expand Down
39 changes: 33 additions & 6 deletions src/main/java/com/diareat/diareat/util/api/ResponseCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,42 @@ public enum ResponseCode {
FORBIDDEN(HttpStatus.FORBIDDEN, false, "권한이 없습니다."),

// 404 Not Found
USER_NOT_FOUND(HttpStatus.NOT_FOUND, false,"사용자를 찾을 수 없습니다."),
FOOD_NOT_FOUND(HttpStatus.NOT_FOUND, false,"음식을 찾을 수 없습니다."),
FAVORITE_NOT_FOUND(HttpStatus.NOT_FOUND, false,"즐겨찾기를 찾을 수 없습니다."),
USER_NOT_FOUND(HttpStatus.NOT_FOUND, false, "사용자를 찾을 수 없습니다."),
FOOD_NOT_FOUND(HttpStatus.NOT_FOUND, false, "음식을 찾을 수 없습니다."),
FAVORITE_NOT_FOUND(HttpStatus.NOT_FOUND, false, "즐겨찾기를 찾을 수 없습니다."),

// 405 Method Not Allowed
METHOD_NOT_ALLOWED(HttpStatus.METHOD_NOT_ALLOWED, false,"허용되지 않은 메소드입니다."),
METHOD_NOT_ALLOWED(HttpStatus.METHOD_NOT_ALLOWED, false, "허용되지 않은 메소드입니다."),

// 409 Conflict
USER_ALREADY_EXIST(HttpStatus.CONFLICT, false, "이미 존재하는 닉네임입니다."),
FOLLOWED_ALREADY(HttpStatus.CONFLICT, false, "이미 팔로우한 사용자입니다."),
UNFOLLOWED_ALREADY(HttpStatus.CONFLICT, false, "이미 언팔로우한 사용자입니다."),
FOOD_ALREADY_EXIST(HttpStatus.CONFLICT, false, "이미 즐겨찾기에 존재하는 음식입니다."),

// 500 Internal Server Error
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, false,"서버에 오류가 발생하였습니다.");
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, false, "서버에 오류가 발생하였습니다."),

// 200 OK
USER_READ_SUCCESS(HttpStatus.CREATED, true, "사용자 정보 조회 성공"),
USER_UPDATE_SUCCESS(HttpStatus.OK, true, "사용자 정보 수정 성공"),
USER_SEARCH_SUCCESS(HttpStatus.OK, true, "사용자 검색 성공"),
USER_FOLLOW_SUCCESS(HttpStatus.OK, true, "사용자 팔로우 성공"),
USER_UNFOLLOW_SUCCESS(HttpStatus.OK, true, "사용자 언팔로우 성공"),

FOOD_READ_SUCCESS(HttpStatus.OK, true, "음식 정보 조회 성공"),
FOOD_UPDATE_SUCCESS(HttpStatus.OK, true, "음식 정보 수정 성공"),
FOOD_DELETE_SUCCESS(HttpStatus.OK, true, "음식 정보 삭제 성공"),
FOOD_FAVORITE_READ_SUCCESS(HttpStatus.OK, true, "즐겨찾기 음식 조회 성공"),
FOOD_FAVORITE_UPDATE_SUCCESS(HttpStatus.OK, true, "즐겨찾기 음식 수정 성공"),
FOOD_FAVORITE_DELETE_SUCCESS(HttpStatus.OK, true, "즐겨찾기 음식 삭제 성공"),


// 201 Created
USER_CREATE_SUCCESS(HttpStatus.CREATED, true, "사용자 생성 성공"),
FOOD_CREATE_SUCCESS(HttpStatus.CREATED, true, "음식 생성 성공"),
FOOD_FAVORITE_CREATE_SUCCESS(HttpStatus.OK, true, "즐겨찾기 음식 생성 성공");


private final HttpStatus httpStatus;
private final Boolean success;
Expand All @@ -33,4 +60,4 @@ public enum ResponseCode {
public int getHttpStatusCode() {
return httpStatus.value();
}
}
}
3 changes: 3 additions & 0 deletions src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
# db
spring.profiles.include = db

# swagger
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
Loading