From 0fc3306fbdd58610dc30f156589a5f0f51ca424c Mon Sep 17 00:00:00 2001 From: LujaeC Date: Tue, 21 Jan 2025 00:40:15 +0900 Subject: [PATCH 01/14] =?UTF-8?q?[PC-383]=20fix:=20=EC=97=B0=EB=9D=BD?= =?UTF-8?q?=EC=B2=98=20=EA=B8=B0=EB=B0=98=20=EC=B0=A8=EB=8B=A8=20=EB=84=A4?= =?UTF-8?q?=EC=9D=B4=EB=B0=8D=20=EC=88=98=EC=A0=95=20(Block=20->=20BlockCo?= =?UTF-8?q?ntact)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/BlockContactService.java | 49 +++++++++++++++++ .../block/application/BlockService.java | 49 ----------------- .../dto/BlockContactCreateDto.java | 13 +++++ .../block/application/dto/BlockCreateDto.java | 12 ----- ...itory.java => BlockContactRepository.java} | 10 ++-- .../presentation/BlockContactController.java | 52 +++++++++++++++++++ .../block/presentation/BlockController.java | 40 -------------- ...esponse.java => BlockContactResponse.java} | 3 +- .../response/UserBlockContactResponses.java | 20 +++++++ .../dto/response/UserBlockResponses.java | 17 ------ .../block/{Block.java => BlockContact.java} | 2 +- 11 files changed, 142 insertions(+), 125 deletions(-) create mode 100644 api/src/main/java/org/yapp/domain/block/application/BlockContactService.java delete mode 100644 api/src/main/java/org/yapp/domain/block/application/BlockService.java create mode 100644 api/src/main/java/org/yapp/domain/block/application/dto/BlockContactCreateDto.java delete mode 100644 api/src/main/java/org/yapp/domain/block/application/dto/BlockCreateDto.java rename api/src/main/java/org/yapp/domain/block/dao/{BlockRepository.java => BlockContactRepository.java} (50%) create mode 100644 api/src/main/java/org/yapp/domain/block/presentation/BlockContactController.java delete mode 100644 api/src/main/java/org/yapp/domain/block/presentation/BlockController.java rename api/src/main/java/org/yapp/domain/block/presentation/dto/response/{BlockResponse.java => BlockContactResponse.java} (52%) create mode 100644 api/src/main/java/org/yapp/domain/block/presentation/dto/response/UserBlockContactResponses.java delete mode 100644 api/src/main/java/org/yapp/domain/block/presentation/dto/response/UserBlockResponses.java rename common/domain/src/main/java/org/yapp/domain/block/{Block.java => BlockContact.java} (96%) diff --git a/api/src/main/java/org/yapp/domain/block/application/BlockContactService.java b/api/src/main/java/org/yapp/domain/block/application/BlockContactService.java new file mode 100644 index 0000000..48bd1c6 --- /dev/null +++ b/api/src/main/java/org/yapp/domain/block/application/BlockContactService.java @@ -0,0 +1,49 @@ +package org.yapp.domain.block.application; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.yapp.domain.block.BlockContact; +import org.yapp.domain.block.application.dto.BlockContactCreateDto; +import org.yapp.domain.block.dao.BlockContactRepository; +import org.yapp.domain.user.User; + +@Service +@RequiredArgsConstructor +public class BlockContactService { + + private final BlockContactRepository blockContactRepository; + + @Transactional() + public void blockPhoneNumbers(BlockContactCreateDto blockContactCreateDto) { + Long userId = blockContactCreateDto.userId(); + List phoneNumbers = blockContactCreateDto.phoneNumbers(); + List newBlockContacts = new ArrayList<>(); + + Set blockedPhoneNumbers = blockContactRepository.findBlocksByUserId(userId) + .stream() + .map(BlockContact::getPhoneNumber) + .collect(Collectors.toSet()); + + phoneNumbers.stream() + .filter(phoneNumber -> !blockedPhoneNumbers.contains(phoneNumber)) + .forEach(phoneNumber -> { + BlockContact blockContact = BlockContact.builder() + .user(User.builder().id(userId).build()) + .phoneNumber(phoneNumber) + .build(); + newBlockContacts.add(blockContact); + }); + + blockContactRepository.saveAll(newBlockContacts); + } + + @Transactional(readOnly = false) + public List findBlocksByUserId(Long userId) { + return blockContactRepository.findBlocksByUserId(userId); + } +} diff --git a/api/src/main/java/org/yapp/domain/block/application/BlockService.java b/api/src/main/java/org/yapp/domain/block/application/BlockService.java deleted file mode 100644 index deb9b82..0000000 --- a/api/src/main/java/org/yapp/domain/block/application/BlockService.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.yapp.domain.block.application; - -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.yapp.domain.block.Block; -import org.yapp.domain.block.application.dto.BlockCreateDto; -import org.yapp.domain.block.dao.BlockRepository; -import org.yapp.domain.user.User; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -@Service -@RequiredArgsConstructor -public class BlockService { - private final BlockRepository blockRepository; - - @Transactional() - public void blockPhoneNumbers(BlockCreateDto blockCreateDto) { - Long userId = blockCreateDto.userId(); - List phoneNumbers = blockCreateDto.phoneNumbers(); - List newBlocks = new ArrayList<>(); - - Set blockedPhoneNumbers = blockRepository.findBlocksByUserId(userId) - .stream() - .map(Block::getPhoneNumber) - .collect(Collectors.toSet()); - - phoneNumbers.stream() - .filter(phoneNumber -> !blockedPhoneNumbers.contains(phoneNumber)) - .forEach(phoneNumber -> { - Block block = Block.builder() - .user(User.builder().id(userId).build()) - .phoneNumber(phoneNumber) - .build(); - newBlocks.add(block); - }); - - blockRepository.saveAll(newBlocks); - } - - @Transactional(readOnly = false) - public List findBlocksByUserId(Long userId) { - return blockRepository.findBlocksByUserId(userId); - } -} diff --git a/api/src/main/java/org/yapp/domain/block/application/dto/BlockContactCreateDto.java b/api/src/main/java/org/yapp/domain/block/application/dto/BlockContactCreateDto.java new file mode 100644 index 0000000..81f8363 --- /dev/null +++ b/api/src/main/java/org/yapp/domain/block/application/dto/BlockContactCreateDto.java @@ -0,0 +1,13 @@ +package org.yapp.domain.block.application.dto; + +import java.util.List; + +public record BlockContactCreateDto(Long userId, List phoneNumbers) { + + public BlockContactCreateDto(Long userId, List phoneNumbers) { + this.userId = userId; + this.phoneNumbers = phoneNumbers.stream() + .map(phone -> phone.replaceAll("-", "")) + .toList(); + } +} diff --git a/api/src/main/java/org/yapp/domain/block/application/dto/BlockCreateDto.java b/api/src/main/java/org/yapp/domain/block/application/dto/BlockCreateDto.java deleted file mode 100644 index f769965..0000000 --- a/api/src/main/java/org/yapp/domain/block/application/dto/BlockCreateDto.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.yapp.domain.block.application.dto; - -import java.util.List; - -public record BlockCreateDto(Long userId, List phoneNumbers) { - public BlockCreateDto(Long userId, List phoneNumbers) { - this.userId = userId; - this.phoneNumbers = phoneNumbers.stream() - .map(phone -> phone.replaceAll("-", "")) - .toList(); - } -} diff --git a/api/src/main/java/org/yapp/domain/block/dao/BlockRepository.java b/api/src/main/java/org/yapp/domain/block/dao/BlockContactRepository.java similarity index 50% rename from api/src/main/java/org/yapp/domain/block/dao/BlockRepository.java rename to api/src/main/java/org/yapp/domain/block/dao/BlockContactRepository.java index 2fe32ad..9f5c9e4 100644 --- a/api/src/main/java/org/yapp/domain/block/dao/BlockRepository.java +++ b/api/src/main/java/org/yapp/domain/block/dao/BlockContactRepository.java @@ -1,12 +1,12 @@ package org.yapp.domain.block.dao; +import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; -import org.yapp.domain.block.Block; - -import java.util.List; +import org.yapp.domain.block.BlockContact; @Repository -public interface BlockRepository extends JpaRepository { - List findBlocksByUserId(Long userId); +public interface BlockContactRepository extends JpaRepository { + + List findBlocksByUserId(Long userId); } diff --git a/api/src/main/java/org/yapp/domain/block/presentation/BlockContactController.java b/api/src/main/java/org/yapp/domain/block/presentation/BlockContactController.java new file mode 100644 index 0000000..acf8e38 --- /dev/null +++ b/api/src/main/java/org/yapp/domain/block/presentation/BlockContactController.java @@ -0,0 +1,52 @@ +package org.yapp.domain.block.presentation; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.yapp.domain.block.BlockContact; +import org.yapp.domain.block.application.BlockContactService; +import org.yapp.domain.block.application.dto.BlockContactCreateDto; +import org.yapp.domain.block.presentation.dto.request.BlockPhoneNumbersRequest; +import org.yapp.domain.block.presentation.dto.response.UserBlockContactResponses; +import org.yapp.util.CommonResponse; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/blockContacts") +public class BlockContactController { + + private final BlockContactService blockContactService; + + @PostMapping("") + @Operation(summary = "핸드폰 번호 차단", description = "핸드폰 번호 리스트를 전달받고, 전달받은 핸드폰 번호 차단을 수행합니다.", tags = { + "차단"}) + @ApiResponse(responseCode = "200", description = "핸드폰 차단 성공") + public ResponseEntity> blockPhoneNumbers( + @AuthenticationPrincipal Long userId, @RequestBody BlockPhoneNumbersRequest request) { + blockContactService.blockPhoneNumbers( + new BlockContactCreateDto(userId, request.phoneNumbers())); + return ResponseEntity.status(HttpStatus.OK) + .body(CommonResponse.createSuccessWithNoContent("핸드폰 번호 차단 성공")); + } + + @GetMapping("") + @Operation(summary = "핸드폰 번호 차단", description = "핸드폰 번호 리스트를 전달받고, 전달받은 핸드폰 번호 차단을 수행합니다.", tags = { + "차단"}) + @ApiResponse(responseCode = "200", description = "핸드폰 차단 성공") + public ResponseEntity> blockPhoneNumbers( + @AuthenticationPrincipal Long userId) { + List blockContacts = blockContactService.findBlocksByUserId(userId); + return ResponseEntity.status(HttpStatus.OK) + .body(CommonResponse.createSuccess(UserBlockContactResponses.from(userId, + blockContacts))); + } +} diff --git a/api/src/main/java/org/yapp/domain/block/presentation/BlockController.java b/api/src/main/java/org/yapp/domain/block/presentation/BlockController.java deleted file mode 100644 index 8440371..0000000 --- a/api/src/main/java/org/yapp/domain/block/presentation/BlockController.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.yapp.domain.block.presentation; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.web.bind.annotation.*; -import org.yapp.domain.block.Block; -import org.yapp.domain.block.application.BlockService; -import org.yapp.domain.block.application.dto.BlockCreateDto; -import org.yapp.domain.block.presentation.dto.request.BlockPhoneNumbersRequest; -import org.yapp.domain.block.presentation.dto.response.UserBlockResponses; -import org.yapp.util.CommonResponse; - -import java.util.List; - -@RestController -@RequiredArgsConstructor -@RequestMapping("/api/blocks") -public class BlockController { - private final BlockService blockService; - - @PostMapping("") - @Operation(summary = "핸드폰 번호 차단", description = "핸드폰 번호 리스트를 전달받고, 전달받은 핸드폰 번호 차단을 수행합니다.", tags = {"차단"}) - @ApiResponse(responseCode = "200", description = "핸드폰 차단 성공") - public ResponseEntity> blockPhoneNumbers(@AuthenticationPrincipal Long userId, @RequestBody BlockPhoneNumbersRequest request) { - blockService.blockPhoneNumbers(new BlockCreateDto(userId, request.phoneNumbers())); - return ResponseEntity.status(HttpStatus.OK).body(CommonResponse.createSuccessWithNoContent("핸드폰 번호 차단 성공")); - } - - @GetMapping("") - @Operation(summary = "핸드폰 번호 차단", description = "핸드폰 번호 리스트를 전달받고, 전달받은 핸드폰 번호 차단을 수행합니다.", tags = {"차단"}) - @ApiResponse(responseCode = "200", description = "핸드폰 차단 성공") - public ResponseEntity> blockPhoneNumbers(@AuthenticationPrincipal Long userId) { - List blocks = blockService.findBlocksByUserId(userId); - return ResponseEntity.status(HttpStatus.OK).body(CommonResponse.createSuccess(UserBlockResponses.from(userId, blocks))); - } -} diff --git a/api/src/main/java/org/yapp/domain/block/presentation/dto/response/BlockResponse.java b/api/src/main/java/org/yapp/domain/block/presentation/dto/response/BlockContactResponse.java similarity index 52% rename from api/src/main/java/org/yapp/domain/block/presentation/dto/response/BlockResponse.java rename to api/src/main/java/org/yapp/domain/block/presentation/dto/response/BlockContactResponse.java index 468de9d..32a0af2 100644 --- a/api/src/main/java/org/yapp/domain/block/presentation/dto/response/BlockResponse.java +++ b/api/src/main/java/org/yapp/domain/block/presentation/dto/response/BlockContactResponse.java @@ -2,5 +2,6 @@ import java.time.LocalDateTime; -public record BlockResponse(String phoneNumber, LocalDateTime blockedAt) { +public record BlockContactResponse(String phoneNumber, LocalDateTime blockedAt) { + } diff --git a/api/src/main/java/org/yapp/domain/block/presentation/dto/response/UserBlockContactResponses.java b/api/src/main/java/org/yapp/domain/block/presentation/dto/response/UserBlockContactResponses.java new file mode 100644 index 0000000..534a839 --- /dev/null +++ b/api/src/main/java/org/yapp/domain/block/presentation/dto/response/UserBlockContactResponses.java @@ -0,0 +1,20 @@ +package org.yapp.domain.block.presentation.dto.response; + +import java.util.List; +import org.yapp.domain.block.BlockContact; + +public record UserBlockContactResponses(Long userId, + List blockContactResponses) { + + public static UserBlockContactResponses from(Long userId, List blockContacts) { + List blockContactResponses = blockContacts.stream().map( + blockContact -> new BlockContactResponse(blockContact.getPhoneNumber(), + blockContact.getCreatedAt())).toList(); + + return new UserBlockContactResponses( + userId, + blockContactResponses + ); + } + +} diff --git a/api/src/main/java/org/yapp/domain/block/presentation/dto/response/UserBlockResponses.java b/api/src/main/java/org/yapp/domain/block/presentation/dto/response/UserBlockResponses.java deleted file mode 100644 index 05bde3a..0000000 --- a/api/src/main/java/org/yapp/domain/block/presentation/dto/response/UserBlockResponses.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.yapp.domain.block.presentation.dto.response; - -import org.yapp.domain.block.Block; - -import java.util.List; - -public record UserBlockResponses (Long userId, List blockResponses) { - public static UserBlockResponses from (Long userId, List blocks) { - List blockResponses = blocks.stream().map(block -> new BlockResponse(block.getPhoneNumber(), block.getCreatedAt())).toList(); - - return new UserBlockResponses( - userId, - blockResponses - ); - } - -} diff --git a/common/domain/src/main/java/org/yapp/domain/block/Block.java b/common/domain/src/main/java/org/yapp/domain/block/BlockContact.java similarity index 96% rename from common/domain/src/main/java/org/yapp/domain/block/Block.java rename to common/domain/src/main/java/org/yapp/domain/block/BlockContact.java index f1400b4..c7cf114 100644 --- a/common/domain/src/main/java/org/yapp/domain/block/Block.java +++ b/common/domain/src/main/java/org/yapp/domain/block/BlockContact.java @@ -31,7 +31,7 @@ @Index(name = "idx_phone_number", columnList = "phoneNumber"), } ) -public class Block extends BaseEntity { +public class BlockContact extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) From 3a500dc1dc701eb35dfe9b82f14365afbeac7e0a Mon Sep 17 00:00:00 2001 From: LujaeC Date: Tue, 21 Jan 2025 01:19:20 +0900 Subject: [PATCH 02/14] =?UTF-8?q?[PC-383]=20feat:=20=EC=B0=A8=EB=8B=A8=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../block/application/UserBlockService.java | 53 +++++++++++++++++++ .../yapp/block/dao/UserBlockRepository.java | 10 ++++ .../presentation/UserBlockController.java | 31 +++++++++++ .../response/UserBlockResponse.java | 12 +++++ .../java/org/yapp/domain/block/UserBlock.java | 37 +++++++++++++ 5 files changed, 143 insertions(+) create mode 100644 admin/src/main/java/org/yapp/block/application/UserBlockService.java create mode 100644 admin/src/main/java/org/yapp/block/dao/UserBlockRepository.java create mode 100644 admin/src/main/java/org/yapp/block/presentation/UserBlockController.java create mode 100644 admin/src/main/java/org/yapp/block/presentation/response/UserBlockResponse.java create mode 100644 common/domain/src/main/java/org/yapp/domain/block/UserBlock.java diff --git a/admin/src/main/java/org/yapp/block/application/UserBlockService.java b/admin/src/main/java/org/yapp/block/application/UserBlockService.java new file mode 100644 index 0000000..0172f2b --- /dev/null +++ b/admin/src/main/java/org/yapp/block/application/UserBlockService.java @@ -0,0 +1,53 @@ +package org.yapp.block.application; + +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Service; +import org.yapp.block.dao.UserBlockRepository; +import org.yapp.block.presentation.response.UserBlockResponse; +import org.yapp.domain.block.UserBlock; +import org.yapp.domain.user.User; +import org.yapp.util.PageResponse; + +@Service +@RequiredArgsConstructor +public class UserBlockService { + + private final UserBlockRepository userBlockRepository; + + public PageResponse getUserBlockPageResponse(int page, int size) { + Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createdAt")); + + Page userBlockPage = userBlockRepository.findAll(pageable); + + List content = userBlockPage.getContent().stream() + .map(userBlock -> { + User blockingUser = userBlock.getBlockingUser(); + User blockedUser = userBlock.getBlockedUser(); + + return new UserBlockResponse( + blockedUser.getId(), + blockedUser.getProfile().getProfileBasic().getNickname(), + blockedUser.getName(), + blockedUser.getProfile().getProfileBasic().getBirthdate(), + blockingUser.getId(), + blockingUser.getProfile().getProfileBasic().getNickname(), + blockingUser.getName(), + userBlock.getCreatedAt().toLocalDate()); + }).toList(); + + return new PageResponse<>( + content, + userBlockPage.getNumber(), + userBlockPage.getSize(), + userBlockPage.getTotalPages(), + userBlockPage.getTotalElements(), + userBlockPage.isFirst(), + userBlockPage.isLast() + ); + } +} diff --git a/admin/src/main/java/org/yapp/block/dao/UserBlockRepository.java b/admin/src/main/java/org/yapp/block/dao/UserBlockRepository.java new file mode 100644 index 0000000..b2dc02f --- /dev/null +++ b/admin/src/main/java/org/yapp/block/dao/UserBlockRepository.java @@ -0,0 +1,10 @@ +package org.yapp.block.dao; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import org.yapp.domain.block.UserBlock; + +@Repository +public interface UserBlockRepository extends JpaRepository { + +} diff --git a/admin/src/main/java/org/yapp/block/presentation/UserBlockController.java b/admin/src/main/java/org/yapp/block/presentation/UserBlockController.java new file mode 100644 index 0000000..28e25ac --- /dev/null +++ b/admin/src/main/java/org/yapp/block/presentation/UserBlockController.java @@ -0,0 +1,31 @@ +package org.yapp.block.presentation; + +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.yapp.block.application.UserBlockService; +import org.yapp.block.presentation.response.UserBlockResponse; +import org.yapp.util.CommonResponse; +import org.yapp.util.PageResponse; + +@RestController() +@RequiredArgsConstructor +@RequestMapping("/admin/v1/blocks") +public class UserBlockController { + + private final UserBlockService userBlockService; + + @GetMapping("") + public ResponseEntity>> getUsers( + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "10") int size) { + + PageResponse userBlockPageResponse = userBlockService.getUserBlockPageResponse( + page, size); + + return ResponseEntity.ok(CommonResponse.createSuccess(userBlockPageResponse)); + } +} diff --git a/admin/src/main/java/org/yapp/block/presentation/response/UserBlockResponse.java b/admin/src/main/java/org/yapp/block/presentation/response/UserBlockResponse.java new file mode 100644 index 0000000..d49c282 --- /dev/null +++ b/admin/src/main/java/org/yapp/block/presentation/response/UserBlockResponse.java @@ -0,0 +1,12 @@ +package org.yapp.block.presentation.response; + +import java.time.LocalDate; + +public record UserBlockResponse( + Long blockedUserId, + String BlockedUserNickname, String BlockedUserName, + LocalDate blockedUserBirthdate, Long blockingUserId, + String blockingUserNickname, + String blockingUserName, LocalDate BlockedDate) { + +} diff --git a/common/domain/src/main/java/org/yapp/domain/block/UserBlock.java b/common/domain/src/main/java/org/yapp/domain/block/UserBlock.java new file mode 100644 index 0000000..df1d1ea --- /dev/null +++ b/common/domain/src/main/java/org/yapp/domain/block/UserBlock.java @@ -0,0 +1,37 @@ +package org.yapp.domain.block; + + +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.ManyToOne; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.yapp.domain.BaseEntity; +import org.yapp.domain.user.User; + +@Entity +@Builder +@Getter +@AllArgsConstructor +@NoArgsConstructor +public class UserBlock extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "user_block_id") + private Long id; + + @ManyToOne + @JoinColumn(name = "blocking_user_id", nullable = false) + private User BlockingUser; + + @ManyToOne + @JoinColumn(name = "blocked_user_id", nullable = false) + private User BlockedUser; +} From 42cdd4f5b1841a8ac1a71701b0a42347d9654616 Mon Sep 17 00:00:00 2001 From: LujaeC Date: Tue, 21 Jan 2025 01:19:35 +0900 Subject: [PATCH 03/14] =?UTF-8?q?[PC-383]=20feat:=20=EC=B0=A8=EB=8B=A8=20?= =?UTF-8?q?=EB=8D=94=EB=AF=B8=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/src/main/resources/init.sql | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/api/src/main/resources/init.sql b/api/src/main/resources/init.sql index 9f360cb..362f2d4 100644 --- a/api/src/main/resources/init.sql +++ b/api/src/main/resources/init.sql @@ -71,6 +71,12 @@ VALUES (11, 3, 2, '부적절한 언어 사용 2222', NOW(), NOW()); INSERT INTO report (report_id, reporter_user_id, reported_user_id, reason, created_at, updated_at) VALUES (12, 1, 2, '부적절한 언어 사용 3333', NOW(), NOW()); +INSERT INTO user_block (user_block_id, blocking_user_id, blocked_user_id, created_at, updated_at) +VALUES (1, 1, 2, NOW(), NOW()), + (2, 2, 3, NOW(), NOW()), + (3, 3, 4, NOW(), NOW()), + (4, 4, 1, NOW(), NOW()), + (5, 1, 3, NOW(), NOW()); -- ValueItem 더미 데이터 삽입 INSERT INTO value_pick (value_pick_id, category, question, answers, is_active) From c62fb0bad1a5ee09e970cbc74bb9afff874c58cc Mon Sep 17 00:00:00 2001 From: LujaeC Date: Thu, 23 Jan 2025 19:52:14 +0900 Subject: [PATCH 04/14] =?UTF-8?q?[PC-382]=20fix:=20RoleStatus=20=ED=8C=A8?= =?UTF-8?q?=ED=82=A4=EC=A7=80=20=EC=9C=84=EC=B9=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/auth/application/oauth/service/OauthService.java | 2 +- .../org/yapp/domain/profile/application/ProfileService.java | 2 +- .../main/java/org/yapp/domain/user/application/UserService.java | 2 +- .../domain/src/main/java/org/yapp/domain/user}/RoleStatus.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename {api/src/main/java/org/yapp/domain/auth/presentation/dto/enums => common/domain/src/main/java/org/yapp/domain/user}/RoleStatus.java (85%) diff --git a/api/src/main/java/org/yapp/domain/auth/application/oauth/service/OauthService.java b/api/src/main/java/org/yapp/domain/auth/application/oauth/service/OauthService.java index dec7aed..0c0277d 100644 --- a/api/src/main/java/org/yapp/domain/auth/application/oauth/service/OauthService.java +++ b/api/src/main/java/org/yapp/domain/auth/application/oauth/service/OauthService.java @@ -6,9 +6,9 @@ import org.yapp.domain.auth.application.jwt.JwtUtil; import org.yapp.domain.auth.application.oauth.OauthProvider; import org.yapp.domain.auth.application.oauth.OauthProviderResolver; -import org.yapp.domain.auth.presentation.dto.enums.RoleStatus; import org.yapp.domain.auth.presentation.dto.request.OauthLoginRequest; import org.yapp.domain.auth.presentation.dto.response.OauthLoginResponse; +import org.yapp.domain.user.RoleStatus; import org.yapp.domain.user.User; import org.yapp.domain.user.dao.UserRepository; diff --git a/api/src/main/java/org/yapp/domain/profile/application/ProfileService.java b/api/src/main/java/org/yapp/domain/profile/application/ProfileService.java index 5b6de76..70ce1ab 100644 --- a/api/src/main/java/org/yapp/domain/profile/application/ProfileService.java +++ b/api/src/main/java/org/yapp/domain/profile/application/ProfileService.java @@ -7,7 +7,6 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.yapp.domain.auth.presentation.dto.enums.RoleStatus; import org.yapp.domain.profile.Profile; import org.yapp.domain.profile.ProfileBasic; import org.yapp.domain.profile.ProfileValuePick; @@ -19,6 +18,7 @@ import org.yapp.domain.profile.presentation.request.ProfileValuePickUpdateRequest.ProfileValuePickPair; import org.yapp.domain.profile.presentation.request.ProfileValueTalkUpdateRequest; import org.yapp.domain.profile.presentation.request.ProfileValueTalkUpdateRequest.ProfileValueTalkPair; +import org.yapp.domain.user.RoleStatus; import org.yapp.domain.user.User; import org.yapp.domain.user.application.UserService; import org.yapp.error.dto.ProfileErrorCode; diff --git a/api/src/main/java/org/yapp/domain/user/application/UserService.java b/api/src/main/java/org/yapp/domain/user/application/UserService.java index cc7b6ef..f6153d4 100644 --- a/api/src/main/java/org/yapp/domain/user/application/UserService.java +++ b/api/src/main/java/org/yapp/domain/user/application/UserService.java @@ -5,9 +5,9 @@ import org.springframework.transaction.annotation.Transactional; import org.yapp.application.AuthenticationService; import org.yapp.domain.auth.application.jwt.JwtUtil; -import org.yapp.domain.auth.presentation.dto.enums.RoleStatus; import org.yapp.domain.auth.presentation.dto.response.OauthLoginResponse; import org.yapp.domain.profile.Profile; +import org.yapp.domain.user.RoleStatus; import org.yapp.domain.user.User; import org.yapp.domain.user.dao.UserRepository; import org.yapp.error.dto.UserErrorCode; diff --git a/api/src/main/java/org/yapp/domain/auth/presentation/dto/enums/RoleStatus.java b/common/domain/src/main/java/org/yapp/domain/user/RoleStatus.java similarity index 85% rename from api/src/main/java/org/yapp/domain/auth/presentation/dto/enums/RoleStatus.java rename to common/domain/src/main/java/org/yapp/domain/user/RoleStatus.java index 192f4c0..35d1b71 100644 --- a/api/src/main/java/org/yapp/domain/auth/presentation/dto/enums/RoleStatus.java +++ b/common/domain/src/main/java/org/yapp/domain/user/RoleStatus.java @@ -1,4 +1,4 @@ -package org.yapp.domain.auth.presentation.dto.enums; +package org.yapp.domain.user; import com.fasterxml.jackson.annotation.JsonValue; import lombok.AllArgsConstructor; From bec314926514e93c2efb24dc15f255067afb5301 Mon Sep 17 00:00:00 2001 From: LujaeC Date: Thu, 23 Jan 2025 20:12:33 +0900 Subject: [PATCH 05/14] =?UTF-8?q?[PC-382]=20fix:=20ManyToOne=20=EA=B4=80?= =?UTF-8?q?=EA=B3=84=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/yapp/domain/profile/ProfileRejectHistory.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/common/domain/src/main/java/org/yapp/domain/profile/ProfileRejectHistory.java b/common/domain/src/main/java/org/yapp/domain/profile/ProfileRejectHistory.java index 392b4ae..c182028 100644 --- a/common/domain/src/main/java/org/yapp/domain/profile/ProfileRejectHistory.java +++ b/common/domain/src/main/java/org/yapp/domain/profile/ProfileRejectHistory.java @@ -2,11 +2,12 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; -import jakarta.persistence.OneToOne; +import jakarta.persistence.ManyToOne; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -26,7 +27,7 @@ public class ProfileRejectHistory extends BaseEntity { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @OneToOne() + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") private User user; From 986e18295d5eec82265c946ac8ff155c2e169306 Mon Sep 17 00:00:00 2001 From: LujaeC Date: Thu, 23 Jan 2025 20:30:11 +0900 Subject: [PATCH 06/14] =?UTF-8?q?[PC-382]=20fix:=20response=20=EB=B3=80?= =?UTF-8?q?=EC=88=98=20=EC=9D=B4=EB=A6=84=20=EC=88=98=EC=A0=95=20(reason?= =?UTF-8?q?=20->=20reject)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../response/UserProfileValidationResponse.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/admin/src/main/java/org/yapp/user/presentation/response/UserProfileValidationResponse.java b/admin/src/main/java/org/yapp/user/presentation/response/UserProfileValidationResponse.java index 5e3bbae..a02e948 100644 --- a/admin/src/main/java/org/yapp/user/presentation/response/UserProfileValidationResponse.java +++ b/admin/src/main/java/org/yapp/user/presentation/response/UserProfileValidationResponse.java @@ -10,11 +10,11 @@ public record UserProfileValidationResponse(Long userId, String description, String nickname, String name, LocalDate birthdate, String phoneNumber, LocalDate joinDate, - String profileStatus, boolean reasonImage, - boolean reasonDescription) { + String profileStatus, boolean rejectImage, + boolean rejectDescription) { - public static UserProfileValidationResponse from(User user, boolean reasonImage, - boolean reasonDescription) { + public static UserProfileValidationResponse from(User user, boolean rejectImage, + boolean rejectDescription) { Profile profile = user.getProfile(); return UserProfileValidationResponse.builder() @@ -26,8 +26,8 @@ public static UserProfileValidationResponse from(User user, boolean reasonImage, .phoneNumber(user.getPhoneNumber() != null ? user.getPhoneNumber() : null) .joinDate(user.getCreatedAt() != null ? user.getCreatedAt().toLocalDate() : null) .profileStatus(profile != null ? profile.getProfileStatus().getDisplayName() : null) - .reasonImage(reasonImage) - .reasonDescription(reasonDescription) + .rejectImage(rejectImage) + .rejectDescription(rejectDescription) .build(); } } From ff5eb5072948bbd25a8ae29ef0608cf963dec748 Mon Sep 17 00:00:00 2001 From: LujaeC Date: Thu, 23 Jan 2025 20:30:39 +0900 Subject: [PATCH 07/14] =?UTF-8?q?[PC-382]=20feat:=20=EA=B4=80=EB=A6=AC?= =?UTF-8?q?=EC=9E=90=20=EC=9C=A0=EC=A0=80=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=EC=88=98=EC=A0=95=20=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/AdminProfileService.java | 53 +++++++++++++++++++ .../user/presentation/UserController.java | 18 ++++++- .../java/org/yapp/domain/profile/Profile.java | 4 ++ 3 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 admin/src/main/java/org/yapp/profile/application/AdminProfileService.java diff --git a/admin/src/main/java/org/yapp/profile/application/AdminProfileService.java b/admin/src/main/java/org/yapp/profile/application/AdminProfileService.java new file mode 100644 index 0000000..c0ae7ca --- /dev/null +++ b/admin/src/main/java/org/yapp/profile/application/AdminProfileService.java @@ -0,0 +1,53 @@ +package org.yapp.profile.application; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.yapp.domain.profile.Profile; +import org.yapp.domain.profile.ProfileRejectHistory; +import org.yapp.domain.profile.ProfileStatus; +import org.yapp.domain.user.RoleStatus; +import org.yapp.domain.user.User; +import org.yapp.error.dto.ProfileErrorCode; +import org.yapp.error.exception.ApplicationException; +import org.yapp.profile.dao.ProfileRejectHistoryRepository; +import org.yapp.user.application.UserService; + +@Service +@RequiredArgsConstructor +public class AdminProfileService { + + private final ProfileRejectHistoryRepository profileRejectHistoryRepository; + private final UserService userService; + + @Transactional + public void updateProfileStatus(Long userId, boolean reasonImage, boolean reasonDescription) { + User user = userService.getUserById(userId); + Profile profile = user.getProfile(); + + if (profile == null) { + throw new ApplicationException(ProfileErrorCode.NOTFOUND_PROFILE); + } + + if (reasonImage || reasonDescription) { + rejectProfile(user.getProfile(), reasonImage, reasonDescription); + } else { + passProfile(profile); + } + } + + private void rejectProfile(Profile profile, boolean reasonImage, boolean reasonDescription) { + profileRejectHistoryRepository.save(ProfileRejectHistory.builder() + .user(profile.getUser()) + .reasonImage(reasonImage) + .reasonDescription(reasonDescription) + .build()); + + profile.updateProfileStatus(ProfileStatus.REJECTED); + } + + private void passProfile(Profile profile) { + profile.updateProfileStatus(ProfileStatus.APPROVED); + profile.getUser().updateUserRole(RoleStatus.USER.getStatus()); + } +} diff --git a/admin/src/main/java/org/yapp/user/presentation/UserController.java b/admin/src/main/java/org/yapp/user/presentation/UserController.java index ab6fcd7..ad35f04 100644 --- a/admin/src/main/java/org/yapp/user/presentation/UserController.java +++ b/admin/src/main/java/org/yapp/user/presentation/UserController.java @@ -1,12 +1,16 @@ package org.yapp.user.presentation; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; 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.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import org.yapp.profile.application.AdminProfileService; import org.yapp.user.application.UserService; import org.yapp.user.presentation.response.UserProfileDetailResponses; import org.yapp.user.presentation.response.UserProfileValidationResponse; @@ -19,6 +23,7 @@ public class UserController { private final UserService userService; + private final AdminProfileService adminProfileService; @GetMapping("") public ResponseEntity>> getUsers( @@ -33,9 +38,20 @@ public ResponseEntity @GetMapping("/{userId}") public ResponseEntity> getUserProfile( - @PathVariable long userId) { + @PathVariable Long userId) { UserProfileDetailResponses userProfileDetails = userService.getUserProfileDetails(userId); return ResponseEntity.ok(CommonResponse.createSuccess(userProfileDetails)); } + + @PostMapping("/{userId}/profile") + public ResponseEntity> updateUserProfileStatus( + @PathVariable Long userId, + @RequestBody @Valid UpdateProfileStatusRequest request) { + + adminProfileService.updateProfileStatus(userId, request.rejectImage(), + request.rejectDescription()); + + return ResponseEntity.ok(CommonResponse.createSuccess(null)); + } } diff --git a/common/domain/src/main/java/org/yapp/domain/profile/Profile.java b/common/domain/src/main/java/org/yapp/domain/profile/Profile.java index df1b562..2c0d53f 100644 --- a/common/domain/src/main/java/org/yapp/domain/profile/Profile.java +++ b/common/domain/src/main/java/org/yapp/domain/profile/Profile.java @@ -68,4 +68,8 @@ public void updateProfileValuePicks(List profileValuePicks) { public void updateProfileValueTalks(List profileValueTalks) { this.profileValueTalks = profileValueTalks; } + + public void updateProfileStatus(ProfileStatus profileStatus) { + this.profileStatus = profileStatus; + } } From 1ec6d93606eb79e2d2fb281cca1dc2d25f20cfee Mon Sep 17 00:00:00 2001 From: LujaeC Date: Thu, 23 Jan 2025 20:30:42 +0900 Subject: [PATCH 08/14] =?UTF-8?q?[PC-382]=20feat:=20=EA=B4=80=EB=A6=AC?= =?UTF-8?q?=EC=9E=90=20=EC=9C=A0=EC=A0=80=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=EC=88=98=EC=A0=95=20=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/presentation/UpdateProfileStatusRequest.java | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 admin/src/main/java/org/yapp/user/presentation/UpdateProfileStatusRequest.java diff --git a/admin/src/main/java/org/yapp/user/presentation/UpdateProfileStatusRequest.java b/admin/src/main/java/org/yapp/user/presentation/UpdateProfileStatusRequest.java new file mode 100644 index 0000000..c74d1c2 --- /dev/null +++ b/admin/src/main/java/org/yapp/user/presentation/UpdateProfileStatusRequest.java @@ -0,0 +1,8 @@ +package org.yapp.user.presentation; + +import jakarta.validation.constraints.NotNull; + +public record UpdateProfileStatusRequest(@NotNull boolean rejectImage, + @NotNull boolean rejectDescription) { + +} From fadde4fe90ee43b18e122b17341334ef7ff90d40 Mon Sep 17 00:00:00 2001 From: LujaeC Date: Thu, 23 Jan 2025 20:33:29 +0900 Subject: [PATCH 09/14] =?UTF-8?q?[PC-382]=20feat:=20=EB=A6=AC=EC=A0=9D=20?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EA=B2=BD=EC=9A=B0=20=EB=A9=A4=EB=B2=84=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20PENDING=EC=9C=BC=EB=A1=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/yapp/profile/application/AdminProfileService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/admin/src/main/java/org/yapp/profile/application/AdminProfileService.java b/admin/src/main/java/org/yapp/profile/application/AdminProfileService.java index c0ae7ca..0d98dee 100644 --- a/admin/src/main/java/org/yapp/profile/application/AdminProfileService.java +++ b/admin/src/main/java/org/yapp/profile/application/AdminProfileService.java @@ -44,6 +44,7 @@ private void rejectProfile(Profile profile, boolean reasonImage, boolean reasonD .build()); profile.updateProfileStatus(ProfileStatus.REJECTED); + profile.getUser().updateUserRole(RoleStatus.PENDING.getStatus()); } private void passProfile(Profile profile) { From aee284c2c3d3046daad3ff74c7db9f24f46ab8e9 Mon Sep 17 00:00:00 2001 From: LujaeC Date: Thu, 23 Jan 2025 22:26:28 +0900 Subject: [PATCH 10/14] =?UTF-8?q?[PC-379]=20fix:=20auth=20=EB=AA=A8?= =?UTF-8?q?=EB=93=88=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- admin/build.gradle | 3 +- .../main/java/org/yapp/auth/AuthToken.java | 5 ++ .../org/yapp/auth/AuthTokenGenerator.java | 30 +++++++ .../src/main/java/org/yapp/jwt/JwtFilter.java | 68 ++++++++++++++ .../src/main/java/org/yapp/jwt/JwtUtil.java | 89 +++++++++++++++++++ settings.gradle | 2 + 6 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 common/auth/src/main/java/org/yapp/auth/AuthToken.java create mode 100644 common/auth/src/main/java/org/yapp/auth/AuthTokenGenerator.java create mode 100644 common/auth/src/main/java/org/yapp/jwt/JwtFilter.java create mode 100644 common/auth/src/main/java/org/yapp/jwt/JwtUtil.java diff --git a/admin/build.gradle b/admin/build.gradle index 5c74e14..fd1696b 100644 --- a/admin/build.gradle +++ b/admin/build.gradle @@ -13,7 +13,8 @@ dependencies { implementation project(':common:domain') implementation project(':common:format') implementation project(':common:exception') - + implementation project(':common:auth') + testImplementation platform('org.junit:junit-bom:5.10.0') testImplementation 'org.junit.jupiter:junit-jupiter' } diff --git a/common/auth/src/main/java/org/yapp/auth/AuthToken.java b/common/auth/src/main/java/org/yapp/auth/AuthToken.java new file mode 100644 index 0000000..8ec9d57 --- /dev/null +++ b/common/auth/src/main/java/org/yapp/auth/AuthToken.java @@ -0,0 +1,5 @@ +package org.yapp.auth; + +public record AuthToken(String accessToken, String refreshToken) { + +} diff --git a/common/auth/src/main/java/org/yapp/auth/AuthTokenGenerator.java b/common/auth/src/main/java/org/yapp/auth/AuthTokenGenerator.java new file mode 100644 index 0000000..fd6778e --- /dev/null +++ b/common/auth/src/main/java/org/yapp/auth/AuthTokenGenerator.java @@ -0,0 +1,30 @@ +package org.yapp.auth; + +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.yapp.jwt.JwtUtil; + +@Component +@RequiredArgsConstructor +public class AuthTokenGenerator { + + private final JwtUtil jwtUtil; + + @Value("${jwt.accessToken.expiration}") + private Long accessTokenExpiration; + + @Value("${jwt.refreshToken.expiration}") + private Long refreshTokenExpiration; + + + public AuthToken generate(Long userId, String oauthId, String role) { + String accessToken = jwtUtil.createJwt("access_token", userId, oauthId, role, + accessTokenExpiration); + + String refreshToken = jwtUtil.createJwt("refesh_token", userId, oauthId, role, + refreshTokenExpiration); + + return new AuthToken(accessToken, refreshToken); + } +} diff --git a/common/auth/src/main/java/org/yapp/jwt/JwtFilter.java b/common/auth/src/main/java/org/yapp/jwt/JwtFilter.java new file mode 100644 index 0000000..4d0ac2b --- /dev/null +++ b/common/auth/src/main/java/org/yapp/jwt/JwtFilter.java @@ -0,0 +1,68 @@ +package org.yapp.jwt; + +import io.jsonwebtoken.ExpiredJwtException; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Collections; +import lombok.RequiredArgsConstructor; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +@RequiredArgsConstructor +@Component +public class JwtFilter extends OncePerRequestFilter { + + private final JwtUtil jwtUtil; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, + FilterChain filterChain) + throws ServletException, IOException { + + String accessToken = request.getHeader("Authorization"); + if (accessToken == null || !accessToken.startsWith("Bearer ")) { + filterChain.doFilter(request, response); + return; + } + accessToken = accessToken.substring(7); + + try { + jwtUtil.isExpired(accessToken); + } catch (ExpiredJwtException e) { + + PrintWriter writer = response.getWriter(); + writer.print("access token expired"); + + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + return; + } + + String category = jwtUtil.getCategory(accessToken); + if (!category.equals("access_token")) { + PrintWriter writer = response.getWriter(); + writer.print("invalid access token"); + + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + return; + } + + Long userId = jwtUtil.getUserId(accessToken); + String role = jwtUtil.getRole(accessToken); + + Authentication authToken = + new UsernamePasswordAuthenticationToken(userId, null, Collections.singleton( + (GrantedAuthority) () -> role)); + + SecurityContextHolder.getContext().setAuthentication(authToken); + + filterChain.doFilter(request, response); + } +} diff --git a/common/auth/src/main/java/org/yapp/jwt/JwtUtil.java b/common/auth/src/main/java/org/yapp/jwt/JwtUtil.java new file mode 100644 index 0000000..1db9c7f --- /dev/null +++ b/common/auth/src/main/java/org/yapp/jwt/JwtUtil.java @@ -0,0 +1,89 @@ +package org.yapp.jwt; + +import io.jsonwebtoken.Jwts; +import java.nio.charset.StandardCharsets; +import java.util.Date; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +public class JwtUtil { + + private final SecretKey secretKey; + + public JwtUtil(@Value("${jwt.secret}") String secret) { + secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), + Jwts.SIG.HS256.key().build().getAlgorithm()); + } + + public String getOauthId(String token) { + String oauthId; + try { + oauthId = Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token) + .getPayload().get("oauthId", String.class); + } catch (Exception e) { + throw new RuntimeException(); + } + return oauthId; + } + + public String getRole(String token) { + String role; + try { + role = Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload() + .get("role", String.class); + } catch (Exception e) { + throw new RuntimeException(); + } + return role; + } + + public Boolean isExpired(String token) { + boolean before; + try { + before = Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token) + .getPayload().getExpiration().before(new Date()); + } catch (Exception e) { + throw new RuntimeException(); + } + return before; + } + + public String createJwt(String category, Long userId, String oauthId, String role, + Long expiredMs) { + return Jwts.builder() + .claim("category", category) + .claim("userId", userId) + .claim("oauthId", oauthId) + .claim("role", role) + .issuedAt(new Date(System.currentTimeMillis())) + .expiration(new Date(System.currentTimeMillis() + expiredMs)) + .signWith(secretKey) + .compact(); + } + + public Long getUserId(String token) { + Long userId; + try { + userId = Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token) + .getPayload().get("userId", Long.class); + } catch (Exception e) { + throw new RuntimeException(); + } + return userId; + } + + public String getCategory(String token) { + String category; + try { + category = Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token) + .getPayload().get("category", String.class); + } catch (Exception e) { + throw new RuntimeException(); + } + return category; + } +} + diff --git a/settings.gradle b/settings.gradle index d3c3d28..6b03748 100644 --- a/settings.gradle +++ b/settings.gradle @@ -8,4 +8,6 @@ include 'common:exception' findProject(':common:exception')?.name = 'exception' include 'common:format' findProject(':common:format')?.name = 'format' +include 'common:auth' +findProject(':common:auth')?.name = 'auth' From af3bfa28585975e151ebe66ffafb7db33d0c642c Mon Sep 17 00:00:00 2001 From: LujaeC Date: Thu, 23 Jan 2025 22:27:28 +0900 Subject: [PATCH 11/14] =?UTF-8?q?[PC-379]=20feat:=20admin=20=EB=AA=A8?= =?UTF-8?q?=EB=93=88=20oauthId=20=EA=B8=B0=EB=B0=98=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/org/yapp/user/application/UserService.java | 5 +++++ admin/src/main/java/org/yapp/user/dao/UserRepository.java | 2 ++ 2 files changed, 7 insertions(+) diff --git a/admin/src/main/java/org/yapp/user/application/UserService.java b/admin/src/main/java/org/yapp/user/application/UserService.java index c206404..2e479f2 100644 --- a/admin/src/main/java/org/yapp/user/application/UserService.java +++ b/admin/src/main/java/org/yapp/user/application/UserService.java @@ -36,6 +36,11 @@ public User getUserById(Long userId) { .orElseThrow(() -> new ApplicationException(UserErrorCode.NOTFOUND_USER)); } + public User getUserByOauthId(String oauthId) { + return userRepository.findByOauthId(oauthId) + .orElseThrow(() -> new ApplicationException(UserErrorCode.NOTFOUND_USER)); + } + public PageResponse getUserProfilesWithPagination(int page, int size) { Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createdAt")); diff --git a/admin/src/main/java/org/yapp/user/dao/UserRepository.java b/admin/src/main/java/org/yapp/user/dao/UserRepository.java index ce89bd9..e1d19a8 100644 --- a/admin/src/main/java/org/yapp/user/dao/UserRepository.java +++ b/admin/src/main/java/org/yapp/user/dao/UserRepository.java @@ -9,4 +9,6 @@ public interface UserRepository extends JpaRepository { Optional findById(Long userId); + + Optional findByOauthId(String oauthId); } From 13edacc873d97f48390068dd306f3bbc3b3d01dc Mon Sep 17 00:00:00 2001 From: LujaeC Date: Thu, 23 Jan 2025 22:27:47 +0900 Subject: [PATCH 12/14] =?UTF-8?q?[PC-379]=20feat:=20admin=20=EB=AA=A8?= =?UTF-8?q?=EB=93=88=20jwt=20=ED=95=84=ED=84=B0=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- admin/src/main/java/org/yapp/config/SecurityConfig.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/admin/src/main/java/org/yapp/config/SecurityConfig.java b/admin/src/main/java/org/yapp/config/SecurityConfig.java index 2f50146..eaf19e0 100644 --- a/admin/src/main/java/org/yapp/config/SecurityConfig.java +++ b/admin/src/main/java/org/yapp/config/SecurityConfig.java @@ -11,16 +11,20 @@ import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.security.web.util.matcher.RequestMatchers; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; +import org.yapp.jwt.JwtFilter; @Configuration @EnableWebSecurity @RequiredArgsConstructor public class SecurityConfig { + private final JwtFilter jwtFilter; + @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { return http.csrf(AbstractHttpConfigurer::disable) @@ -33,6 +37,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .permitAll() .anyRequest() .hasAnyRole("ADMIN")) + .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class) .build(); } @@ -47,6 +52,6 @@ private CorsConfigurationSource corsConfigurationSource() { } private RequestMatcher getMatcherForAnyone() { - return RequestMatchers.anyOf(antMatcher("/admin/v1/login/**")); + return RequestMatchers.anyOf(antMatcher("/admin/v1/auth/login/**")); } } \ No newline at end of file From da942bcdb04d0372826056d9278ad384484034e8 Mon Sep 17 00:00:00 2001 From: LujaeC Date: Thu, 23 Jan 2025 22:28:19 +0900 Subject: [PATCH 13/14] =?UTF-8?q?[PC-379]=20feat:=20admin=20=EB=AA=A8?= =?UTF-8?q?=EB=93=88=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/yapp/auth/application/AdminRole.java | 6 +++ .../yapp/auth/application/AuthService.java | 40 +++++++++++++++++++ .../yapp/auth/application/dto/LoginDto.java | 5 +++ .../auth/presentation/AuthController.java | 28 +++++++++++++ .../presentation/request/LoginRequest.java | 11 +++++ admin/src/main/resources/application.yml | 2 +- .../yapp/error/code/auth/AuthErrorCode.java | 2 + 7 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 admin/src/main/java/org/yapp/auth/application/AdminRole.java create mode 100644 admin/src/main/java/org/yapp/auth/application/AuthService.java create mode 100644 admin/src/main/java/org/yapp/auth/application/dto/LoginDto.java create mode 100644 admin/src/main/java/org/yapp/auth/presentation/AuthController.java create mode 100644 admin/src/main/java/org/yapp/auth/presentation/request/LoginRequest.java diff --git a/admin/src/main/java/org/yapp/auth/application/AdminRole.java b/admin/src/main/java/org/yapp/auth/application/AdminRole.java new file mode 100644 index 0000000..bb44eac --- /dev/null +++ b/admin/src/main/java/org/yapp/auth/application/AdminRole.java @@ -0,0 +1,6 @@ +package org.yapp.auth.application; + +public enum AdminRole { + ADMIN, + ROLE_ADMIN; +} diff --git a/admin/src/main/java/org/yapp/auth/application/AuthService.java b/admin/src/main/java/org/yapp/auth/application/AuthService.java new file mode 100644 index 0000000..b361d9c --- /dev/null +++ b/admin/src/main/java/org/yapp/auth/application/AuthService.java @@ -0,0 +1,40 @@ +package org.yapp.auth.application; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.yapp.auth.AuthToken; +import org.yapp.auth.AuthTokenGenerator; +import org.yapp.auth.application.dto.LoginDto; +import org.yapp.domain.user.User; +import org.yapp.error.code.auth.AuthErrorCode; +import org.yapp.error.exception.ApplicationException; +import org.yapp.user.application.UserService; + +@Slf4j +@Service +@RequiredArgsConstructor +public class AuthService { + + private final UserService userService; + + private final AuthTokenGenerator authTokenGenerator; + + @Value("${admin.password}") + private String PWD; + + public AuthToken login(LoginDto loginDto) { + String oauthId = loginDto.oauthId(); + String password = loginDto.password(); + + User loginUser = userService.getUserByOauthId(oauthId); + + if (password.equals(PWD) && loginUser.getRole().equals(AdminRole.ADMIN.toString())) { + return authTokenGenerator.generate(loginUser.getId(), null, + AdminRole.ROLE_ADMIN.toString()); + } else { + throw new ApplicationException(AuthErrorCode.ACCESS_DENIED); + } + } +} diff --git a/admin/src/main/java/org/yapp/auth/application/dto/LoginDto.java b/admin/src/main/java/org/yapp/auth/application/dto/LoginDto.java new file mode 100644 index 0000000..232153a --- /dev/null +++ b/admin/src/main/java/org/yapp/auth/application/dto/LoginDto.java @@ -0,0 +1,5 @@ +package org.yapp.auth.application.dto; + +public record LoginDto(String oauthId, String password) { + +} diff --git a/admin/src/main/java/org/yapp/auth/presentation/AuthController.java b/admin/src/main/java/org/yapp/auth/presentation/AuthController.java new file mode 100644 index 0000000..84d20c6 --- /dev/null +++ b/admin/src/main/java/org/yapp/auth/presentation/AuthController.java @@ -0,0 +1,28 @@ +package org.yapp.auth.presentation; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.yapp.auth.AuthToken; +import org.yapp.auth.application.AuthService; +import org.yapp.auth.presentation.request.LoginRequest; +import org.yapp.util.CommonResponse; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/admin/v1/auth/") +public class AuthController { + + private final AuthService authService; + + @PostMapping("/login") + public ResponseEntity> login( + @RequestBody @Valid LoginRequest loginRequest) { + AuthToken authToken = authService.login(loginRequest.toDto()); + return ResponseEntity.ok(CommonResponse.createSuccess(authToken)); + } +} diff --git a/admin/src/main/java/org/yapp/auth/presentation/request/LoginRequest.java b/admin/src/main/java/org/yapp/auth/presentation/request/LoginRequest.java new file mode 100644 index 0000000..ad8e82f --- /dev/null +++ b/admin/src/main/java/org/yapp/auth/presentation/request/LoginRequest.java @@ -0,0 +1,11 @@ +package org.yapp.auth.presentation.request; + +import jakarta.validation.constraints.NotNull; +import org.yapp.auth.application.dto.LoginDto; + +public record LoginRequest(@NotNull String loginId, @NotNull String password) { + + public LoginDto toDto() { + return new LoginDto(loginId, password); + } +} diff --git a/admin/src/main/resources/application.yml b/admin/src/main/resources/application.yml index 620ba3b..fb06f99 100644 --- a/admin/src/main/resources/application.yml +++ b/admin/src/main/resources/application.yml @@ -1,3 +1,3 @@ spring: profiles: - include: db \ No newline at end of file + include: db, secret \ No newline at end of file diff --git a/common/exception/src/main/java/org/yapp/error/code/auth/AuthErrorCode.java b/common/exception/src/main/java/org/yapp/error/code/auth/AuthErrorCode.java index 0b01dfb..3e90524 100644 --- a/common/exception/src/main/java/org/yapp/error/code/auth/AuthErrorCode.java +++ b/common/exception/src/main/java/org/yapp/error/code/auth/AuthErrorCode.java @@ -9,8 +9,10 @@ @RequiredArgsConstructor public enum AuthErrorCode implements ErrorCode { OAUTH_ERROR(HttpStatus.FORBIDDEN, "Oauth Error"), + ACCESS_DENIED(HttpStatus.FORBIDDEN, "권한이 없습니다."), ; + private final HttpStatus httpStatus; private final String message; } From d5decee6c141b3e3ae49083dfa305a8a57dd6b0a Mon Sep 17 00:00:00 2001 From: LujaeC Date: Thu, 23 Jan 2025 22:31:53 +0900 Subject: [PATCH 14/14] =?UTF-8?q?[PC-379]=20chore:=20=EB=88=84=EB=9D=BD=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/auth/build.gradle | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 common/auth/build.gradle diff --git a/common/auth/build.gradle b/common/auth/build.gradle new file mode 100644 index 0000000..fda8bf5 --- /dev/null +++ b/common/auth/build.gradle @@ -0,0 +1,19 @@ +plugins { + id 'java' +} + +group = 'org.yapp' +version = '0.0.1-SNAPSHOT' + +repositories { + mavenCentral() +} + +dependencies { + testImplementation platform('org.junit:junit-bom:5.10.0') + testImplementation 'org.junit.jupiter:junit-jupiter' +} + +test { + useJUnitPlatform() +} \ No newline at end of file