diff --git a/build.gradle b/build.gradle index 1ed3bd7..2000e5e 100644 --- a/build.gradle +++ b/build.gradle @@ -51,6 +51,11 @@ dependencies { runtimeOnly 'com.mysql:mysql-connector-j' runtimeOnly 'com.h2database:h2' + implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' + annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta" + annotationProcessor "jakarta.annotation:jakarta.annotation-api" + annotationProcessor "jakarta.persistence:jakarta.persistence-api" + // Spring docs implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0' @@ -62,8 +67,10 @@ dependencies { // Monitoring implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation 'io.micrometer:micrometer-registry-prometheus' -} + //S3 + implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' +} tasks.named('test') { useJUnitPlatform() } diff --git a/src/main/java/com/helpmeCookies/global/config/QueryDSLConfig.java b/src/main/java/com/helpmeCookies/global/config/QueryDSLConfig.java new file mode 100644 index 0000000..23d914f --- /dev/null +++ b/src/main/java/com/helpmeCookies/global/config/QueryDSLConfig.java @@ -0,0 +1,20 @@ +package com.helpmeCookies.global.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.querydsl.jpa.impl.JPAQueryFactory; + +import jakarta.persistence.EntityManager; +import lombok.RequiredArgsConstructor; + +@Configuration +@RequiredArgsConstructor +public class QueryDSLConfig { + private final EntityManager entityManager; + + @Bean + public JPAQueryFactory jpaQueryFactory(){ + return new JPAQueryFactory(entityManager); + } +} \ No newline at end of file diff --git a/src/main/java/com/helpmeCookies/global/config/S3Config.java b/src/main/java/com/helpmeCookies/global/config/S3Config.java new file mode 100644 index 0000000..bdeb16f --- /dev/null +++ b/src/main/java/com/helpmeCookies/global/config/S3Config.java @@ -0,0 +1,31 @@ +package com.helpmeCookies.global.config; + +import com.amazonaws.auth.AWSCredentials; +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class S3Config { + @Value("${cloud.aws.credentials.access-key}") + private String accessKey; + @Value("${cloud.aws.credentials.secret-key}") + private String secretKey; + @Value("${cloud.aws.region.static}") + private String region; + + @Bean + public AmazonS3 amazonS3() { + AWSCredentials credentials = new BasicAWSCredentials(accessKey,secretKey); + + return AmazonS3ClientBuilder + .standard() + .withCredentials(new AWSStaticCredentialsProvider(credentials)) + .withRegion(region) + .build(); + } +} diff --git a/src/main/java/com/helpmeCookies/global/exception/GlobalExceptionHandler.java b/src/main/java/com/helpmeCookies/global/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..0872d56 --- /dev/null +++ b/src/main/java/com/helpmeCookies/global/exception/GlobalExceptionHandler.java @@ -0,0 +1,15 @@ +package com.helpmeCookies.global.exception; + +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + +import com.helpmeCookies.global.exception.user.ResourceNotFoundException; + +@ControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(ResourceNotFoundException.class) + public String handleResourceNotFoundException() { + return "Resource not found"; + } +} diff --git a/src/main/java/com/helpmeCookies/global/exception/user/ResourceNotFoundException.java b/src/main/java/com/helpmeCookies/global/exception/user/ResourceNotFoundException.java new file mode 100644 index 0000000..38faecf --- /dev/null +++ b/src/main/java/com/helpmeCookies/global/exception/user/ResourceNotFoundException.java @@ -0,0 +1,7 @@ +package com.helpmeCookies.global.exception.user; + +public class ResourceNotFoundException extends RuntimeException { + public ResourceNotFoundException() { + super("Resource not found"); + } +} diff --git a/src/main/java/com/helpmeCookies/global/security/WebSecurityConfig.java b/src/main/java/com/helpmeCookies/global/security/WebSecurityConfig.java index 793f211..5ef983f 100644 --- a/src/main/java/com/helpmeCookies/global/security/WebSecurityConfig.java +++ b/src/main/java/com/helpmeCookies/global/security/WebSecurityConfig.java @@ -46,7 +46,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { "/login", "/signup", "/", "/user", "/api/auth/**", "/swagger-ui/**", - "/actuator/**" + "/actuator/**", + "/v1/**" ).permitAll() .anyRequest().authenticated() ); diff --git a/src/main/java/com/helpmeCookies/global/utils/AwsS3FileUtils.java b/src/main/java/com/helpmeCookies/global/utils/AwsS3FileUtils.java new file mode 100644 index 0000000..afc9043 --- /dev/null +++ b/src/main/java/com/helpmeCookies/global/utils/AwsS3FileUtils.java @@ -0,0 +1,72 @@ +package com.helpmeCookies.global.utils; + +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.CannedAccessControlList; +import com.amazonaws.services.s3.model.ObjectMetadata; +import com.amazonaws.services.s3.model.PutObjectRequest; +import com.helpmeCookies.product.dto.FileUploadResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.server.ResponseStatusException; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; + +@Component +@RequiredArgsConstructor +public class AwsS3FileUtils { + private final AmazonS3 amazonS3; + + @Value("${cloud.aws.s3.bucket}") + private String bucket; + + //다중파일 업로드후 url 반환 + public List uploadMultiImages(List multipartFiles) { + List fileList = new ArrayList<>(); + + multipartFiles.forEach(file -> { + String fileName = createFileName(file.getOriginalFilename()); //파일 이름 난수화 + ObjectMetadata objectMetadata = new ObjectMetadata(); + objectMetadata.setContentLength(file.getSize()); + objectMetadata.setContentType(file.getContentType()); + + try (InputStream inputStream = file.getInputStream()) { + amazonS3.putObject(new PutObjectRequest(bucket, fileName, inputStream, objectMetadata) + .withCannedAcl(CannedAccessControlList.PublicRead)); + } catch (IOException e) { + throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "파일 업로드 실패" + fileName); + } + + fileList.add(new FileUploadResponse(amazonS3.getUrl(bucket,fileName).toString(),fileName)); + }); + + return fileList; + } + + public String createFileName(String fileName) { + return UUID.randomUUID().toString().concat(getFileExtension(fileName)); + } + + //TODO error handler 필요 + public String getFileExtension(String fileName) { + try { + String extension = fileName.substring(fileName.lastIndexOf(".")).toLowerCase(); + //이미지 파일 확장자 목록 + List allowedExtensions = Arrays.asList(".jpg", ".jpeg", ".png"); + + if (!allowedExtensions.contains(extension)) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "이미지 파일만 업로드가 가능합니다. 지원되지 않는 형식의 파일" + fileName); + } + return extension; + } catch (StringIndexOutOfBoundsException e) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST,"잘못된 형식의 파일" + fileName + "입니다."); + } + } +} diff --git a/src/main/java/com/helpmeCookies/product/controller/ProductController.java b/src/main/java/com/helpmeCookies/product/controller/ProductController.java index c87732e..c655ee7 100644 --- a/src/main/java/com/helpmeCookies/product/controller/ProductController.java +++ b/src/main/java/com/helpmeCookies/product/controller/ProductController.java @@ -1,28 +1,40 @@ package com.helpmeCookies.product.controller; +import com.helpmeCookies.product.dto.FileUploadResponse; +import com.helpmeCookies.product.dto.ProductImageResponse; import com.helpmeCookies.product.dto.ProductRequest; import com.helpmeCookies.product.dto.ProductResponse; import com.helpmeCookies.product.entity.Product; +import com.helpmeCookies.product.service.ProductImageService; import com.helpmeCookies.product.service.ProductService; +import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.List; @RestController @RequestMapping("/api/v1/products") +@RequiredArgsConstructor public class ProductController { private final ProductService productService; - - public ProductController(ProductService productService) { - this.productService = productService; - } + private final ProductImageService productImageService; @PostMapping public ResponseEntity saveProduct(@RequestBody ProductRequest productRequest) { - Product product = productService.save(productRequest); + productService.save(productRequest); return ResponseEntity.ok().build(); } + @PostMapping("/{productId}/images") + public ResponseEntity uploadImages(@PathVariable("productId") Long productId, List files) throws IOException { + List responses = productImageService.uploadMultiFiles(productId,files); + return ResponseEntity.ok(new ProductImageResponse(responses.stream().map(FileUploadResponse::photoUrl).toList())); + } + @GetMapping("/{productId}") public ResponseEntity getProductInfo(@PathVariable("productId") Long productId) { Product product = productService.find(productId); @@ -36,6 +48,12 @@ public ResponseEntity editProductInfo(@PathVariable("productId") Long prod return ResponseEntity.ok().build(); } + @PutMapping("/{productId}/images") + public ResponseEntity editImages(@PathVariable("productId") Long productId, List files) throws IOException { + productImageService.editImages(productId, files); + return ResponseEntity.ok().build(); + } + @DeleteMapping("/{productId}") public ResponseEntity deleteProduct(@PathVariable("productId") Long productId) { productService.delete(productId); diff --git a/src/main/java/com/helpmeCookies/product/dto/FileUploadResponse.java b/src/main/java/com/helpmeCookies/product/dto/FileUploadResponse.java new file mode 100644 index 0000000..ad03aff --- /dev/null +++ b/src/main/java/com/helpmeCookies/product/dto/FileUploadResponse.java @@ -0,0 +1,16 @@ +package com.helpmeCookies.product.dto; + +import com.helpmeCookies.product.entity.ProductImage; + +public record FileUploadResponse( + String photoUrl, + String uuid +) { + public ProductImage toEntity(Long productId) { + return ProductImage.builder() + .productId(productId) + .photoUrl(photoUrl) + .uuid(uuid) + .build(); + } +} diff --git a/src/main/java/com/helpmeCookies/product/dto/ProductImageResponse.java b/src/main/java/com/helpmeCookies/product/dto/ProductImageResponse.java new file mode 100644 index 0000000..b090167 --- /dev/null +++ b/src/main/java/com/helpmeCookies/product/dto/ProductImageResponse.java @@ -0,0 +1,8 @@ +package com.helpmeCookies.product.dto; + +import java.util.List; + +public record ProductImageResponse( + List urls +) { +} diff --git a/src/main/java/com/helpmeCookies/product/entity/Category.java b/src/main/java/com/helpmeCookies/product/entity/Category.java index a58a7c0..0ab8e3a 100644 --- a/src/main/java/com/helpmeCookies/product/entity/Category.java +++ b/src/main/java/com/helpmeCookies/product/entity/Category.java @@ -33,7 +33,6 @@ public String getName() { } public static Category fromString(String name) { - System.out.println(name); Category category = nameToCategoryMap.get(name); if (category == null) { throw new IllegalArgumentException(name + "에 해당하는 카테고리가 없습니다."); diff --git a/src/main/java/com/helpmeCookies/product/entity/ProductImage.java b/src/main/java/com/helpmeCookies/product/entity/ProductImage.java index 699f53e..0a23f4f 100644 --- a/src/main/java/com/helpmeCookies/product/entity/ProductImage.java +++ b/src/main/java/com/helpmeCookies/product/entity/ProductImage.java @@ -4,8 +4,7 @@ import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; +import lombok.Builder; @Entity public class ProductImage { @@ -15,7 +14,15 @@ public class ProductImage { private String photoUrl; - @ManyToOne - @JoinColumn(name = "product_id") - private Product product; + private Long productId; + private String uuid; + + public ProductImage() {} + + @Builder + public ProductImage(String photoUrl, Long productId, String uuid) { + this.photoUrl = photoUrl; + this.productId = productId; + this.uuid = uuid; + } } diff --git a/src/main/java/com/helpmeCookies/product/repository/ProductImageRepository.java b/src/main/java/com/helpmeCookies/product/repository/ProductImageRepository.java new file mode 100644 index 0000000..718b605 --- /dev/null +++ b/src/main/java/com/helpmeCookies/product/repository/ProductImageRepository.java @@ -0,0 +1,13 @@ +package com.helpmeCookies.product.repository; + +import com.helpmeCookies.product.entity.ProductImage; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface ProductImageRepository extends JpaRepository { + List findAllByProductId(Long productId); + void deleteAllByProductId(Long productId); +} diff --git a/src/main/java/com/helpmeCookies/product/service/ProductImageService.java b/src/main/java/com/helpmeCookies/product/service/ProductImageService.java new file mode 100644 index 0000000..25215c5 --- /dev/null +++ b/src/main/java/com/helpmeCookies/product/service/ProductImageService.java @@ -0,0 +1,35 @@ +package com.helpmeCookies.product.service; + +import com.helpmeCookies.global.utils.AwsS3FileUtils; +import com.helpmeCookies.product.dto.FileUploadResponse; +import com.helpmeCookies.product.repository.ProductImageRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.List; + +@Service +@RequiredArgsConstructor +public class ProductImageService { + private final AwsS3FileUtils awsS3FileUtils; + private final ProductImageRepository productImageRepository; + + @Transactional + public List uploadMultiFiles(Long productId, List files) throws IOException { + List uploadResponses = awsS3FileUtils.uploadMultiImages(files); + uploadResponses.forEach(response -> + productImageRepository.save(response.toEntity(productId))); + return uploadResponses; + } + + @Transactional + public void editImages(Long productId, List files) throws IOException { + //우선은 전부 삭제하고 다시 업로드 + //추후에 개선 예정 + productImageRepository.deleteAllByProductId(productId); + uploadMultiFiles(productId, files); + } +} diff --git a/src/main/java/com/helpmeCookies/product/service/ProductService.java b/src/main/java/com/helpmeCookies/product/service/ProductService.java index 1a16699..c9b22d4 100644 --- a/src/main/java/com/helpmeCookies/product/service/ProductService.java +++ b/src/main/java/com/helpmeCookies/product/service/ProductService.java @@ -3,17 +3,17 @@ import com.helpmeCookies.product.dto.ProductRequest; import com.helpmeCookies.product.entity.Category; import com.helpmeCookies.product.entity.Product; +import com.helpmeCookies.product.repository.ProductImageRepository; import com.helpmeCookies.product.repository.ProductRepository; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service +@RequiredArgsConstructor public class ProductService { private final ProductRepository productRepository; - - public ProductService(ProductRepository productRepository) { - this.productRepository = productRepository; - } + private final ProductImageRepository productImageRepository; public Product save(ProductRequest productSaveRequest) { //TODO ArtistInfo 코드 병합시 수정 예정 @@ -43,6 +43,7 @@ public void edit(Long productId, ProductRequest productRequest) { public void delete(Long productId) { Product product = productRepository.findById(productId).orElseThrow(() -> new IllegalArgumentException("유효하지 않은 id입니다")); - productRepository.deleteById(productId); + productRepository.delete(product); + productImageRepository.deleteAllByProductId(productId); } } diff --git a/src/main/java/com/helpmeCookies/user/controller/ArtistController.java b/src/main/java/com/helpmeCookies/user/controller/ArtistController.java index 536d814..8640807 100644 --- a/src/main/java/com/helpmeCookies/user/controller/ArtistController.java +++ b/src/main/java/com/helpmeCookies/user/controller/ArtistController.java @@ -5,7 +5,6 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @@ -13,8 +12,7 @@ import com.helpmeCookies.global.jwt.JwtUser; import com.helpmeCookies.user.dto.request.BusinessArtistReq; import com.helpmeCookies.user.dto.request.StudentArtistReq; -import com.helpmeCookies.user.dto.response.BusinessArtistRes; -import com.helpmeCookies.user.dto.response.StudentArtistRes; +import com.helpmeCookies.user.dto.response.ArtistDetailsRes; import com.helpmeCookies.user.service.ArtistService; import lombok.RequiredArgsConstructor; @@ -25,22 +23,25 @@ public class ArtistController { private final ArtistService artistService; private final JwtProvider jwtProvider; + + // 작가 등록(학생) @PostMapping("/v1/artists/students") - public ResponseEntity registerStudents( - @RequestBody StudentArtistReq studentArtistReq, + public ResponseEntity registerStudents( + @RequestBody StudentArtistReq artistDetailsReq, @AuthenticationPrincipal JwtUser jwtUser ) { - StudentArtistRes response = StudentArtistRes.from(artistService.registerStudentsArtist(studentArtistReq, jwtUser.getId())); - return ResponseEntity.ok(response); + artistService.registerStudentsArtist(artistDetailsReq, jwtUser.getId()); + return ResponseEntity.ok().build(); } + // 작가 등록(사업자) @PostMapping("/v1/artists/bussinesses") - public ResponseEntity registerbussinsess( + public ResponseEntity registerbussinsess( @RequestBody BusinessArtistReq businessArtistReq, @AuthenticationPrincipal JwtUser jwtUser ) { - BusinessArtistRes response = BusinessArtistRes.from(artistService.registerBusinessArtist(businessArtistReq, jwtUser.getId())); - return ResponseEntity.ok(response); + artistService.registerBusinessArtist(businessArtistReq, jwtUser.getId()); + return ResponseEntity.ok().build(); } // 작가 목록 조회(페이징) @@ -51,9 +52,19 @@ public String getArtists() { } // 작가 프로필 조회 - // TODO: 6주차 회의 이후 추가 @GetMapping("/v1/artists/{userId}") - public String getArtist() { - return "ok"; + public ArtistDetailsRes getArtist( + @AuthenticationPrincipal JwtUser jwtUser, + @PathVariable Long userId + ) { + return artistService.getArtistDetails(userId); + } + + // 자기 자신 작가 프로필 조회 + @GetMapping("/v1/artist") + public ArtistDetailsRes getArtist( + @AuthenticationPrincipal JwtUser jwtUser + ) { + return artistService.getArtistDetails(jwtUser.getId()); } } diff --git a/src/main/java/com/helpmeCookies/user/controller/LoginController.java b/src/main/java/com/helpmeCookies/user/controller/LoginController.java index c004414..319af2a 100644 --- a/src/main/java/com/helpmeCookies/user/controller/LoginController.java +++ b/src/main/java/com/helpmeCookies/user/controller/LoginController.java @@ -13,6 +13,7 @@ import com.helpmeCookies.global.jwt.JwtUser; import com.helpmeCookies.product.entity.HashTag; import com.helpmeCookies.user.entity.User; +import com.helpmeCookies.user.entity.UserInfo; import com.helpmeCookies.user.repository.UserRepository; import lombok.RequiredArgsConstructor; @@ -27,14 +28,21 @@ public class LoginController { // 임시 회원가입 url. 유저를 생성하고 jwt 토큰을 반환한다. @GetMapping("/test/signup") public JwtToken signup() { - User user = User.builder() - .nickname("test") + UserInfo userInfo = UserInfo.builder() + .userImageUrl("https://www.naver.com") .email("test@test") - .birthdate("1999-01-01") - .address("서울시 강남구") + .birthdate("1995-01-01") .phone("010-1234-5678") - .hashTags(List.of(HashTag.DREAMLIKE, HashTag.VIBRANCE)) + .hashTags(List.of(HashTag.DREAMLIKE)) + .name("test") + .nickname("test") + .address("서울시 강남구") .build(); + + User user = User.builder() + .userInfo(userInfo) + .build(); + userRepository.save(user); return jwtProvider.createToken(JwtUser.of(user.getId())); } @@ -43,6 +51,6 @@ public JwtToken signup() { @GetMapping("/login") public String login(@AuthenticationPrincipal JwtUser jwtUser) { User user = userRepository.findById(jwtUser.getId()).orElseThrow(); - return user.getEmail(); + return user.getUserInfo().getEmail(); } } diff --git a/src/main/java/com/helpmeCookies/user/controller/UserController.java b/src/main/java/com/helpmeCookies/user/controller/UserController.java index c123502..ead4878 100644 --- a/src/main/java/com/helpmeCookies/user/controller/UserController.java +++ b/src/main/java/com/helpmeCookies/user/controller/UserController.java @@ -1,36 +1,105 @@ package com.helpmeCookies.user.controller; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.web.PageableDefault; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; import com.helpmeCookies.global.jwt.JwtUser; -import com.helpmeCookies.user.dto.UserDto; +import com.helpmeCookies.user.dto.UserFollowingDto; +import com.helpmeCookies.user.dto.response.UserCommonInfoRes; +import com.helpmeCookies.user.dto.request.UserInfoReq; +import com.helpmeCookies.user.dto.response.UserDetailsInfoRes; +import com.helpmeCookies.user.dto.UserTypeDto; +import com.helpmeCookies.user.dto.response.UserFollowingRes; import com.helpmeCookies.user.service.UserService; import lombok.RequiredArgsConstructor; -@Controller +@RestController @RequiredArgsConstructor public class UserController { private final UserService userService; - // TODO: 이후 추가되는 요구사항에 따라 별도의 UserRes로 반환 - // TODO: 구매 판매 내역에 대한 추가 정보 필요. Product 도메인 완성이후 추가 - @GetMapping("/v1/users/{userId}") - public UserDto getUsers( - @PathVariable Long userId + + // 유저 일반 정보 조회 + @GetMapping("/v1/users") + public UserCommonInfoRes getUsers( + @AuthenticationPrincipal JwtUser jwtUser + ) { + return UserCommonInfoRes.fromDto(userService.getUserInfo(jwtUser.getId())); + } + + // 유저 상세 정보 조회 + @GetMapping("/v1/users/details") + public UserDetailsInfoRes getUserDetails( + @AuthenticationPrincipal JwtUser jwtUser + ) { + return UserDetailsInfoRes.fromDto(userService.getUserInfo(jwtUser.getId())); + } + + // 유저 타입 조회 + @GetMapping("/v1/users/type") + public UserTypeDto getUserType( + @AuthenticationPrincipal JwtUser jwtUser + ) { + return userService.getUserType(jwtUser.getId()); + } + + // 유저 정보 수정 + @PutMapping("/v1/users") + public String updateUserInfo( + @AuthenticationPrincipal JwtUser jwtUser, + @RequestBody UserInfoReq userInfoReq + ) { + // UserInfoDto를 통해서 유저 정보를 수정한다. + userService.updateUserInfo(userInfoReq.toDto(), jwtUser.getId()); + return "ok"; + } + + // 아티스트 팔로우하기 + @PostMapping("/v1/users/following/{artistId}") + public String followArtist( + @AuthenticationPrincipal JwtUser jwtUser, + @PathVariable Long artistId ) { - return userService.getUser(userId); + userService.followArtist(jwtUser.getId(), artistId); + return "ok"; } - //유저 팔로우 목록 조회 - @GetMapping("/v1/users/{userId}/follows") - public String getFollows() { + // 아티스트 팔로우 취소하기 + @DeleteMapping("/v1/users/following/{artistId}") + public String unfollowArtist( + @AuthenticationPrincipal JwtUser jwtUser, + @PathVariable Long artistId + ) { + userService.unfollowArtist(jwtUser.getId(), artistId); + return "ok"; + } + // 유저 팔로우 목록 조회 + @GetMapping("/v1/users/following") + public Page getFollowingList( + @AuthenticationPrincipal JwtUser jwtUser, + @PageableDefault(size = 10, sort = "createdAt", direction = Sort.Direction.DESC) Pageable pageable + ) { + return userService.getFollowingWithPaging(jwtUser.getId(),pageable); + } + + // 유저 탈퇴 + @DeleteMapping("/v1/users") + public String deleteUser( + @AuthenticationPrincipal JwtUser jwtUser + ) { return "ok"; } } diff --git a/src/main/java/com/helpmeCookies/user/dto/ArtistInfoDto.java b/src/main/java/com/helpmeCookies/user/dto/ArtistInfoDto.java index afad531..4c50e5e 100644 --- a/src/main/java/com/helpmeCookies/user/dto/ArtistInfoDto.java +++ b/src/main/java/com/helpmeCookies/user/dto/ArtistInfoDto.java @@ -1,11 +1,29 @@ package com.helpmeCookies.user.dto; +import com.helpmeCookies.user.entity.ArtistInfo; +import com.helpmeCookies.user.entity.ArtistType; + +import lombok.Builder; + +@Builder public record ArtistInfoDto( + Long id, + Long userId, + ArtistType artistType, + String nickname, Long totalFollowers, Long totalLikes, String about ) { - public static ArtistInfoDto of(Long totalFollowers, Long totalLikes, String about) { - return new ArtistInfoDto(totalFollowers, totalLikes, about); + public static ArtistInfoDto fromEntity(ArtistInfo artistInfo) { + return ArtistInfoDto.builder() + .id(artistInfo.getId()) + .userId(artistInfo.getUserId()) + .artistType(artistInfo.getArtistType()) + .nickname(artistInfo.getNickname()) + .totalFollowers(artistInfo.getTotalFollowers()) + .totalLikes(artistInfo.getTotalLikes()) + .about(artistInfo.getAbout()) + .build(); } } diff --git a/src/main/java/com/helpmeCookies/user/dto/BusinessArtistDto.java b/src/main/java/com/helpmeCookies/user/dto/BusinessArtistDto.java index a677d95..8d4c8f0 100644 --- a/src/main/java/com/helpmeCookies/user/dto/BusinessArtistDto.java +++ b/src/main/java/com/helpmeCookies/user/dto/BusinessArtistDto.java @@ -4,24 +4,16 @@ public record BusinessArtistDto( Long id, - Long userId, String businessNumber, String openDate, - String headName, - ArtistInfoDto artistInfo + String headName ) { - public static BusinessArtistDto fromEntity(BusinessArtist businessArtist){ + public static BusinessArtistDto from(BusinessArtist businessArtist) { return new BusinessArtistDto( businessArtist.getId(), - businessArtist.getUserId(), businessArtist.getBusinessNumber(), businessArtist.getOpenDate(), - businessArtist.getHeadName(), - ArtistInfoDto.of( - businessArtist.getArtistInfo().getTotalFollowers(), - businessArtist.getArtistInfo().getTotalLikes(), - businessArtist.getArtistInfo().getAbout() - ) + businessArtist.getHeadName() ); } } diff --git a/src/main/java/com/helpmeCookies/user/dto/StudentArtistDto.java b/src/main/java/com/helpmeCookies/user/dto/StudentArtistDto.java index f453c3a..75e2201 100644 --- a/src/main/java/com/helpmeCookies/user/dto/StudentArtistDto.java +++ b/src/main/java/com/helpmeCookies/user/dto/StudentArtistDto.java @@ -4,24 +4,16 @@ public record StudentArtistDto( Long id, - Long userId, String schoolEmail, String schoolName, - String major, - ArtistInfoDto artistInfo + String major ) { - public static StudentArtistDto fromEntity(StudentArtist studentArtist) { + public static StudentArtistDto from(StudentArtist studentArtist) { return new StudentArtistDto( studentArtist.getId(), - studentArtist.getUserId(), studentArtist.getSchoolEmail(), studentArtist.getSchoolName(), - studentArtist.getMajor(), - ArtistInfoDto.of( - studentArtist.getArtistInfo().getTotalFollowers(), - studentArtist.getArtistInfo().getTotalLikes(), - studentArtist.getArtistInfo().getAbout() - ) + studentArtist.getMajor() ); } } diff --git a/src/main/java/com/helpmeCookies/user/dto/UserDto.java b/src/main/java/com/helpmeCookies/user/dto/UserDto.java index 74cc627..d5544d5 100644 --- a/src/main/java/com/helpmeCookies/user/dto/UserDto.java +++ b/src/main/java/com/helpmeCookies/user/dto/UserDto.java @@ -8,24 +8,14 @@ public record UserDto( Long id, - String nickname, - String email, - String birthdate, - String phone, - String address, - LocalDateTime createdAt, - List hashTags + UserInfoDto userInfo, + LocalDateTime createdAt ) { public static UserDto fromEntity(User user) { return new UserDto( user.getId(), - user.getNickname(), - user.getEmail(), - user.getBirthdate(), - user.getPhone(), - user.getAddress(), - user.getCreatedAt(), - user.getHashTags() + UserInfoDto.fromEntity(user.getUserInfo()), + user.getCreatedAt() ); } } diff --git a/src/main/java/com/helpmeCookies/user/dto/UserFollowingDto.java b/src/main/java/com/helpmeCookies/user/dto/UserFollowingDto.java new file mode 100644 index 0000000..35d3ca4 --- /dev/null +++ b/src/main/java/com/helpmeCookies/user/dto/UserFollowingDto.java @@ -0,0 +1,10 @@ +package com.helpmeCookies.user.dto; + +public record UserFollowingDto( + Long userId, + String imageUrl, + String nickname, + Long totalFollowers, + Long totalLikes +) { +} diff --git a/src/main/java/com/helpmeCookies/user/dto/UserInfoDto.java b/src/main/java/com/helpmeCookies/user/dto/UserInfoDto.java new file mode 100644 index 0000000..28099ff --- /dev/null +++ b/src/main/java/com/helpmeCookies/user/dto/UserInfoDto.java @@ -0,0 +1,44 @@ +package com.helpmeCookies.user.dto; + +import java.util.List; +import java.util.Set; + +import com.helpmeCookies.product.entity.HashTag; +import com.helpmeCookies.user.entity.UserInfo; + +public record UserInfoDto( + String name, + String userImageUrl, + String nickname, + String email, + String birthdate, + String phone, + String address, + List hashTags +) { + public static UserInfoDto fromEntity(UserInfo userInfo) { + return new UserInfoDto( + userInfo.getName(), + userInfo.getUserImageUrl(), + userInfo.getNickname(), + userInfo.getEmail(), + userInfo.getBirthdate(), + userInfo.getPhone(), + userInfo.getAddress(), + userInfo.getHashTags() + ); + } + + public UserInfo toEntity() { + return new UserInfo( + name, + userImageUrl, + nickname, + email, + birthdate, + phone, + address, + hashTags + ); + } +} diff --git a/src/main/java/com/helpmeCookies/user/dto/UserTypeDto.java b/src/main/java/com/helpmeCookies/user/dto/UserTypeDto.java new file mode 100644 index 0000000..627a88c --- /dev/null +++ b/src/main/java/com/helpmeCookies/user/dto/UserTypeDto.java @@ -0,0 +1,10 @@ +package com.helpmeCookies.user.dto; + +import lombok.Builder; + +@Builder +public record UserTypeDto( + String role, + String userType +) { +} diff --git a/src/main/java/com/helpmeCookies/user/dto/request/ArtistDetailsReq.java b/src/main/java/com/helpmeCookies/user/dto/request/ArtistDetailsReq.java new file mode 100644 index 0000000..1ffc730 --- /dev/null +++ b/src/main/java/com/helpmeCookies/user/dto/request/ArtistDetailsReq.java @@ -0,0 +1,10 @@ +package com.helpmeCookies.user.dto.request; + +public record ArtistDetailsReq( + String nickname, + String description, + Long totalFollowers, + Long totalLikes, + String about +) { +} diff --git a/src/main/java/com/helpmeCookies/user/dto/request/BusinessArtistReq.java b/src/main/java/com/helpmeCookies/user/dto/request/BusinessArtistReq.java index 607a2f6..f4eb72c 100644 --- a/src/main/java/com/helpmeCookies/user/dto/request/BusinessArtistReq.java +++ b/src/main/java/com/helpmeCookies/user/dto/request/BusinessArtistReq.java @@ -5,7 +5,8 @@ public record BusinessArtistReq( String businessNumber, String openDate, - String headName + String headName, + String about ) { } diff --git a/src/main/java/com/helpmeCookies/user/dto/request/StudentArtistReq.java b/src/main/java/com/helpmeCookies/user/dto/request/StudentArtistReq.java index 8b5d205..95f1e66 100644 --- a/src/main/java/com/helpmeCookies/user/dto/request/StudentArtistReq.java +++ b/src/main/java/com/helpmeCookies/user/dto/request/StudentArtistReq.java @@ -5,6 +5,7 @@ public record StudentArtistReq( String schoolEmail, String schoolName, - String major + String major, + String about ) { } diff --git a/src/main/java/com/helpmeCookies/user/dto/request/UserInfoReq.java b/src/main/java/com/helpmeCookies/user/dto/request/UserInfoReq.java new file mode 100644 index 0000000..d5c44a1 --- /dev/null +++ b/src/main/java/com/helpmeCookies/user/dto/request/UserInfoReq.java @@ -0,0 +1,30 @@ +package com.helpmeCookies.user.dto.request; + +import java.util.List; + +import com.helpmeCookies.product.entity.HashTag; +import com.helpmeCookies.user.dto.UserInfoDto; + +public record UserInfoReq( + String name, + String userImageUrl, + String nickname, + String email, + String birthdate, + String phone, + String address, + List hashTags +) { + public UserInfoDto toDto() { + return new UserInfoDto( + name, + userImageUrl, + nickname, + email, + birthdate, + phone, + address, + hashTags + ); + } +} diff --git a/src/main/java/com/helpmeCookies/user/dto/response/ArtistDetailsRes.java b/src/main/java/com/helpmeCookies/user/dto/response/ArtistDetailsRes.java new file mode 100644 index 0000000..ead04ea --- /dev/null +++ b/src/main/java/com/helpmeCookies/user/dto/response/ArtistDetailsRes.java @@ -0,0 +1,34 @@ +package com.helpmeCookies.user.dto.response; + +import com.helpmeCookies.user.dto.ArtistInfoDto; +import com.helpmeCookies.user.dto.BusinessArtistDto; +import com.helpmeCookies.user.dto.StudentArtistDto; + +public record ArtistDetailsRes( + String nickname, + String description, + Long totalFollowers, + Long totalLikes, + String about + +) { + public static ArtistDetailsRes from(ArtistInfoDto artistInfoDto, BusinessArtistDto businessArtistDto) { + return new ArtistDetailsRes( + artistInfoDto.nickname(), + businessArtistDto.headName(), + artistInfoDto.totalFollowers(), + artistInfoDto.totalLikes(), + artistInfoDto.about() + ); + } + + public static ArtistDetailsRes from(ArtistInfoDto artistInfoDto, StudentArtistDto studentArtistDto) { + return new ArtistDetailsRes( + artistInfoDto.nickname(), + studentArtistDto.schoolName(), + artistInfoDto.totalFollowers(), + artistInfoDto.totalLikes(), + artistInfoDto.about() + ); + } +} diff --git a/src/main/java/com/helpmeCookies/user/dto/response/ArtistInfoRes.java b/src/main/java/com/helpmeCookies/user/dto/response/ArtistInfoRes.java deleted file mode 100644 index c6bd44a..0000000 --- a/src/main/java/com/helpmeCookies/user/dto/response/ArtistInfoRes.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.helpmeCookies.user.dto.response; - -import com.helpmeCookies.user.dto.ArtistInfoDto; - -public record ArtistInfoRes( - Long totalFollowers, - Long totalLikes, - String about -) { - public static ArtistInfoRes from(ArtistInfoDto artistInfoDto) { - return new ArtistInfoRes( - artistInfoDto.totalFollowers(), - artistInfoDto.totalLikes(), - artistInfoDto.about()); - } -} diff --git a/src/main/java/com/helpmeCookies/user/dto/response/BusinessArtistRes.java b/src/main/java/com/helpmeCookies/user/dto/response/BusinessArtistRes.java deleted file mode 100644 index ad95d6c..0000000 --- a/src/main/java/com/helpmeCookies/user/dto/response/BusinessArtistRes.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.helpmeCookies.user.dto.response; - -import com.helpmeCookies.user.dto.BusinessArtistDto; - -public record BusinessArtistRes( - String businessNumber, - String openDate, - String headName, - ArtistInfoRes artistInfo -) { - public static BusinessArtistRes from(BusinessArtistDto businessArtistDto) { - return new BusinessArtistRes( - businessArtistDto.businessNumber(), - businessArtistDto.openDate(), - businessArtistDto.headName(), - ArtistInfoRes.from(businessArtistDto.artistInfo()) - ); - } -} diff --git a/src/main/java/com/helpmeCookies/user/dto/response/StudentArtistRes.java b/src/main/java/com/helpmeCookies/user/dto/response/StudentArtistRes.java deleted file mode 100644 index af688ca..0000000 --- a/src/main/java/com/helpmeCookies/user/dto/response/StudentArtistRes.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.helpmeCookies.user.dto.response; - -import com.helpmeCookies.user.dto.StudentArtistDto; - -public record StudentArtistRes( - String schoolEmail, - String schoolName, - String major, - ArtistInfoRes artistInfo -) { - public static StudentArtistRes from(StudentArtistDto studentArtistDto) { - return new StudentArtistRes( - studentArtistDto.schoolEmail(), - studentArtistDto.schoolName(), - studentArtistDto.major(), - ArtistInfoRes.from(studentArtistDto.artistInfo()) - ); - } -} diff --git a/src/main/java/com/helpmeCookies/user/dto/response/UserCommonInfoRes.java b/src/main/java/com/helpmeCookies/user/dto/response/UserCommonInfoRes.java new file mode 100644 index 0000000..5cc9ce4 --- /dev/null +++ b/src/main/java/com/helpmeCookies/user/dto/response/UserCommonInfoRes.java @@ -0,0 +1,21 @@ +package com.helpmeCookies.user.dto.response; + +import java.util.List; + +import com.helpmeCookies.product.entity.HashTag; +import com.helpmeCookies.user.dto.UserInfoDto; + +public record UserCommonInfoRes( + String username, + List hashTags, + String userImageUrl +) { + public static UserCommonInfoRes fromDto(UserInfoDto userInfoDto) { + return new UserCommonInfoRes( + userInfoDto.nickname(), + userInfoDto.hashTags(), + userInfoDto.userImageUrl() + ); + } + +} diff --git a/src/main/java/com/helpmeCookies/user/dto/response/UserDetailsInfoRes.java b/src/main/java/com/helpmeCookies/user/dto/response/UserDetailsInfoRes.java new file mode 100644 index 0000000..67fface --- /dev/null +++ b/src/main/java/com/helpmeCookies/user/dto/response/UserDetailsInfoRes.java @@ -0,0 +1,33 @@ +package com.helpmeCookies.user.dto.response; + +import java.util.List; + +import com.helpmeCookies.product.entity.HashTag; +import com.helpmeCookies.user.dto.UserInfoDto; + +import lombok.Builder; + +@Builder +public record UserDetailsInfoRes( + String name, + String userImageUrl, + String nickname, + String email, + String birthdate, + String phone, + String address, + List hashTags +) { + public static UserDetailsInfoRes fromDto(UserInfoDto userInfoDto) { + return UserDetailsInfoRes.builder() + .name(userInfoDto.name()) + .userImageUrl(userInfoDto.userImageUrl()) + .nickname(userInfoDto.nickname()) + .email(userInfoDto.email()) + .birthdate(userInfoDto.birthdate()) + .phone(userInfoDto.phone()) + .address(userInfoDto.address()) + .hashTags(userInfoDto.hashTags()) + .build(); + } +} diff --git a/src/main/java/com/helpmeCookies/user/dto/response/UserFollowingRes.java b/src/main/java/com/helpmeCookies/user/dto/response/UserFollowingRes.java new file mode 100644 index 0000000..3acdc28 --- /dev/null +++ b/src/main/java/com/helpmeCookies/user/dto/response/UserFollowingRes.java @@ -0,0 +1,20 @@ +package com.helpmeCookies.user.dto.response; + +import com.helpmeCookies.user.dto.UserFollowingDto; + +public record UserFollowingRes( + Long userId, + String nickname, + String userImageUrl, + Long totalFollowers, + Long totalLikes +) { + public static UserFollowingRes fromDto(UserFollowingDto dto) { + return new UserFollowingRes( + dto.userId(), + dto.nickname(), + dto.imageUrl(), + dto.totalFollowers(), + dto.totalLikes()); + } +} diff --git a/src/main/java/com/helpmeCookies/user/entity/ArtistInfo.java b/src/main/java/com/helpmeCookies/user/entity/ArtistInfo.java index ec97418..0aea587 100644 --- a/src/main/java/com/helpmeCookies/user/entity/ArtistInfo.java +++ b/src/main/java/com/helpmeCookies/user/entity/ArtistInfo.java @@ -2,6 +2,8 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; @@ -19,12 +21,18 @@ public class ArtistInfo { @Id - @GeneratedValue(strategy = GenerationType.AUTO) + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private Long userId; - private String artistType; + private String nickname; + + private String artistImageUrl; + + @Column(nullable = false) + @Enumerated(EnumType.STRING) + private ArtistType artistType; @Column(nullable = false) private Long totalFollowers; diff --git a/src/main/java/com/helpmeCookies/user/entity/ArtistType.java b/src/main/java/com/helpmeCookies/user/entity/ArtistType.java new file mode 100644 index 0000000..daefeda --- /dev/null +++ b/src/main/java/com/helpmeCookies/user/entity/ArtistType.java @@ -0,0 +1,15 @@ +package com.helpmeCookies.user.entity; + +public enum ArtistType { + BUSINESS("BusinessArtist"), STUDENT("StudentArtist"); + + private final String type; + + ArtistType(String type) { + this.type = type; + } + + public String getType() { + return type; + } +} diff --git a/src/main/java/com/helpmeCookies/user/entity/BusinessArtist.java b/src/main/java/com/helpmeCookies/user/entity/BusinessArtist.java index a923d82..c4dec97 100644 --- a/src/main/java/com/helpmeCookies/user/entity/BusinessArtist.java +++ b/src/main/java/com/helpmeCookies/user/entity/BusinessArtist.java @@ -1,10 +1,12 @@ package com.helpmeCookies.user.entity; +import jakarta.persistence.CascadeType; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; import jakarta.persistence.OneToOne; import jakarta.persistence.Table; import lombok.AccessLevel; @@ -32,6 +34,7 @@ public class BusinessArtist { @Column(nullable = false) private String headName; - @OneToOne + @OneToOne(cascade = CascadeType.ALL) + @JoinColumn(name = "artist_info_id") private ArtistInfo artistInfo; } diff --git a/src/main/java/com/helpmeCookies/user/entity/User.java b/src/main/java/com/helpmeCookies/user/entity/User.java index 6423939..d2a150a 100644 --- a/src/main/java/com/helpmeCookies/user/entity/User.java +++ b/src/main/java/com/helpmeCookies/user/entity/User.java @@ -12,6 +12,7 @@ import jakarta.persistence.CollectionTable; import jakarta.persistence.Column; import jakarta.persistence.ElementCollection; +import jakarta.persistence.Embedded; import jakarta.persistence.Entity; import jakarta.persistence.EntityListeners; import jakarta.persistence.EnumType; @@ -25,6 +26,7 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.Setter; @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -39,32 +41,26 @@ public class User { @Column(nullable = false) private Long id; - @Column(nullable = false) - private String name; - - @Column(nullable = false) - private String nickname; - - @Column(nullable = false) - private String email; - - @Column(nullable = false) - private String birthdate; - - @Column(nullable = false) - private String phone; - - @Column(nullable = false) - private String address; + @Embedded + private UserInfo userInfo; @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) @CreatedDate @Column(nullable = false, updatable = false) protected LocalDateTime createdAt; - // 별도의 테이블 생성. 문자열로 저장 - @ElementCollection(targetClass = HashTag.class) - @CollectionTable(name = "user_hashtags") - @Enumerated(EnumType.STRING) - private List hashTags; // 기본 FetchType.LAZY + + public void createUser(UserInfo userInfo) { + + } + + public void updateUserInfo(UserInfo userInfo) { + // TODO: 유저 정보 업데이트시 유효성 검사 + setUserInfo(userInfo); + } + + private User setUserInfo(UserInfo userInfo) { + this.userInfo = userInfo; + return this; + } } \ No newline at end of file diff --git a/src/main/java/com/helpmeCookies/user/entity/UserInfo.java b/src/main/java/com/helpmeCookies/user/entity/UserInfo.java new file mode 100644 index 0000000..e70ac05 --- /dev/null +++ b/src/main/java/com/helpmeCookies/user/entity/UserInfo.java @@ -0,0 +1,44 @@ +package com.helpmeCookies.user.entity; + +import java.util.List; + +import com.helpmeCookies.product.entity.HashTag; + +import jakarta.persistence.CollectionTable; +import jakarta.persistence.Column; +import jakarta.persistence.ElementCollection; +import jakarta.persistence.Embeddable; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Embeddable +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +@Getter +@Builder +public class UserInfo { + + private String name; + + private String nickname; + + private String userImageUrl; + + private String email; + + private String birthdate; + + private String phone; + + private String address; + + @ElementCollection(targetClass = HashTag.class) + @CollectionTable(name = "user_hashtags") + @Enumerated(EnumType.STRING) + private List hashTags; // 기본 FetchType.LAZY +} diff --git a/src/main/java/com/helpmeCookies/user/repository/ArtistInfoRepository.java b/src/main/java/com/helpmeCookies/user/repository/ArtistInfoRepository.java index b2120eb..49847d2 100644 --- a/src/main/java/com/helpmeCookies/user/repository/ArtistInfoRepository.java +++ b/src/main/java/com/helpmeCookies/user/repository/ArtistInfoRepository.java @@ -1,8 +1,12 @@ package com.helpmeCookies.user.repository; +import java.util.Optional; + import org.springframework.data.jpa.repository.JpaRepository; import com.helpmeCookies.user.entity.ArtistInfo; public interface ArtistInfoRepository extends JpaRepository { + Optional findByUserId(Long userId); + boolean existsByUserId(Long userId); } diff --git a/src/main/java/com/helpmeCookies/user/repository/BusinessArtistRepository.java b/src/main/java/com/helpmeCookies/user/repository/BusinessArtistRepository.java index 3d8e33d..5d65c52 100644 --- a/src/main/java/com/helpmeCookies/user/repository/BusinessArtistRepository.java +++ b/src/main/java/com/helpmeCookies/user/repository/BusinessArtistRepository.java @@ -1,8 +1,12 @@ package com.helpmeCookies.user.repository; +import java.util.Optional; + import org.springframework.data.jpa.repository.JpaRepository; +import com.helpmeCookies.user.entity.ArtistInfo; import com.helpmeCookies.user.entity.BusinessArtist; public interface BusinessArtistRepository extends JpaRepository { + Optional findByArtistInfo(ArtistInfo artistInfo); } diff --git a/src/main/java/com/helpmeCookies/user/repository/SocialRepository.java b/src/main/java/com/helpmeCookies/user/repository/SocialRepository.java index 027839c..4d10d75 100644 --- a/src/main/java/com/helpmeCookies/user/repository/SocialRepository.java +++ b/src/main/java/com/helpmeCookies/user/repository/SocialRepository.java @@ -1,8 +1,18 @@ package com.helpmeCookies.user.repository; +import java.util.Optional; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import com.helpmeCookies.user.entity.ArtistInfo; import com.helpmeCookies.user.entity.Social; +import com.helpmeCookies.user.entity.User; +@Repository public interface SocialRepository extends JpaRepository { + Boolean existsByFollowerAndFollowing(User follower, ArtistInfo following); + Optional findByFollowerAndFollowing(User follower, ArtistInfo following); } diff --git a/src/main/java/com/helpmeCookies/user/repository/StudentArtistRepository.java b/src/main/java/com/helpmeCookies/user/repository/StudentArtistRepository.java index 84102a8..2c11c8f 100644 --- a/src/main/java/com/helpmeCookies/user/repository/StudentArtistRepository.java +++ b/src/main/java/com/helpmeCookies/user/repository/StudentArtistRepository.java @@ -1,10 +1,14 @@ package com.helpmeCookies.user.repository; +import java.util.Optional; + import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; +import com.helpmeCookies.user.entity.ArtistInfo; import com.helpmeCookies.user.entity.StudentArtist; @Repository public interface StudentArtistRepository extends JpaRepository { + Optional findByArtistInfo(ArtistInfo artistInfo); } diff --git a/src/main/java/com/helpmeCookies/user/repository/UserRepository.java b/src/main/java/com/helpmeCookies/user/repository/UserRepository.java index c3ce1bc..7aa80d0 100644 --- a/src/main/java/com/helpmeCookies/user/repository/UserRepository.java +++ b/src/main/java/com/helpmeCookies/user/repository/UserRepository.java @@ -6,8 +6,9 @@ import org.springframework.stereotype.Repository; import com.helpmeCookies.user.entity.User; +import com.helpmeCookies.user.repository.querydsl.UserCustomRepository; @Repository -public interface UserRepository extends JpaRepository { +public interface UserRepository extends JpaRepository, UserCustomRepository { Optional findById(Long id); } diff --git a/src/main/java/com/helpmeCookies/user/repository/querydsl/UserCustomRepository.java b/src/main/java/com/helpmeCookies/user/repository/querydsl/UserCustomRepository.java new file mode 100644 index 0000000..23a0b93 --- /dev/null +++ b/src/main/java/com/helpmeCookies/user/repository/querydsl/UserCustomRepository.java @@ -0,0 +1,11 @@ +package com.helpmeCookies.user.repository.querydsl; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +import com.helpmeCookies.user.dto.UserFollowingDto; + +public interface UserCustomRepository { + // User 테이블의 ImageUrl과 ArtistInfo의 totalFollows,totalLikes,nickname,id를 pageable하게 가져오는 쿼리 + Page findFollowingUsers(Long userId, Pageable pageable); +} diff --git a/src/main/java/com/helpmeCookies/user/repository/querydsl/UserCustomRepositoryImpl.java b/src/main/java/com/helpmeCookies/user/repository/querydsl/UserCustomRepositoryImpl.java new file mode 100644 index 0000000..ca3164e --- /dev/null +++ b/src/main/java/com/helpmeCookies/user/repository/querydsl/UserCustomRepositoryImpl.java @@ -0,0 +1,54 @@ +package com.helpmeCookies.user.repository.querydsl; + +import java.util.List; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Repository; + +import com.helpmeCookies.user.dto.UserFollowingDto; +import com.helpmeCookies.user.entity.QArtistInfo; +import com.helpmeCookies.user.entity.QSocial; +import com.helpmeCookies.user.entity.QUser; +import com.querydsl.core.types.Projections; +import com.querydsl.jpa.JPQLQuery; +import com.querydsl.jpa.impl.JPAQueryFactory; + +import lombok.AllArgsConstructor; + +@Repository +@AllArgsConstructor +public class UserCustomRepositoryImpl implements UserCustomRepository { + + private final JPAQueryFactory queryFactory; + + @Override + public Page findFollowingUsers(Long userId, Pageable pageable) { + QUser user = QUser.user; + QArtistInfo artistInfo = QArtistInfo.artistInfo; + QSocial social = QSocial.social; // Social 테이블 추가 + + // + JPQLQuery query = queryFactory + .select(Projections.constructor( + UserFollowingDto.class, + user.id, + user.userInfo.userImageUrl, + user.userInfo.nickname, + artistInfo.totalFollowers, + artistInfo.totalLikes + )) + .from(social) + .join(user).on(user.id.eq(social.following.userId)) + .join(social.following, artistInfo) + .where(social.follower.id.eq(userId)) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()); + + List content = query.fetch(); + long total = query.fetchCount(); + + return new PageImpl<>(content, pageable, total); + } +} diff --git a/src/main/java/com/helpmeCookies/user/service/ArtistService.java b/src/main/java/com/helpmeCookies/user/service/ArtistService.java index 8941696..b90d2ff 100644 --- a/src/main/java/com/helpmeCookies/user/service/ArtistService.java +++ b/src/main/java/com/helpmeCookies/user/service/ArtistService.java @@ -8,62 +8,107 @@ import com.helpmeCookies.user.dto.StudentArtistDto; import com.helpmeCookies.user.dto.request.BusinessArtistReq; import com.helpmeCookies.user.dto.request.StudentArtistReq; +import com.helpmeCookies.user.dto.response.ArtistDetailsRes; import com.helpmeCookies.user.entity.ArtistInfo; +import com.helpmeCookies.user.entity.ArtistType; import com.helpmeCookies.user.entity.BusinessArtist; import com.helpmeCookies.user.entity.StudentArtist; +import com.helpmeCookies.user.entity.User; import com.helpmeCookies.user.repository.ArtistInfoRepository; import com.helpmeCookies.user.repository.BusinessArtistRepository; import com.helpmeCookies.user.repository.StudentArtistRepository; +import com.helpmeCookies.user.repository.UserRepository; import lombok.RequiredArgsConstructor; @RequiredArgsConstructor @Service public class ArtistService { + private final UserRepository userRepository; private final BusinessArtistRepository businessArtistRepository; private final StudentArtistRepository studentArtistRepository; + private final ArtistInfoRepository artistInfoRepository; @Transactional - public StudentArtistDto registerStudentsArtist(StudentArtistReq studentArtistReq, Long userId) { - // StudentArtist 생성 + public void registerStudentsArtist(StudentArtistReq studentArtistReq, Long userId) { + + User user = userRepository.findById(userId) + .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 유저입니다.")); + + if (artistInfoRepository.existsByUserId(userId)) { + throw new IllegalArgumentException("이미 등록된 아티스트입니다."); + } + + // ArtistInfo 생성 ArtistInfo artistInfo = ArtistInfo.builder() + .userId(userId) + .artistType(ArtistType.STUDENT) + .nickname(user.getUserInfo().getNickname()) .totalFollowers(0L) .totalLikes(0L) + .about(studentArtistReq.about()) .build(); + // StudentArtist 생성 StudentArtist studentArtist = StudentArtist.builder() - .userId(userId) - .schoolEmail(studentArtistReq.schoolEmail()) .schoolName(studentArtistReq.schoolName()) + .schoolEmail(studentArtistReq.schoolEmail()) .major(studentArtistReq.major()) .artistInfo(artistInfo) .build(); - studentArtist = studentArtistRepository.save(studentArtist); - - return StudentArtistDto.fromEntity(studentArtist); + studentArtistRepository.save(studentArtist); } @Transactional - public BusinessArtistDto registerBusinessArtist(BusinessArtistReq businessArtistReq, Long userId) { + public void registerBusinessArtist(BusinessArtistReq businessArtistReq, Long userId) { + + User user = userRepository.getReferenceById(userId); + + if (artistInfoRepository.existsByUserId(userId)) { + throw new IllegalArgumentException("이미 등록된 아티스트입니다."); + } + // BusinessArtist 생성 ArtistInfo artistInfo = ArtistInfo.builder() + .userId(userId) + .artistType(ArtistType.BUSINESS) + .nickname(user.getUserInfo().getNickname()) .totalFollowers(0L) .totalLikes(0L) + .about(businessArtistReq.about()) .build(); BusinessArtist businessArtist = BusinessArtist .builder() - .userId(userId) .businessNumber(businessArtistReq.businessNumber()) .openDate(businessArtistReq.openDate()) .headName(businessArtistReq.headName()) .artistInfo(artistInfo) .build(); - businessArtist = businessArtistRepository.save(businessArtist); - - return BusinessArtistDto.fromEntity(businessArtist); + businessArtistRepository.save(businessArtist); } + @Transactional + public ArtistDetailsRes getArtistDetails(Long userId) { + ArtistInfo artistInfo = artistInfoRepository.findByUserId(userId) + .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 아티스트입니다.")); + ArtistInfoDto artistInfoDto = ArtistInfoDto.fromEntity(artistInfo); + + switch (artistInfo.getArtistType()) { + case STUDENT: + StudentArtist studentArtist = studentArtistRepository.findByArtistInfo(artistInfo) + .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 학생 아티스트입니다.")); + StudentArtistDto studentArtistDto = StudentArtistDto.from(studentArtist); + return ArtistDetailsRes.from(artistInfoDto, studentArtistDto); + case BUSINESS: + BusinessArtist businessArtist = businessArtistRepository.findByArtistInfo(artistInfo) + .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 사업자 아티스트입니다.")); + BusinessArtistDto businessArtistDto = BusinessArtistDto.from(businessArtist); + return ArtistDetailsRes.from(artistInfoDto, businessArtistDto); + default: + throw new IllegalArgumentException("존재하지 않는 아티스트입니다."); + } + } } diff --git a/src/main/java/com/helpmeCookies/user/service/UserService.java b/src/main/java/com/helpmeCookies/user/service/UserService.java index d5b6aef..3c29d55 100644 --- a/src/main/java/com/helpmeCookies/user/service/UserService.java +++ b/src/main/java/com/helpmeCookies/user/service/UserService.java @@ -1,20 +1,106 @@ package com.helpmeCookies.user.service; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; -import com.helpmeCookies.global.jwt.JwtUser; +import com.helpmeCookies.global.exception.user.ResourceNotFoundException; import com.helpmeCookies.user.dto.UserDto; +import com.helpmeCookies.user.dto.UserFollowingDto; +import com.helpmeCookies.user.dto.UserInfoDto; +import com.helpmeCookies.user.dto.UserTypeDto; +import com.helpmeCookies.user.dto.response.UserFollowingRes; +import com.helpmeCookies.user.entity.ArtistInfo; +import com.helpmeCookies.user.entity.Social; +import com.helpmeCookies.user.entity.User; +import com.helpmeCookies.user.entity.UserInfo; +import com.helpmeCookies.user.repository.ArtistInfoRepository; +import com.helpmeCookies.user.repository.SocialRepository; import com.helpmeCookies.user.repository.UserRepository; -import lombok.NoArgsConstructor; import lombok.RequiredArgsConstructor; @Service @RequiredArgsConstructor public class UserService { private final UserRepository userRepository; + private final ArtistInfoRepository artistInfoRepository; + private final SocialRepository socialRepository; - public UserDto getUser(Long userId) { - return UserDto.fromEntity(userRepository.findById(userId).orElseThrow()); + + @Transactional + public UserInfoDto getUserInfo(Long userId) { + System.out.println("userId = " + userId); + UserInfo userInfo = userRepository.findById(userId) + .orElseThrow(() -> new ResourceNotFoundException()) + .getUserInfo(); + + System.out.println("userInfo = " + userInfo); + return UserInfoDto.fromEntity(userInfo); + } + + @Transactional + public UserDto updateUserInfo(UserInfoDto userInfoDto, Long userId) { + + User existingUser = userRepository.findById(userId) + .orElseThrow(() -> new ResourceNotFoundException()); + + existingUser.updateUserInfo(userInfoDto.toEntity()); + + return UserDto.fromEntity(userRepository.save(existingUser)); + } + + @Transactional + public UserTypeDto getUserType(Long userId) { + String usertype = artistInfoRepository.findByUserId(userId) + .map(artistInfo -> artistInfo.getArtistType().getType()) // 값이 있을 때 처리 + .orElse("User"); // 값이 없을 때 기본값 + + //artistType을 확인 + return UserTypeDto.builder() + .userType(usertype) + .build(); + } + + @Transactional + public Page getFollowingWithPaging(Long userId, Pageable pageable) { + + return userRepository.findFollowingUsers(userId, pageable) + .map(UserFollowingRes::fromDto); + } + + @Transactional + public void followArtist(Long userId, Long artistId) { + User user = userRepository.findById(userId) + .orElseThrow(() -> new ResourceNotFoundException()); + + ArtistInfo artistInfo = artistInfoRepository.findByUserId(artistId) + .orElseThrow(() -> new ResourceNotFoundException()); + + if (socialRepository.existsByFollowerAndFollowing(user, artistInfo)) { + throw new IllegalArgumentException("이미 팔로우한 아티스트입니다."); + } + + Social social = Social.builder() + .follower(user) + .following(artistInfo) + .build(); + + socialRepository.save(social); + } + + @Transactional + public void unfollowArtist(Long userId, Long artistId) { + User user = userRepository.findById(userId) + .orElseThrow(() -> new ResourceNotFoundException()); + + ArtistInfo artistInfo = artistInfoRepository.findByUserId(artistId) + .orElseThrow(() -> new ResourceNotFoundException()); + + Social social = socialRepository.findByFollowerAndFollowing(user, artistInfo) + .orElseThrow(() -> new ResourceNotFoundException()); + + socialRepository.delete(social); } } diff --git a/src/test/java/com/helpmeCookies/product/service/ProductImageServiceTest.java b/src/test/java/com/helpmeCookies/product/service/ProductImageServiceTest.java new file mode 100644 index 0000000..b0b3653 --- /dev/null +++ b/src/test/java/com/helpmeCookies/product/service/ProductImageServiceTest.java @@ -0,0 +1,69 @@ +package com.helpmeCookies.product.service; + +import com.helpmeCookies.global.utils.AwsS3FileUtils; +import com.helpmeCookies.product.dto.FileUploadResponse; +import com.helpmeCookies.product.entity.Category; +import com.helpmeCookies.product.entity.Product; +import com.helpmeCookies.product.repository.ProductImageRepository; +import com.helpmeCookies.product.repository.ProductRepository; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.when; + +@SpringBootTest +@ActiveProfiles("test") +class ProductImageServiceTest { + @MockBean + private AwsS3FileUtils awsS3FileUtils; + + private ProductImageService productImageService; + @MockBean + private ProductRepository productRepository; + @MockBean + private ProductImageRepository productImageRepository; + + @BeforeEach + void setUp() { + productImageService = new ProductImageService(awsS3FileUtils, productImageRepository); + } + + @AfterEach + void tearDown() { + } + + @Test + @DisplayName("상품 이미지 S3 서버에 업로드") + void uploadMultiFiles() throws IOException { + given(productRepository.findById(any())) + .willReturn(Optional.of(new Product("더미",Category.CERAMIC,"100",10000L,"테스트항목","테스트 주소", + null,null))); + MockMultipartFile file1 = new MockMultipartFile("test1","img1.jpg","image/jpeg","image content".getBytes()); + MockMultipartFile file2 = new MockMultipartFile("test2","img2.jpg","image/jpeg","image content".getBytes()); + List files = Arrays.asList(file1,file2); + + List expected = new ArrayList<>(); + expected.add(new FileUploadResponse("url1","1111")); + expected.add(new FileUploadResponse("url2","2222")); + when(awsS3FileUtils.uploadMultiImages(files)).thenReturn(expected); + + List actual = productImageService.uploadMultiFiles(1L,files); + assertEquals(2,actual.size(), "배열의 크기는 2여야함"); + } +} diff --git a/src/test/resources/application.yaml b/src/test/resources/application.yaml index bf26694..eea2d27 100644 --- a/src/test/resources/application.yaml +++ b/src/test/resources/application.yaml @@ -21,4 +21,17 @@ logging.level: jwt: secret: testtesttesttesttesttesttesttesttest access-token-expire-time: 1800000 # 30 minutes - refresh-token-expire-time: 2592000000 # 30 days \ No newline at end of file + refresh-token-expire-time: 2592000000 # 30 days + +cloud: + aws: + s3: + bucket: testtest + credentials: + access-key: test-access-key + secret-key: test-secret-key + region: + static: ap-northeast-2 + auto: false + stack: + auto: false