diff --git a/build.gradle b/build.gradle index 7dd46b61..d92528d1 100644 --- a/build.gradle +++ b/build.gradle @@ -55,6 +55,9 @@ dependencies { // Feign implementation 'org.springframework.cloud:spring-cloud-starter-openfeign:4.1.2' + + // Redis + implementation 'org.springframework.boot:spring-boot-starter-data-redis' } tasks.named('test') { diff --git a/src/main/java/org/hankki/hankkiserver/api/user/controller/UserController.java b/src/main/java/org/hankki/hankkiserver/api/user/controller/UserController.java new file mode 100644 index 00000000..b41cc82e --- /dev/null +++ b/src/main/java/org/hankki/hankkiserver/api/user/controller/UserController.java @@ -0,0 +1,34 @@ +package org.hankki.hankkiserver.api.user.controller; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.hankki.hankkiserver.api.dto.HankkiResponse; +import org.hankki.hankkiserver.api.user.controller.request.UserUniversityPostRequest; +import org.hankki.hankkiserver.api.user.service.UserCommandService; +import org.hankki.hankkiserver.api.user.service.UserQueryService; +import org.hankki.hankkiserver.api.user.service.command.UserUniversityPostCommand; +import org.hankki.hankkiserver.api.user.service.response.UserUniversityFindResponse; +import org.hankki.hankkiserver.auth.UserId; +import org.hankki.hankkiserver.common.code.CommonSuccessCode; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/v1") +@RequiredArgsConstructor +public class UserController { + + private final UserCommandService userCommandService; + private final UserQueryService userQueryService; + + @PostMapping("/users/me/university") + public HankkiResponse postUserUniversity(@UserId final Long userId, + @Valid @RequestBody final UserUniversityPostRequest request) { + userCommandService.saveUserUniversity(new UserUniversityPostCommand(userId, request)); + return HankkiResponse.success(CommonSuccessCode.CREATED); + } + + @GetMapping("/users/me/university") + public HankkiResponse findUserUniversity(@UserId final Long userId) { + return HankkiResponse.success(CommonSuccessCode.OK, userQueryService.findUserUniversity(userId)); + } +} diff --git a/src/main/java/org/hankki/hankkiserver/api/user/controller/request/UserUniversityPostRequest.java b/src/main/java/org/hankki/hankkiserver/api/user/controller/request/UserUniversityPostRequest.java new file mode 100644 index 00000000..1de3d460 --- /dev/null +++ b/src/main/java/org/hankki/hankkiserver/api/user/controller/request/UserUniversityPostRequest.java @@ -0,0 +1,7 @@ +package org.hankki.hankkiserver.api.user.controller.request; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +public record UserUniversityPostRequest(long universityId, @NotBlank @Size(max = 5) String name, double longitude, double latitude) { +} diff --git a/src/main/java/org/hankki/hankkiserver/api/user/service/UserCommandService.java b/src/main/java/org/hankki/hankkiserver/api/user/service/UserCommandService.java new file mode 100644 index 00000000..b62e30c4 --- /dev/null +++ b/src/main/java/org/hankki/hankkiserver/api/user/service/UserCommandService.java @@ -0,0 +1,22 @@ +package org.hankki.hankkiserver.api.user.service; + +import lombok.RequiredArgsConstructor; +import org.hankki.hankkiserver.api.user.service.command.UserUniversityPostCommand; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class UserCommandService { + + private final UserUniversityUpdater userUniversityUpdater; + private final UserUniversityDeleter userUniversityDeleter; + private final UserUniversityFinder userUniversityFinder; + + @Transactional + public void saveUserUniversity(UserUniversityPostCommand userUniversityPostCommand) { + userUniversityFinder.findByUserId(userUniversityPostCommand.userId()) + .ifPresent(userUniversity -> userUniversityDeleter.deleteById(userUniversity.getId())); + userUniversityUpdater.save(userUniversityPostCommand.toEntity()); + } +} diff --git a/src/main/java/org/hankki/hankkiserver/api/user/service/UserQueryService.java b/src/main/java/org/hankki/hankkiserver/api/user/service/UserQueryService.java new file mode 100644 index 00000000..0edbf3cf --- /dev/null +++ b/src/main/java/org/hankki/hankkiserver/api/user/service/UserQueryService.java @@ -0,0 +1,21 @@ +package org.hankki.hankkiserver.api.user.service; + +import lombok.RequiredArgsConstructor; +import org.hankki.hankkiserver.api.user.service.response.UserUniversityFindResponse; +import org.hankki.hankkiserver.common.code.UserUniversityErrorCode; +import org.hankki.hankkiserver.common.exception.NotFoundException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class UserQueryService { + + private final UserUniversityFinder userUniversityFinder; + + @Transactional(readOnly = true) + public UserUniversityFindResponse findUserUniversity(Long userId) { + return UserUniversityFindResponse.of(userUniversityFinder.findByUserId(userId) + .orElseThrow(() -> new NotFoundException(UserUniversityErrorCode.USER_UNIVERSITY_NOT_FOUND))); + } +} diff --git a/src/main/java/org/hankki/hankkiserver/api/user/service/UserUniversityDeleter.java b/src/main/java/org/hankki/hankkiserver/api/user/service/UserUniversityDeleter.java new file mode 100644 index 00000000..747438dc --- /dev/null +++ b/src/main/java/org/hankki/hankkiserver/api/user/service/UserUniversityDeleter.java @@ -0,0 +1,20 @@ +package org.hankki.hankkiserver.api.user.service; + +import lombok.RequiredArgsConstructor; +import org.hankki.hankkiserver.domain.user.repository.UserUniversityRepository; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class UserUniversityDeleter { + + private final UserUniversityRepository userUniversityRepository; + + protected void deleteById(Long id) { + userUniversityRepository.deleteById(id); + } + + protected void deleteByUserId(Long userId) { + userUniversityRepository.deleteByUserId(userId); + } +} diff --git a/src/main/java/org/hankki/hankkiserver/api/user/service/UserUniversityFinder.java b/src/main/java/org/hankki/hankkiserver/api/user/service/UserUniversityFinder.java new file mode 100644 index 00000000..56f920b8 --- /dev/null +++ b/src/main/java/org/hankki/hankkiserver/api/user/service/UserUniversityFinder.java @@ -0,0 +1,19 @@ +package org.hankki.hankkiserver.api.user.service; + +import lombok.RequiredArgsConstructor; +import org.hankki.hankkiserver.domain.user.model.UserUniversity; +import org.hankki.hankkiserver.domain.user.repository.UserUniversityRepository; +import org.springframework.stereotype.Component; + +import java.util.Optional; + +@Component +@RequiredArgsConstructor +public class UserUniversityFinder { + + private final UserUniversityRepository userUniversityRepository; + + public Optional findByUserId(Long userId) { + return userUniversityRepository.findByUserId(userId); + } +} diff --git a/src/main/java/org/hankki/hankkiserver/api/user/service/UserUniversityUpdater.java b/src/main/java/org/hankki/hankkiserver/api/user/service/UserUniversityUpdater.java new file mode 100644 index 00000000..dd15d8db --- /dev/null +++ b/src/main/java/org/hankki/hankkiserver/api/user/service/UserUniversityUpdater.java @@ -0,0 +1,17 @@ +package org.hankki.hankkiserver.api.user.service; + +import lombok.RequiredArgsConstructor; +import org.hankki.hankkiserver.domain.user.model.UserUniversity; +import org.hankki.hankkiserver.domain.user.repository.UserUniversityRepository; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class UserUniversityUpdater { + + private final UserUniversityRepository userUniversityRepository; + + protected void save(UserUniversity userUniversity) { + userUniversityRepository.save(userUniversity).getUserId(); + } +} diff --git a/src/main/java/org/hankki/hankkiserver/api/user/service/command/UserUniversityPostCommand.java b/src/main/java/org/hankki/hankkiserver/api/user/service/command/UserUniversityPostCommand.java new file mode 100644 index 00000000..84636716 --- /dev/null +++ b/src/main/java/org/hankki/hankkiserver/api/user/service/command/UserUniversityPostCommand.java @@ -0,0 +1,22 @@ +package org.hankki.hankkiserver.api.user.service.command; + +import org.hankki.hankkiserver.api.user.controller.request.UserUniversityPostRequest; +import org.hankki.hankkiserver.domain.user.model.UserUniversity; + +public record UserUniversityPostCommand(Long userId, Long universityId, String universityName, + double longitude, double latitude) { + public UserUniversityPostCommand(Long userId, UserUniversityPostRequest request) { + this(userId, request.universityId(), request.name(), request.longitude(), request.latitude()); + } + + public UserUniversity toEntity() { + return UserUniversity.builder() + .userId(userId) + .universityId(universityId) + .universityName(universityName) + .longitude(longitude) + .latitude(latitude) + .build(); + } +} + diff --git a/src/main/java/org/hankki/hankkiserver/api/user/service/response/UserUniversityFindResponse.java b/src/main/java/org/hankki/hankkiserver/api/user/service/response/UserUniversityFindResponse.java new file mode 100644 index 00000000..ef947a13 --- /dev/null +++ b/src/main/java/org/hankki/hankkiserver/api/user/service/response/UserUniversityFindResponse.java @@ -0,0 +1,13 @@ +package org.hankki.hankkiserver.api.user.service.response; + +import org.hankki.hankkiserver.domain.user.model.UserUniversity; + +public record UserUniversityFindResponse(long id, + String name, + double longitude, + double latitude) { + public static UserUniversityFindResponse of (UserUniversity userUniversity) { + return new UserUniversityFindResponse(userUniversity.getUniversityId(), userUniversity.getUniversityName(), + userUniversity.getLongitude(), userUniversity.getLatitude()); + } +} diff --git a/src/main/java/org/hankki/hankkiserver/common/code/UserUniversityErrorCode.java b/src/main/java/org/hankki/hankkiserver/common/code/UserUniversityErrorCode.java new file mode 100644 index 00000000..70f0189d --- /dev/null +++ b/src/main/java/org/hankki/hankkiserver/common/code/UserUniversityErrorCode.java @@ -0,0 +1,14 @@ +package org.hankki.hankkiserver.common.code; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; + +@Getter +@RequiredArgsConstructor +public enum UserUniversityErrorCode implements ErrorCode { + USER_UNIVERSITY_NOT_FOUND(HttpStatus.NOT_FOUND, "대학 정보를 찾을 수 없습니다."); + + private final HttpStatus httpStatus; + private final String message; +} diff --git a/src/main/java/org/hankki/hankkiserver/domain/user/model/UserUniversity.java b/src/main/java/org/hankki/hankkiserver/domain/user/model/UserUniversity.java new file mode 100644 index 00000000..5bf33e52 --- /dev/null +++ b/src/main/java/org/hankki/hankkiserver/domain/user/model/UserUniversity.java @@ -0,0 +1,30 @@ +package org.hankki.hankkiserver.domain.user.model; + +import jakarta.persistence.Id; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import org.springframework.data.redis.core.RedisHash; +import org.springframework.data.redis.core.index.Indexed; + +@RedisHash(value = "UserUniversity", timeToLive = 60 * 60 * 24 * 1000L * 30) +@Builder +@AllArgsConstructor +@Getter +public class UserUniversity { + + @Id + private Long id; + + @Indexed + private Long userId; + + private Long universityId; + + private String universityName; + + private Double longitude; + + private Double latitude; + +} \ No newline at end of file diff --git a/src/main/java/org/hankki/hankkiserver/domain/user/repository/UserUniversityRepository.java b/src/main/java/org/hankki/hankkiserver/domain/user/repository/UserUniversityRepository.java new file mode 100644 index 00000000..cc60ccc1 --- /dev/null +++ b/src/main/java/org/hankki/hankkiserver/domain/user/repository/UserUniversityRepository.java @@ -0,0 +1,12 @@ +package org.hankki.hankkiserver.domain.user.repository; + +import org.hankki.hankkiserver.domain.user.model.UserUniversity; +import org.springframework.data.repository.CrudRepository; + +import java.util.Optional; + +public interface UserUniversityRepository extends CrudRepository { + Optional findByUserId(Long userId); + void deleteById(Long id); + void deleteByUserId(Long userId); +}