diff --git a/src/main/java/org/atdev/artrip/domain/auth/data/User.java b/src/main/java/org/atdev/artrip/domain/auth/data/User.java index 03e909a..45e41a5 100644 --- a/src/main/java/org/atdev/artrip/domain/auth/data/User.java +++ b/src/main/java/org/atdev/artrip/domain/auth/data/User.java @@ -42,6 +42,9 @@ public class User { @Column(name = "stamp_num") private Byte stampNum; + @Column(name = "profile_image_Url") + private String profileImageUrl; + @Column(name = "nick_name") private String nickName; @@ -72,4 +75,12 @@ public boolean updateUserInfo(String name, String email) { return changed; } + + public void updateNickname(String nickName) { + this.nickName=nickName.trim(); + } + + public void updateProfileImage(String url){ + this.profileImageUrl=url; + } } diff --git a/src/main/java/org/atdev/artrip/domain/auth/repository/UserRepository.java b/src/main/java/org/atdev/artrip/domain/auth/repository/UserRepository.java index e4174c5..edcd686 100644 --- a/src/main/java/org/atdev/artrip/domain/auth/repository/UserRepository.java +++ b/src/main/java/org/atdev/artrip/domain/auth/repository/UserRepository.java @@ -20,4 +20,6 @@ public interface UserRepository extends JpaRepository { @Query("SELECT u FROM User u JOIN FETCH u.socialAccounts sa WHERE sa.provider = :provider AND sa.providerId = :providerId") Optional findBySocialAccountsProviderAndProviderId(@Param("provider") Provider provider, @Param("providerId") String providerId); + + boolean existsByNickName(String nickname); } diff --git a/src/main/java/org/atdev/artrip/domain/exhibit/repository/ExhibitRepositoryCustom.java b/src/main/java/org/atdev/artrip/domain/exhibit/repository/ExhibitRepositoryCustom.java index bd23c50..81fd2c7 100644 --- a/src/main/java/org/atdev/artrip/domain/exhibit/repository/ExhibitRepositoryCustom.java +++ b/src/main/java/org/atdev/artrip/domain/exhibit/repository/ExhibitRepositoryCustom.java @@ -1,9 +1,9 @@ package org.atdev.artrip.domain.exhibit.repository; import org.atdev.artrip.domain.exhibit.data.Exhibit; -import org.atdev.artrip.domain.exhibit.web.dto.request.ExhibitFilterRequestDto; +import org.atdev.artrip.domain.exhibit.web.dto.request.ExhibitFilterRequest; import org.atdev.artrip.domain.home.response.HomeListResponse; -import org.atdev.artrip.domain.home.web.dto.RandomExhibitRequest; +import org.atdev.artrip.domain.home.web.dto.request.RandomExhibitRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.stereotype.Repository; @@ -13,7 +13,7 @@ @Repository public interface ExhibitRepositoryCustom { - Slice findExhibitByFilters(ExhibitFilterRequestDto filter, Pageable pageable, Long cursorId); + Slice findExhibitByFilters(ExhibitFilterRequest filter, Pageable pageable, Long cursorId); List findRandomExhibits(RandomExhibitRequest condition); diff --git a/src/main/java/org/atdev/artrip/domain/exhibit/repository/ExhibitRepositoryImpl.java b/src/main/java/org/atdev/artrip/domain/exhibit/repository/ExhibitRepositoryImpl.java index c5e3e18..4023e47 100644 --- a/src/main/java/org/atdev/artrip/domain/exhibit/repository/ExhibitRepositoryImpl.java +++ b/src/main/java/org/atdev/artrip/domain/exhibit/repository/ExhibitRepositoryImpl.java @@ -11,17 +11,16 @@ import org.atdev.artrip.domain.Enum.Status; import org.atdev.artrip.domain.exhibit.data.Exhibit; import org.atdev.artrip.domain.exhibit.data.QExhibit; -import org.atdev.artrip.domain.exhibit.web.dto.request.ExhibitFilterRequestDto; +import org.atdev.artrip.domain.exhibit.web.dto.request.ExhibitFilterRequest; import org.atdev.artrip.domain.exhibitHall.data.QExhibitHall; import org.atdev.artrip.domain.favortie.data.QFavoriteExhibit; import org.atdev.artrip.domain.home.response.HomeListResponse; -import org.atdev.artrip.domain.home.web.dto.RandomExhibitRequest; +import org.atdev.artrip.domain.home.web.dto.request.RandomExhibitRequest; import org.atdev.artrip.domain.keyword.data.QKeyword; import org.springframework.data.domain.*; import org.springframework.stereotype.Repository; import java.time.LocalDate; -import java.time.LocalDateTime; import java.util.List; import java.util.Set; @@ -32,7 +31,7 @@ public class ExhibitRepositoryImpl implements ExhibitRepositoryCustom{ private final JPAQueryFactory queryFactory; @Override - public Slice findExhibitByFilters(ExhibitFilterRequestDto dto, Pageable pageable, Long cursorId) { + public Slice findExhibitByFilters(ExhibitFilterRequest dto, Pageable pageable, Long cursorId) { QExhibit e = QExhibit.exhibit; QExhibitHall h = QExhibitHall.exhibitHall; @@ -145,7 +144,7 @@ private BooleanExpression cursorCondition(Exhibit cursor, long cursorFavoriteCo }; } - private OrderSpecifier[] sortFilter(ExhibitFilterRequestDto dto, QExhibit e, QFavoriteExhibit f) { + private OrderSpecifier[] sortFilter(ExhibitFilterRequest dto, QExhibit e, QFavoriteExhibit f) { if (dto.getSortType() == null) { return new OrderSpecifier[]{e.createdAt.desc()}; diff --git a/src/main/java/org/atdev/artrip/domain/exhibit/web/controller/ExhibitController.java b/src/main/java/org/atdev/artrip/domain/exhibit/web/controller/ExhibitController.java index f5b4b0d..6284cf6 100644 --- a/src/main/java/org/atdev/artrip/domain/exhibit/web/controller/ExhibitController.java +++ b/src/main/java/org/atdev/artrip/domain/exhibit/web/controller/ExhibitController.java @@ -4,9 +4,8 @@ import lombok.RequiredArgsConstructor; import org.atdev.artrip.domain.exhibit.reponse.ExhibitDetailResponse; import org.atdev.artrip.domain.exhibit.service.ExhibitService; -import org.atdev.artrip.domain.exhibit.web.dto.request.ExhibitFilterRequestDto; +import org.atdev.artrip.domain.exhibit.web.dto.request.ExhibitFilterRequest; import org.atdev.artrip.domain.home.response.FilterResponse; -import org.atdev.artrip.domain.home.response.HomeListResponse; import org.atdev.artrip.domain.home.service.HomeService; import org.atdev.artrip.global.apipayload.CommonResponse; import org.atdev.artrip.global.apipayload.code.status.CommonError; @@ -14,13 +13,9 @@ import org.atdev.artrip.global.swagger.ApiErrorResponses; import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; -import org.springframework.format.annotation.DateTimeFormat; import org.springframework.http.ResponseEntity; -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.security.core.userdetails.UserDetails; import org.springframework.web.bind.annotation.*; -import java.time.LocalDate; import java.util.List; @RestController @@ -89,7 +84,7 @@ public ResponseEntity>> getDomestic(){ home = {HomeError._HOME_INVALID_DATE_RANGE, HomeError._HOME_UNRECOGNIZED_REGION, HomeError._HOME_EXHIBIT_NOT_FOUND} ) @PostMapping("/filter") - public ResponseEntity getDomesticFilter(@RequestBody ExhibitFilterRequestDto dto, + public ResponseEntity getDomesticFilter(@RequestBody ExhibitFilterRequest dto, @RequestParam(required = false) Long cursor, @PageableDefault(size = 20) Pageable pageable){ diff --git a/src/main/java/org/atdev/artrip/domain/exhibit/web/dto/request/ExhibitFilterRequestDto.java b/src/main/java/org/atdev/artrip/domain/exhibit/web/dto/request/ExhibitFilterRequest.java similarity index 92% rename from src/main/java/org/atdev/artrip/domain/exhibit/web/dto/request/ExhibitFilterRequestDto.java rename to src/main/java/org/atdev/artrip/domain/exhibit/web/dto/request/ExhibitFilterRequest.java index c74c120..8e9e9c8 100644 --- a/src/main/java/org/atdev/artrip/domain/exhibit/web/dto/request/ExhibitFilterRequestDto.java +++ b/src/main/java/org/atdev/artrip/domain/exhibit/web/dto/request/ExhibitFilterRequest.java @@ -9,7 +9,7 @@ @Builder @Data -public class ExhibitFilterRequestDto { +public class ExhibitFilterRequest { private LocalDate startDate; private LocalDate endDate; diff --git a/src/main/java/org/atdev/artrip/domain/favortie/data/FavoriteExhibit.java b/src/main/java/org/atdev/artrip/domain/favortie/data/FavoriteExhibit.java index 968fe04..9468c8e 100644 --- a/src/main/java/org/atdev/artrip/domain/favortie/data/FavoriteExhibit.java +++ b/src/main/java/org/atdev/artrip/domain/favortie/data/FavoriteExhibit.java @@ -26,6 +26,7 @@ public class FavoriteExhibit { private User user; @Column(nullable = false) + @Builder.Default private boolean status = false; @ManyToOne(fetch = FetchType.LAZY) diff --git a/src/main/java/org/atdev/artrip/domain/home/converter/HomeConverter.java b/src/main/java/org/atdev/artrip/domain/home/converter/HomeConverter.java index e2735f8..b0dc1f7 100644 --- a/src/main/java/org/atdev/artrip/domain/home/converter/HomeConverter.java +++ b/src/main/java/org/atdev/artrip/domain/home/converter/HomeConverter.java @@ -3,9 +3,9 @@ import org.atdev.artrip.domain.Enum.KeywordType; import org.atdev.artrip.domain.exhibit.data.Exhibit; import org.atdev.artrip.domain.exhibit.reponse.ExhibitDetailResponse; -import org.atdev.artrip.domain.home.web.dto.*; import org.atdev.artrip.domain.home.response.FilterResponse; import org.atdev.artrip.domain.home.response.HomeListResponse; +import org.atdev.artrip.domain.home.web.dto.request.*; import org.atdev.artrip.domain.keyword.data.Keyword; import org.springframework.data.domain.Slice; import org.springframework.stereotype.Component; @@ -75,7 +75,7 @@ public ExhibitDetailResponse toHomeExhibitResponse(Exhibit exhibit) { } - public RandomExhibitRequest from(PersonalizedRequestDto request, List keywords) { + public RandomExhibitRequest from(PersonalizedRequest request, List keywords) { Set genres = keywords.stream() .filter(k -> k.getType() == KeywordType.GENRE) @@ -97,7 +97,7 @@ public RandomExhibitRequest from(PersonalizedRequestDto request, List k .build(); } - public RandomExhibitRequest from(ScheduleRandomRequestDto request) { + public RandomExhibitRequest from(ScheduleRandomRequest request) { return RandomExhibitRequest.builder() .isDomestic(request.getIsDomestic()) @@ -108,7 +108,7 @@ public RandomExhibitRequest from(ScheduleRandomRequestDto request) { .build(); } - public RandomExhibitRequest fromToday(TodayRandomRequestDto request) { + public RandomExhibitRequest fromToday(TodayRandomRequest request) { return RandomExhibitRequest.builder() .isDomestic(request.getIsDomestic()) .country(normalize(request.getCountry())) @@ -116,7 +116,7 @@ public RandomExhibitRequest fromToday(TodayRandomRequestDto request) { .limit(3) .build(); } - public RandomExhibitRequest fromGenre(GenreRandomRequestDto request) { + public RandomExhibitRequest fromGenre(GenreRandomRequest request) { return RandomExhibitRequest.builder() .isDomestic(request.getIsDomestic()) .country(normalize(request.getCountry())) diff --git a/src/main/java/org/atdev/artrip/domain/home/service/HomeService.java b/src/main/java/org/atdev/artrip/domain/home/service/HomeService.java index 8f3b1a3..9a29c5c 100644 --- a/src/main/java/org/atdev/artrip/domain/home/service/HomeService.java +++ b/src/main/java/org/atdev/artrip/domain/home/service/HomeService.java @@ -1,22 +1,19 @@ package org.atdev.artrip.domain.home.service; import lombok.RequiredArgsConstructor; -import org.atdev.artrip.domain.Enum.KeywordType; import org.atdev.artrip.domain.auth.repository.UserRepository; import org.atdev.artrip.domain.exhibit.data.Exhibit; -import org.atdev.artrip.domain.exhibit.reponse.ExhibitDetailResponse; -import org.atdev.artrip.domain.exhibit.web.dto.request.ExhibitFilterRequestDto; -import org.atdev.artrip.domain.home.web.dto.*; +import org.atdev.artrip.domain.exhibit.web.dto.request.ExhibitFilterRequest; import org.atdev.artrip.domain.exhibitHall.repository.ExhibitHallRepository; import org.atdev.artrip.domain.home.converter.HomeConverter; import org.atdev.artrip.domain.home.response.FilterResponse; import org.atdev.artrip.domain.exhibit.repository.ExhibitRepository; import org.atdev.artrip.domain.home.response.HomeListResponse; +import org.atdev.artrip.domain.home.web.dto.request.*; import org.atdev.artrip.domain.keyword.data.Keyword; import org.atdev.artrip.domain.keyword.data.UserKeyword; import org.atdev.artrip.domain.keyword.repository.UserKeywordRepository; -import org.atdev.artrip.global.apipayload.code.status.ExhibitError; import org.atdev.artrip.global.apipayload.code.status.UserError; import org.atdev.artrip.global.apipayload.exception.GeneralException; @@ -25,10 +22,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.time.LocalDate; import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; @Service @RequiredArgsConstructor @@ -68,7 +62,7 @@ public List getDomestic(){ //필터 전체 조회 - public FilterResponse getFilterExhibit(ExhibitFilterRequestDto dto, Pageable pageable, Long cursorId) { + public FilterResponse getFilterExhibit(ExhibitFilterRequest dto, Pageable pageable, Long cursorId) { Slice slice = exhibitRepository.findExhibitByFilters(dto, pageable, cursorId); @@ -77,7 +71,7 @@ public FilterResponse getFilterExhibit(ExhibitFilterRequestDto dto, Pageable pag // 사용자 맞춤 전시 랜덤 추천 @Transactional - public List getRandomPersonalized(Long userId, PersonalizedRequestDto request){ + public List getRandomPersonalized(Long userId, PersonalizedRequest request){ if (!userRepository.existsById(userId)) { throw new GeneralException(UserError._USER_NOT_FOUND); @@ -106,7 +100,7 @@ public List getRandomPersonalized(Long userId, PersonalizedReq } // 이번주 랜덤 전시 추천 - public List getRandomSchedule(ScheduleRandomRequestDto request){ + public List getRandomSchedule(ScheduleRandomRequest request){ RandomExhibitRequest filter = homeConverter.from(request); List results = exhibitRepository.findRandomExhibits(filter); @@ -122,7 +116,7 @@ public List getRandomSchedule(ScheduleRandomRequestDto request } // 장르별 전시 랜덤 추천 - public List getRandomGenre(GenreRandomRequestDto request){ + public List getRandomGenre(GenreRandomRequest request){ RandomExhibitRequest filter = homeConverter.fromGenre(request); @@ -139,7 +133,7 @@ public List getRandomGenre(GenreRandomRequestDto request){ } // 오늘날 전시 랜덤 추천 - public List getRandomToday(TodayRandomRequestDto request){ + public List getRandomToday(TodayRandomRequest request){ RandomExhibitRequest filter = homeConverter.fromToday(request); diff --git a/src/main/java/org/atdev/artrip/domain/home/web/controller/HomeController.java b/src/main/java/org/atdev/artrip/domain/home/web/controller/HomeController.java index 8da1b29..109d67b 100644 --- a/src/main/java/org/atdev/artrip/domain/home/web/controller/HomeController.java +++ b/src/main/java/org/atdev/artrip/domain/home/web/controller/HomeController.java @@ -3,9 +3,12 @@ import io.swagger.v3.oas.annotations.Operation; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; -import org.atdev.artrip.domain.home.web.dto.*; import org.atdev.artrip.domain.home.response.HomeListResponse; import org.atdev.artrip.domain.home.service.HomeService; +import org.atdev.artrip.domain.home.web.dto.request.GenreRandomRequest; +import org.atdev.artrip.domain.home.web.dto.request.PersonalizedRequest; +import org.atdev.artrip.domain.home.web.dto.request.ScheduleRandomRequest; +import org.atdev.artrip.domain.home.web.dto.request.TodayRandomRequest; import org.atdev.artrip.global.apipayload.CommonResponse; import org.atdev.artrip.global.apipayload.code.status.CommonError; import org.atdev.artrip.global.apipayload.code.status.HomeError; @@ -47,7 +50,7 @@ public class HomeController { @PostMapping("/personalized/random") public ResponseEntity>> getRandomPersonalized( @AuthenticationPrincipal UserDetails userDetails, - @Valid @RequestBody PersonalizedRequestDto requestDto){ + @Valid @RequestBody PersonalizedRequest requestDto){ long userId = Long.parseLong(userDetails.getUsername()); @@ -78,7 +81,7 @@ public ResponseEntity>> getRandomPersonali ) @PostMapping("/schedule") public ResponseEntity>> getRandomSchedule( - @Valid @RequestBody ScheduleRandomRequestDto request){ + @Valid @RequestBody ScheduleRandomRequest request){ List exhibits= homeService.getRandomSchedule(request); @@ -109,7 +112,7 @@ public ResponseEntity>> getRandomSchedule( ) @PostMapping("/genre/random") public ResponseEntity>> getRandomExhibits( - @Valid @RequestBody GenreRandomRequestDto request){ + @Valid @RequestBody GenreRandomRequest request){ List exhibits = homeService.getRandomGenre(request); return ResponseEntity.ok(CommonResponse.onSuccess(exhibits)); @@ -137,7 +140,7 @@ public ResponseEntity>> getRandomExhibits( ) @PostMapping("recommend/today") public ResponseEntity>> getTodayRecommendations( - @Valid @RequestBody TodayRandomRequestDto request){ + @Valid @RequestBody TodayRandomRequest request){ List exhibits = homeService.getRandomToday(request); diff --git a/src/main/java/org/atdev/artrip/domain/home/web/dto/GenreRandomRequestDto.java b/src/main/java/org/atdev/artrip/domain/home/web/dto/GenreRandomRequestDto.java deleted file mode 100644 index e0841c6..0000000 --- a/src/main/java/org/atdev/artrip/domain/home/web/dto/GenreRandomRequestDto.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.atdev.artrip.domain.home.web.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.AssertTrue; -import jakarta.validation.constraints.NotNull; -import lombok.*; - -@Getter -@Setter -@NoArgsConstructor -@AllArgsConstructor -@Builder -public class GenreRandomRequestDto extends BaseRandomRequestDto{ - - @NotNull - private String singleGenre; -} diff --git a/src/main/java/org/atdev/artrip/domain/home/web/dto/PersonalizedRequestDto.java b/src/main/java/org/atdev/artrip/domain/home/web/dto/PersonalizedRequestDto.java deleted file mode 100644 index dcc4a99..0000000 --- a/src/main/java/org/atdev/artrip/domain/home/web/dto/PersonalizedRequestDto.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.atdev.artrip.domain.home.web.dto; - -import lombok.*; - -@Getter -@Setter -@NoArgsConstructor -@Builder -public class PersonalizedRequestDto extends BaseRandomRequestDto{ - -} diff --git a/src/main/java/org/atdev/artrip/domain/home/web/dto/ScheduleRandomRequestDto.java b/src/main/java/org/atdev/artrip/domain/home/web/dto/ScheduleRandomRequestDto.java deleted file mode 100644 index f5a471a..0000000 --- a/src/main/java/org/atdev/artrip/domain/home/web/dto/ScheduleRandomRequestDto.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.atdev.artrip.domain.home.web.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.AssertTrue; -import jakarta.validation.constraints.NotNull; -import lombok.*; - -import java.time.LocalDate; - -@Getter -@Setter -@NoArgsConstructor -@AllArgsConstructor -@Builder -public class ScheduleRandomRequestDto extends BaseRandomRequestDto { - - @NotNull - private LocalDate date; - -} diff --git a/src/main/java/org/atdev/artrip/domain/home/web/dto/TodayRandomRequestDto.java b/src/main/java/org/atdev/artrip/domain/home/web/dto/TodayRandomRequestDto.java deleted file mode 100644 index 939ebce..0000000 --- a/src/main/java/org/atdev/artrip/domain/home/web/dto/TodayRandomRequestDto.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.atdev.artrip.domain.home.web.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.AssertTrue; -import jakarta.validation.constraints.NotNull; -import lombok.*; - -@Getter -@Setter -@NoArgsConstructor -@Builder -public class TodayRandomRequestDto extends BaseRandomRequestDto{ - -} - diff --git a/src/main/java/org/atdev/artrip/domain/home/web/dto/BaseRandomRequestDto.java b/src/main/java/org/atdev/artrip/domain/home/web/dto/request/BaseRandomRequest.java similarity index 92% rename from src/main/java/org/atdev/artrip/domain/home/web/dto/BaseRandomRequestDto.java rename to src/main/java/org/atdev/artrip/domain/home/web/dto/request/BaseRandomRequest.java index 706f753..7534342 100644 --- a/src/main/java/org/atdev/artrip/domain/home/web/dto/BaseRandomRequestDto.java +++ b/src/main/java/org/atdev/artrip/domain/home/web/dto/request/BaseRandomRequest.java @@ -1,4 +1,4 @@ -package org.atdev.artrip.domain.home.web.dto; +package org.atdev.artrip.domain.home.web.dto.request; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.AssertTrue; @@ -12,7 +12,7 @@ @Setter @NoArgsConstructor @AllArgsConstructor -public class BaseRandomRequestDto { +public class BaseRandomRequest { @NotNull protected Boolean isDomestic; diff --git a/src/main/java/org/atdev/artrip/domain/home/web/dto/request/GenreRandomRequest.java b/src/main/java/org/atdev/artrip/domain/home/web/dto/request/GenreRandomRequest.java new file mode 100644 index 0000000..283930f --- /dev/null +++ b/src/main/java/org/atdev/artrip/domain/home/web/dto/request/GenreRandomRequest.java @@ -0,0 +1,15 @@ +package org.atdev.artrip.domain.home.web.dto.request; + +import jakarta.validation.constraints.NotNull; +import lombok.*; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class GenreRandomRequest extends BaseRandomRequest { + + @NotNull + private String singleGenre; +} diff --git a/src/main/java/org/atdev/artrip/domain/home/web/dto/request/PersonalizedRequest.java b/src/main/java/org/atdev/artrip/domain/home/web/dto/request/PersonalizedRequest.java new file mode 100644 index 0000000..06dca73 --- /dev/null +++ b/src/main/java/org/atdev/artrip/domain/home/web/dto/request/PersonalizedRequest.java @@ -0,0 +1,11 @@ +package org.atdev.artrip.domain.home.web.dto.request; + +import lombok.*; + +@Getter +@Setter +@NoArgsConstructor +@Builder +public class PersonalizedRequest extends BaseRandomRequest { + +} diff --git a/src/main/java/org/atdev/artrip/domain/home/web/dto/RandomExhibitRequest.java b/src/main/java/org/atdev/artrip/domain/home/web/dto/request/RandomExhibitRequest.java similarity index 88% rename from src/main/java/org/atdev/artrip/domain/home/web/dto/RandomExhibitRequest.java rename to src/main/java/org/atdev/artrip/domain/home/web/dto/request/RandomExhibitRequest.java index faa742f..ea1b968 100644 --- a/src/main/java/org/atdev/artrip/domain/home/web/dto/RandomExhibitRequest.java +++ b/src/main/java/org/atdev/artrip/domain/home/web/dto/request/RandomExhibitRequest.java @@ -1,4 +1,4 @@ -package org.atdev.artrip.domain.home.web.dto; +package org.atdev.artrip.domain.home.web.dto.request; import lombok.*; diff --git a/src/main/java/org/atdev/artrip/domain/home/web/dto/request/ScheduleRandomRequest.java b/src/main/java/org/atdev/artrip/domain/home/web/dto/request/ScheduleRandomRequest.java new file mode 100644 index 0000000..f9d14e6 --- /dev/null +++ b/src/main/java/org/atdev/artrip/domain/home/web/dto/request/ScheduleRandomRequest.java @@ -0,0 +1,18 @@ +package org.atdev.artrip.domain.home.web.dto.request; + +import jakarta.validation.constraints.NotNull; +import lombok.*; + +import java.time.LocalDate; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class ScheduleRandomRequest extends BaseRandomRequest { + + @NotNull + private LocalDate date; + +} diff --git a/src/main/java/org/atdev/artrip/domain/home/web/dto/request/TodayRandomRequest.java b/src/main/java/org/atdev/artrip/domain/home/web/dto/request/TodayRandomRequest.java new file mode 100644 index 0000000..77803bd --- /dev/null +++ b/src/main/java/org/atdev/artrip/domain/home/web/dto/request/TodayRandomRequest.java @@ -0,0 +1,12 @@ +package org.atdev.artrip.domain.home.web.dto.request; + +import lombok.*; + +@Getter +@Setter +@NoArgsConstructor +@Builder +public class TodayRandomRequest extends BaseRandomRequest { + +} + diff --git a/src/main/java/org/atdev/artrip/domain/keyword/service/KeywordService.java b/src/main/java/org/atdev/artrip/domain/keyword/service/KeywordService.java index 08a74b8..2acfa31 100644 --- a/src/main/java/org/atdev/artrip/domain/keyword/service/KeywordService.java +++ b/src/main/java/org/atdev/artrip/domain/keyword/service/KeywordService.java @@ -1,20 +1,12 @@ package org.atdev.artrip.domain.keyword.service; import lombok.RequiredArgsConstructor; -import org.atdev.artrip.domain.auth.data.User; -import org.atdev.artrip.domain.auth.repository.UserRepository; -import org.atdev.artrip.domain.keyword.data.Keyword; -import org.atdev.artrip.domain.keyword.data.UserKeyword; import org.atdev.artrip.domain.keyword.repository.KeywordRepository; import org.atdev.artrip.domain.keyword.repository.UserKeywordRepository; import org.atdev.artrip.domain.keyword.web.dto.KeywordResponse; -import org.atdev.artrip.global.apipayload.code.status.CommonError; -import org.atdev.artrip.global.apipayload.code.status.UserError; -import org.atdev.artrip.global.apipayload.exception.GeneralException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.time.LocalDateTime; import java.util.List; import java.util.stream.Collectors; @@ -24,28 +16,6 @@ public class KeywordService { private final KeywordRepository keywordRepository; private final UserKeywordRepository userKeywordRepository; - private final UserRepository userRepository; - - @Transactional - public void saveUserKeywords(Long userId, List keywordIds) { - - User user = userRepository.findById(userId) - .orElseThrow(() -> new GeneralException(UserError._USER_NOT_FOUND)); - - userKeywordRepository.deleteByUser(user); - - List keywords = keywordRepository.findAllById(keywordIds); - - List userKeywords = keywords.stream() - .map(keyword -> UserKeyword.builder() - .user(user) - .keyword(keyword) - .createdAt(LocalDateTime.now()) - .build()) - .toList(); - - userKeywordRepository.saveAll(userKeywords); - } @Transactional public List getAllKeywords() { diff --git a/src/main/java/org/atdev/artrip/domain/keyword/web/controller/UserKeywordController.java b/src/main/java/org/atdev/artrip/domain/keyword/web/controller/UserKeywordController.java index 3b51f7d..d2883e3 100644 --- a/src/main/java/org/atdev/artrip/domain/keyword/web/controller/UserKeywordController.java +++ b/src/main/java/org/atdev/artrip/domain/keyword/web/controller/UserKeywordController.java @@ -48,22 +48,6 @@ public ResponseEntity>> getUserKeywords( return ResponseEntity.ok(CommonResponse.onSuccess(keywords)); } - @Operation(summary = "내 키워드 선택 저장", description = "내가 선택한 키워드 저장") - @ApiErrorResponses( - common = {CommonError._INTERNAL_SERVER_ERROR, CommonError._UNAUTHORIZED}, - keyword = {KeywordError._KEYWORD_INVALID_REQUEST, KeywordError._KEYWORD_SELECTION_LIMIT_EXCEEDED, KeywordError._KEYWORD_NOT_FOUND} - ) - @PostMapping("/keywords") - public ResponseEntity> saveUserKeywords( - @AuthenticationPrincipal UserDetails userDetails, - @RequestBody KeywordRequest request) { - - Long userId = Long.parseLong(userDetails.getUsername()); // subject → userId형변환 - - keywordService.saveUserKeywords(userId, request.getKeywordIds()); - return ResponseEntity.ok(CommonResponse.onSuccess(null)); - } - } diff --git a/src/main/java/org/atdev/artrip/domain/user/converter/UserConverter.java b/src/main/java/org/atdev/artrip/domain/user/converter/UserConverter.java new file mode 100644 index 0000000..49b2018 --- /dev/null +++ b/src/main/java/org/atdev/artrip/domain/user/converter/UserConverter.java @@ -0,0 +1,8 @@ +package org.atdev.artrip.domain.user.converter; + + +import org.springframework.stereotype.Component; + +@Component +public class UserConverter { +} diff --git a/src/main/java/org/atdev/artrip/domain/user/service/UserService.java b/src/main/java/org/atdev/artrip/domain/user/service/UserService.java new file mode 100644 index 0000000..98eb47e --- /dev/null +++ b/src/main/java/org/atdev/artrip/domain/user/service/UserService.java @@ -0,0 +1,161 @@ +package org.atdev.artrip.domain.user.service; + + +import lombok.RequiredArgsConstructor; +import org.atdev.artrip.domain.auth.data.User; +import org.atdev.artrip.domain.auth.repository.UserRepository; +import org.atdev.artrip.domain.keyword.data.Keyword; +import org.atdev.artrip.domain.keyword.data.UserKeyword; +import org.atdev.artrip.domain.keyword.repository.KeywordRepository; +import org.atdev.artrip.domain.keyword.repository.UserKeywordRepository; +import org.atdev.artrip.domain.user.web.dto.request.NicknameRequest; +import org.atdev.artrip.domain.user.web.dto.response.MypageResponse; +import org.atdev.artrip.domain.user.web.dto.response.NicknameResponse; +import org.atdev.artrip.global.apipayload.code.status.S3Error; +import org.atdev.artrip.global.apipayload.code.status.UserError; +import org.atdev.artrip.global.apipayload.exception.GeneralException; +import org.atdev.artrip.global.s3.S3Service; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import java.time.LocalDateTime; +import java.util.List; + + +@Service +@RequiredArgsConstructor +public class UserService { + + private final KeywordRepository keywordRepository; + private final UserKeywordRepository userKeywordRepository; + private final UserRepository userRepository; + private final S3Service s3Service; + + private static final String NICKNAME_REGEX = "^[a-zA-Z0-9가-힣]+$"; + + + @Transactional + public void saveUserKeywords(Long userId, List keywordIds) { + + User user = userRepository.findById(userId) + .orElseThrow(() -> new GeneralException(UserError._USER_NOT_FOUND)); + + userKeywordRepository.deleteByUser(user); + + List keywords = keywordRepository.findAllById(keywordIds); + + List userKeywords = keywords.stream() + .map(keyword -> UserKeyword.builder() + .user(user) + .keyword(keyword) + .createdAt(LocalDateTime.now()) + .build()) + .toList(); + + userKeywordRepository.saveAll(userKeywords); + } + + @Transactional + public NicknameResponse updateNickName(Long userId, NicknameRequest dto){ + + //1. 유저 검사 + //2. 닉네임 유효성 검사 + 공백 금지 + //3. 기존과 동일한지 체크 + //4. 중복 검사 + //5. 업뎃 후 반환 + User user = userRepository.findById(userId) + .orElseThrow(() -> new GeneralException(UserError._USER_NOT_FOUND)); + + String newNick = validateNickname(dto); + + if (newNick.equals(user.getNickName())) { + return new NicknameResponse(newNick); + } + + if (userRepository.existsByNickName(newNick)) { + throw new GeneralException(UserError._DUPLICATE_NICKNAME); + } + + user.updateNickname(newNick); + + return new NicknameResponse(newNick); + } + + private String validateNickname(NicknameRequest dto) { + + if (dto == null || dto.getNickName() == null) { + throw new GeneralException(UserError._NICKNAME_BAD_REQUEST); + } + + String nickname = dto.getNickName(); + + if (nickname.isBlank() || nickname.contains(" ")) { + throw new GeneralException(UserError._NICKNAME_BAD_REQUEST); + } + + if (!nickname.matches(NICKNAME_REGEX)) { + throw new GeneralException(UserError._NICKNAME_BAD_REQUEST); + } + + return nickname; + } + + @Transactional + public String updateProfileImg(Long userId, MultipartFile image){ + + User user = userRepository.findById(userId) + .orElseThrow(()-> new GeneralException(UserError._USER_NOT_FOUND)); + + if (image == null || image.isEmpty()) { + throw new GeneralException(UserError._PROFILE_IMAGE_NOT_EXIST); + } + + String newUrl; + try { + newUrl = s3Service.upload(image); + } catch (Exception e) { + throw new GeneralException(S3Error._IO_EXCEPTION_UPLOAD_FILE); + } + user.updateProfileImage(newUrl); + + String oldUrl = user.getProfileImageUrl(); + if (oldUrl != null && !oldUrl.isBlank()) { + try { + s3Service.delete(oldUrl); + } catch (Exception e) { + throw new GeneralException(S3Error._IO_EXCEPTION_DELETE_FILE); + } + } + return newUrl; + } + + @Transactional + public void deleteProfileImg(Long userId){ + + User user = userRepository.findById(userId) + .orElseThrow(()-> new GeneralException(UserError._USER_NOT_FOUND)); + + String oldUrl = user.getProfileImageUrl(); + + if (oldUrl != null && !oldUrl.isBlank()) { + try { + s3Service.delete(oldUrl); + } catch (Exception e) { + throw new GeneralException(S3Error._IO_EXCEPTION_UPLOAD_FILE); + } + } + user.updateProfileImage(null); + } + + @Transactional(readOnly = true) + public MypageResponse getMypage(Long userId){ + + User user = userRepository.findById(userId) + .orElseThrow(()-> new GeneralException(UserError._USER_NOT_FOUND)); + + return new MypageResponse(user.getNickName(), user.getProfileImageUrl()); + } + + +} diff --git a/src/main/java/org/atdev/artrip/domain/user/web/controller/UserController.java b/src/main/java/org/atdev/artrip/domain/user/web/controller/UserController.java new file mode 100644 index 0000000..1de7093 --- /dev/null +++ b/src/main/java/org/atdev/artrip/domain/user/web/controller/UserController.java @@ -0,0 +1,111 @@ +package org.atdev.artrip.domain.user.web.controller; + +import io.swagger.v3.oas.annotations.Operation; +import lombok.RequiredArgsConstructor; +import org.atdev.artrip.domain.keyword.web.dto.KeywordRequest; +import org.atdev.artrip.domain.user.service.UserService; +import org.atdev.artrip.domain.user.web.dto.request.NicknameRequest; +import org.atdev.artrip.domain.user.web.dto.response.MypageResponse; +import org.atdev.artrip.domain.user.web.dto.response.NicknameResponse; +import org.atdev.artrip.global.apipayload.CommonResponse; +import org.atdev.artrip.global.apipayload.code.status.CommonError; +import org.atdev.artrip.global.apipayload.code.status.KeywordError; +import org.atdev.artrip.global.apipayload.code.status.UserError; +import org.atdev.artrip.global.swagger.ApiErrorResponses; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/my") +public class UserController { + + private final UserService userService; + + @Operation(summary = "나의 취향 분석", description = "내가 선택한 키워드 선택 저장") + @ApiErrorResponses( + common = {CommonError._INTERNAL_SERVER_ERROR, CommonError._UNAUTHORIZED}, + keyword = {KeywordError._KEYWORD_INVALID_REQUEST, KeywordError._KEYWORD_SELECTION_LIMIT_EXCEEDED, KeywordError._KEYWORD_NOT_FOUND} + ) + @PostMapping("/keywords") + public ResponseEntity> saveUserKeywords( + @AuthenticationPrincipal UserDetails userDetails, + @RequestBody KeywordRequest request) { + + Long userId = Long.parseLong(userDetails.getUsername()); // subject → userId형변환 + + userService.saveUserKeywords(userId, request.getKeywordIds()); + return ResponseEntity.ok(CommonResponse.onSuccess(null)); + } + + @Operation(summary = "프로필 이미지 추가", description = "프로필 이미지를 추가합니다") + @PostMapping(value = "/profile", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @ApiErrorResponses( + common = {CommonError._INTERNAL_SERVER_ERROR, CommonError._UNAUTHORIZED}, + user = {UserError._PROFILE_IMAGE_NOT_EXIST,UserError._USER_NOT_FOUND} + ) + public ResponseEntity> getUpdateImage( + @AuthenticationPrincipal UserDetails userDetails, + @RequestPart("image") MultipartFile image){ + + Long userId = Long.parseLong(userDetails.getUsername()); + + userService.updateProfileImg(userId,image); + + return ResponseEntity.ok(CommonResponse.onSuccess("프로필 이미지 생성")); + } + + @Operation(summary = "프로필 이미지 삭제", description = "기본 프로필 이미지로 변경됩니다") + @DeleteMapping("/profile") + @ApiErrorResponses( + common = {CommonError._INTERNAL_SERVER_ERROR, CommonError._UNAUTHORIZED}, + user = {UserError._PROFILE_IMAGE_NOT_EXIST,UserError._USER_NOT_FOUND} + ) + public ResponseEntity> getDeleteImage( + @AuthenticationPrincipal UserDetails userDetails){ + + Long userId = Long.parseLong(userDetails.getUsername()); + + userService.deleteProfileImg(userId); + + return ResponseEntity.ok(CommonResponse.onSuccess("프로필 이미지 삭제")); + } + + @Operation(summary = "닉네임 설정", description = "공백 입력 불가") + @PatchMapping("/nickname") + @ApiErrorResponses( + common = {CommonError._INTERNAL_SERVER_ERROR, CommonError._UNAUTHORIZED}, + user = {UserError._DUPLICATE_NICKNAME,UserError._USER_NOT_FOUND,UserError._NICKNAME_BAD_REQUEST} + ) + public ResponseEntity> updateNickname( + @AuthenticationPrincipal UserDetails user, + @RequestBody NicknameRequest dto) { + + Long userId = Long.valueOf(user.getUsername()); + + NicknameResponse response = userService.updateNickName(userId, dto); + + return ResponseEntity.ok(CommonResponse.onSuccess(response)); + } + + @Operation(summary = "마이페이지 조회", description = "닉네임, 프로필 이미지 조회") + @GetMapping("/mypage") + @ApiErrorResponses( + common = {CommonError._INTERNAL_SERVER_ERROR, CommonError._UNAUTHORIZED}, + user = {UserError._USER_NOT_FOUND} + ) + public ResponseEntity> getMypage( + @AuthenticationPrincipal UserDetails user) { + + Long userId = Long.valueOf(user.getUsername()); + + MypageResponse response = userService.getMypage(userId); + + return ResponseEntity.ok(CommonResponse.onSuccess(response)); + } + +} diff --git a/src/main/java/org/atdev/artrip/domain/user/web/dto/request/NicknameRequest.java b/src/main/java/org/atdev/artrip/domain/user/web/dto/request/NicknameRequest.java new file mode 100644 index 0000000..4c4f325 --- /dev/null +++ b/src/main/java/org/atdev/artrip/domain/user/web/dto/request/NicknameRequest.java @@ -0,0 +1,14 @@ +package org.atdev.artrip.domain.user.web.dto.request; + +import lombok.*; + +@Builder +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class NicknameRequest { + + private String NickName; + +} diff --git a/src/main/java/org/atdev/artrip/domain/user/web/dto/response/MypageResponse.java b/src/main/java/org/atdev/artrip/domain/user/web/dto/response/MypageResponse.java new file mode 100644 index 0000000..53d5e10 --- /dev/null +++ b/src/main/java/org/atdev/artrip/domain/user/web/dto/response/MypageResponse.java @@ -0,0 +1,13 @@ +package org.atdev.artrip.domain.user.web.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class MypageResponse { + + private String nickName; + private String profileImage; + +} diff --git a/src/main/java/org/atdev/artrip/domain/user/web/dto/response/NicknameResponse.java b/src/main/java/org/atdev/artrip/domain/user/web/dto/response/NicknameResponse.java new file mode 100644 index 0000000..5b3e892 --- /dev/null +++ b/src/main/java/org/atdev/artrip/domain/user/web/dto/response/NicknameResponse.java @@ -0,0 +1,11 @@ +package org.atdev.artrip.domain.user.web.dto.response; + + +import lombok.*; + +@Getter +@AllArgsConstructor +public class NicknameResponse { + + private String nickName; +} diff --git a/src/main/java/org/atdev/artrip/global/apipayload/code/status/S3Error.java b/src/main/java/org/atdev/artrip/global/apipayload/code/status/S3Error.java index 6f07900..a36e15b 100644 --- a/src/main/java/org/atdev/artrip/global/apipayload/code/status/S3Error.java +++ b/src/main/java/org/atdev/artrip/global/apipayload/code/status/S3Error.java @@ -15,7 +15,7 @@ public enum S3Error implements BaseErrorCode { _INVALID_FILE_EXTENSION(HttpStatus.UNSUPPORTED_MEDIA_TYPE, "FILE415-EXT_UNSUPPORTED", "허용되지 않는 확장자입니다."), _INVALID_URL_FORMAT(HttpStatus.BAD_REQUEST, "FILE400-URL_INVALID", "잘못된 URL 형식입니다."), _IO_EXCEPTION_UPLOAD_FILE(HttpStatus.INTERNAL_SERVER_ERROR, "FILE500-UPLOAD_IO", "업로드 중 오류가 발생했습니다."), - _IO_EXCEPTION_DELETE_FILE(HttpStatus.INTERNAL_SERVER_ERROR, "FILE500-DELETE_IO", "파일을 삭제할 수 없습니다."); + _IO_EXCEPTION_DELETE_FILE(HttpStatus.INTERNAL_SERVER_ERROR, "FILE500-DELETE_IO", "파일 삭제 중 오류가 발생했습니다."); private final HttpStatus httpStatus; private final String code; diff --git a/src/main/java/org/atdev/artrip/global/apipayload/code/status/UserError.java b/src/main/java/org/atdev/artrip/global/apipayload/code/status/UserError.java index b61ca9a..e070fdd 100644 --- a/src/main/java/org/atdev/artrip/global/apipayload/code/status/UserError.java +++ b/src/main/java/org/atdev/artrip/global/apipayload/code/status/UserError.java @@ -12,6 +12,9 @@ public enum UserError implements BaseErrorCode { // User Errors _USER_NOT_FOUND(HttpStatus.NOT_FOUND, "USER404-NOT_FOUND", "존재하지 않는 회원입니다."), _USER_FORBIDDEN(HttpStatus.FORBIDDEN, "USER403-FORBIDDEN", "접근 권한이 없습니다."), + _DUPLICATE_NICKNAME(HttpStatus.CONFLICT,"USER409-CONFLICT","닉네임이 중복되었습니다."), + _PROFILE_IMAGE_NOT_EXIST(HttpStatus.NOT_FOUND,"USER404-NOT_FOUND","프로필 이미지가 존재하지 않습니다."), + _NICKNAME_BAD_REQUEST(HttpStatus.BAD_REQUEST,"USER400-BAD_REQUEST","닉네임 형식이 올바르지 않습니다."), // JWT Errors _JWT_EXPIRED_ACCESS_TOKEN(HttpStatus.UNAUTHORIZED, "JWT401-EXPIRED_ACCESS", "만료된 엑세스 토큰입니다."), diff --git a/src/main/java/org/atdev/artrip/global/s3/S3Service.java b/src/main/java/org/atdev/artrip/global/s3/S3Service.java index 948aaed..29f8075 100644 --- a/src/main/java/org/atdev/artrip/global/s3/S3Service.java +++ b/src/main/java/org/atdev/artrip/global/s3/S3Service.java @@ -41,6 +41,10 @@ public List upload(List files) { .toList(); } + public String upload(MultipartFile file) { + return uploadImage(file); + } + // validateFile메서드를 호출하여 유효성 검증 후 uploadImageToS3메서드에 데이터를 반환하여 S3에 파일 업로드, public url을 받아 서비스 로직에 반환 private String uploadImage(MultipartFile file) { validateFile(file.getOriginalFilename()); // 파일 유효성 검증 @@ -122,6 +126,22 @@ public void delete(List imageUrls) { } } +//단일 delete + public void delete(String imageUrl) { + try { + String key = getKeyFromImageUrls(imageUrl); + + s3Client.deleteObject(builder -> builder + .bucket(bucketName) + .key(key) + ); + + } catch (Exception exception) { + log.error(exception.getMessage(), exception); + throw new GeneralException(S3Error._IO_EXCEPTION_DELETE_FILE); + } + } + // 삭제에 필요한 key 반환 private String getKeyFromImageUrls(String imageUrl) { try { diff --git a/src/main/resources/db/migration/V20251221__add_profile_image_to_user.sql b/src/main/resources/db/migration/V20251221__add_profile_image_to_user.sql new file mode 100644 index 0000000..59f3c1c --- /dev/null +++ b/src/main/resources/db/migration/V20251221__add_profile_image_to_user.sql @@ -0,0 +1,2 @@ +ALTER TABLE `user` + ADD COLUMN profile_image_url VARCHAR(500) NULL