diff --git a/src/main/java/in/koreatech/koin/admin/owner/controller/AdminOwnerApi.java b/src/main/java/in/koreatech/koin/admin/owner/controller/AdminOwnerApi.java new file mode 100644 index 000000000..9f2ecf4fd --- /dev/null +++ b/src/main/java/in/koreatech/koin/admin/owner/controller/AdminOwnerApi.java @@ -0,0 +1,112 @@ +package in.koreatech.koin.admin.owner.controller; + +import static in.koreatech.koin.domain.user.model.UserType.ADMIN; + +import org.springdoc.core.annotations.ParameterObject; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; + +import in.koreatech.koin.admin.owner.dto.AdminNewOwnersResponse; +import in.koreatech.koin.admin.owner.dto.AdminOwnerResponse; +import in.koreatech.koin.admin.owner.dto.AdminOwnerUpdateRequest; +import in.koreatech.koin.admin.owner.dto.AdminOwnerUpdateResponse; +import in.koreatech.koin.admin.owner.dto.AdminOwnersResponse; +import in.koreatech.koin.admin.owner.dto.OwnersCondition; +import in.koreatech.koin.global.auth.Auth; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; + +@Tag(name = "(Admin) Owner: 사장님", description = "관리자 권한으로 사장님 정보를 관리한다") +public interface AdminOwnerApi { + + @ApiResponses( + value = { + @ApiResponse(responseCode = "200"), + @ApiResponse(responseCode = "401", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "403", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "404", content = @Content(schema = @Schema(hidden = true))), + } + ) + @Operation(summary = "사장님 권한 요청 허용") + @SecurityRequirement(name = "Jwt Authentication") + @PutMapping("/admin/owner/{id}/authed") + ResponseEntity allowOwnerPermission( + @PathVariable Integer id, + @Auth(permit = {ADMIN}) Integer adminId + ); + + @ApiResponses( + value = { + @ApiResponse(responseCode = "200"), + @ApiResponse(responseCode = "401", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "403", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "404", content = @Content(schema = @Schema(hidden = true))), + } + ) + @Operation(summary = "특정 사장님 조회") + @SecurityRequirement(name = "Jwt Authentication") + @GetMapping("/admin/users/owner/{id}") + ResponseEntity getOwner( + @PathVariable Integer id, + @Auth(permit = {ADMIN}) Integer adminId + ); + + @ApiResponses( + value = { + @ApiResponse(responseCode = "200"), + @ApiResponse(responseCode = "401", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "403", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "404", content = @Content(schema = @Schema(hidden = true))), + } + ) + @Operation(summary = "특정 사장님 수정") + @SecurityRequirement(name = "Jwt Authentication") + @PutMapping("/admin/users/owner/{id}") + ResponseEntity updateOwner( + @PathVariable Integer id, + @RequestBody @Valid AdminOwnerUpdateRequest request, + @Auth(permit = {ADMIN}) Integer adminId + ); + + @ApiResponses( + value = { + @ApiResponse(responseCode = "200"), + @ApiResponse(responseCode = "401", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "403", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "404", content = @Content(schema = @Schema(hidden = true))) + } + ) + @Operation(summary = "가입 신청한 사장님 리스트 조회 (페이지네이션)") + @SecurityRequirement(name = "Jwt Authentication") + @GetMapping("/admin/users/new-owners") + ResponseEntity getNewOwners( + @ParameterObject @ModelAttribute OwnersCondition ownersCondition, + @Auth(permit = {ADMIN}) Integer adminId + ); + + @ApiResponses( + value = { + @ApiResponse(responseCode = "200"), + @ApiResponse(responseCode = "401", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "403", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "404", content = @Content(schema = @Schema(hidden = true))) + } + ) + @Operation(summary = "사장 리스트 조회 (페이지네이션)") + @SecurityRequirement(name = "Jwt Authentication") + @GetMapping("/admin/users/owners") + ResponseEntity getOwners( + @ParameterObject @ModelAttribute OwnersCondition ownersCondition, + @Auth(permit = {ADMIN}) Integer adminId + ); +} diff --git a/src/main/java/in/koreatech/koin/admin/owner/controller/AdminOwnerController.java b/src/main/java/in/koreatech/koin/admin/owner/controller/AdminOwnerController.java new file mode 100644 index 000000000..23cd9ab72 --- /dev/null +++ b/src/main/java/in/koreatech/koin/admin/owner/controller/AdminOwnerController.java @@ -0,0 +1,73 @@ +package in.koreatech.koin.admin.owner.controller; + +import static in.koreatech.koin.domain.user.model.UserType.ADMIN; + +import org.springdoc.core.annotations.ParameterObject; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import in.koreatech.koin.admin.owner.service.AdminOwnerService; +import in.koreatech.koin.admin.owner.dto.AdminNewOwnersResponse; +import in.koreatech.koin.admin.owner.dto.AdminOwnerResponse; +import in.koreatech.koin.admin.owner.dto.AdminOwnerUpdateRequest; +import in.koreatech.koin.admin.owner.dto.AdminOwnerUpdateResponse; +import in.koreatech.koin.admin.owner.dto.AdminOwnersResponse; +import in.koreatech.koin.admin.owner.dto.OwnersCondition; +import in.koreatech.koin.global.auth.Auth; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; + +@RestController +@RequiredArgsConstructor +public class AdminOwnerController implements AdminOwnerApi{ + + private final AdminOwnerService adminOwnerService; + + @PutMapping("/admin/owner/{id}/authed") + public ResponseEntity allowOwnerPermission( + @PathVariable Integer id, + @Auth(permit = {ADMIN}) Integer adminId) { + adminOwnerService.allowOwnerPermission(id); + return ResponseEntity.ok().build(); + } + + @GetMapping("/admin/users/owner/{id}") + public ResponseEntity getOwner( + @PathVariable Integer id, + @Auth(permit = {ADMIN}) Integer adminId + ) { + AdminOwnerResponse response = adminOwnerService.getOwner(id); + return ResponseEntity.ok().body(response); + } + + @PutMapping("/admin/users/owner/{id}") + public ResponseEntity updateOwner( + @PathVariable Integer id, + @RequestBody @Valid AdminOwnerUpdateRequest request, + @Auth(permit = {ADMIN}) Integer adminId + ) { + AdminOwnerUpdateResponse response = adminOwnerService.updateOwner(id, request); + return ResponseEntity.ok().body(response); + } + + @GetMapping("/admin/users/new-owners") + public ResponseEntity getNewOwners( + @ParameterObject @ModelAttribute OwnersCondition ownersCondition, + @Auth(permit = {ADMIN}) Integer adminId + ) { + return ResponseEntity.ok().body(adminOwnerService.getNewOwners(ownersCondition)); + } + + @GetMapping("/admin/users/owners") + public ResponseEntity getOwners( + @ParameterObject @ModelAttribute OwnersCondition ownersCondition, + @Auth(permit = {ADMIN}) Integer adminId + ) { + return ResponseEntity.ok().body(adminOwnerService.getOwners(ownersCondition)); + } +} diff --git a/src/main/java/in/koreatech/koin/admin/user/dto/AdminNewOwnersResponse.java b/src/main/java/in/koreatech/koin/admin/owner/dto/AdminNewOwnersResponse.java similarity index 98% rename from src/main/java/in/koreatech/koin/admin/user/dto/AdminNewOwnersResponse.java rename to src/main/java/in/koreatech/koin/admin/owner/dto/AdminNewOwnersResponse.java index 818d069d4..a11ba6fe2 100644 --- a/src/main/java/in/koreatech/koin/admin/user/dto/AdminNewOwnersResponse.java +++ b/src/main/java/in/koreatech/koin/admin/owner/dto/AdminNewOwnersResponse.java @@ -1,4 +1,4 @@ -package in.koreatech.koin.admin.user.dto; +package in.koreatech.koin.admin.owner.dto; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.NOT_REQUIRED; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; diff --git a/src/main/java/in/koreatech/koin/admin/user/dto/AdminOwnerResponse.java b/src/main/java/in/koreatech/koin/admin/owner/dto/AdminOwnerResponse.java similarity index 94% rename from src/main/java/in/koreatech/koin/admin/user/dto/AdminOwnerResponse.java rename to src/main/java/in/koreatech/koin/admin/owner/dto/AdminOwnerResponse.java index 75c3003a1..6b1e75f7b 100644 --- a/src/main/java/in/koreatech/koin/admin/user/dto/AdminOwnerResponse.java +++ b/src/main/java/in/koreatech/koin/admin/owner/dto/AdminOwnerResponse.java @@ -1,4 +1,4 @@ -package in.koreatech.koin.admin.user.dto; +package in.koreatech.koin.admin.owner.dto; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.NOT_REQUIRED; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; @@ -7,16 +7,12 @@ import java.util.List; import java.util.stream.Collectors; -import org.springframework.data.domain.Page; - import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy; import com.fasterxml.jackson.databind.annotation.JsonNaming; import in.koreatech.koin.domain.owner.model.Owner; import in.koreatech.koin.domain.owner.model.OwnerAttachment; -import in.koreatech.koin.domain.owner.model.OwnerIncludingShop; -import in.koreatech.koin.global.model.Criteria; import io.swagger.v3.oas.annotations.media.Schema; @JsonNaming(value = SnakeCaseStrategy.class) diff --git a/src/main/java/in/koreatech/koin/admin/user/dto/AdminOwnerUpdateRequest.java b/src/main/java/in/koreatech/koin/admin/owner/dto/AdminOwnerUpdateRequest.java similarity index 97% rename from src/main/java/in/koreatech/koin/admin/user/dto/AdminOwnerUpdateRequest.java rename to src/main/java/in/koreatech/koin/admin/owner/dto/AdminOwnerUpdateRequest.java index 237ddb8d7..277f4c68f 100644 --- a/src/main/java/in/koreatech/koin/admin/user/dto/AdminOwnerUpdateRequest.java +++ b/src/main/java/in/koreatech/koin/admin/owner/dto/AdminOwnerUpdateRequest.java @@ -1,4 +1,4 @@ -package in.koreatech.koin.admin.user.dto; +package in.koreatech.koin.admin.owner.dto; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.NOT_REQUIRED; diff --git a/src/main/java/in/koreatech/koin/admin/user/dto/AdminOwnerUpdateResponse.java b/src/main/java/in/koreatech/koin/admin/owner/dto/AdminOwnerUpdateResponse.java similarity index 97% rename from src/main/java/in/koreatech/koin/admin/user/dto/AdminOwnerUpdateResponse.java rename to src/main/java/in/koreatech/koin/admin/owner/dto/AdminOwnerUpdateResponse.java index f288e6fa3..434ba9634 100644 --- a/src/main/java/in/koreatech/koin/admin/user/dto/AdminOwnerUpdateResponse.java +++ b/src/main/java/in/koreatech/koin/admin/owner/dto/AdminOwnerUpdateResponse.java @@ -1,4 +1,4 @@ -package in.koreatech.koin.admin.user.dto; +package in.koreatech.koin.admin.owner.dto; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.NOT_REQUIRED; diff --git a/src/main/java/in/koreatech/koin/admin/user/dto/AdminOwnersResponse.java b/src/main/java/in/koreatech/koin/admin/owner/dto/AdminOwnersResponse.java similarity index 98% rename from src/main/java/in/koreatech/koin/admin/user/dto/AdminOwnersResponse.java rename to src/main/java/in/koreatech/koin/admin/owner/dto/AdminOwnersResponse.java index e74ef1ba3..9dc3fd0b8 100644 --- a/src/main/java/in/koreatech/koin/admin/user/dto/AdminOwnersResponse.java +++ b/src/main/java/in/koreatech/koin/admin/owner/dto/AdminOwnersResponse.java @@ -1,4 +1,4 @@ -package in.koreatech.koin.admin.user.dto; +package in.koreatech.koin.admin.owner.dto; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.NOT_REQUIRED; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; diff --git a/src/main/java/in/koreatech/koin/admin/user/dto/OwnersCondition.java b/src/main/java/in/koreatech/koin/admin/owner/dto/OwnersCondition.java similarity index 98% rename from src/main/java/in/koreatech/koin/admin/user/dto/OwnersCondition.java rename to src/main/java/in/koreatech/koin/admin/owner/dto/OwnersCondition.java index 12a3ebace..f42482a4a 100644 --- a/src/main/java/in/koreatech/koin/admin/user/dto/OwnersCondition.java +++ b/src/main/java/in/koreatech/koin/admin/owner/dto/OwnersCondition.java @@ -1,4 +1,4 @@ -package in.koreatech.koin.admin.user.dto; +package in.koreatech.koin.admin.owner.dto; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.NOT_REQUIRED; diff --git a/src/main/java/in/koreatech/koin/admin/user/repository/AdminOwnerRepository.java b/src/main/java/in/koreatech/koin/admin/owner/repository/AdminOwnerRepository.java similarity index 97% rename from src/main/java/in/koreatech/koin/admin/user/repository/AdminOwnerRepository.java rename to src/main/java/in/koreatech/koin/admin/owner/repository/AdminOwnerRepository.java index 7b4ae2209..50b418556 100644 --- a/src/main/java/in/koreatech/koin/admin/user/repository/AdminOwnerRepository.java +++ b/src/main/java/in/koreatech/koin/admin/owner/repository/AdminOwnerRepository.java @@ -1,4 +1,4 @@ -package in.koreatech.koin.admin.user.repository; +package in.koreatech.koin.admin.owner.repository; import java.util.Optional; diff --git a/src/main/java/in/koreatech/koin/admin/user/repository/AdminOwnerShopRedisRepository.java b/src/main/java/in/koreatech/koin/admin/owner/repository/AdminOwnerShopRedisRepository.java similarity index 86% rename from src/main/java/in/koreatech/koin/admin/user/repository/AdminOwnerShopRedisRepository.java rename to src/main/java/in/koreatech/koin/admin/owner/repository/AdminOwnerShopRedisRepository.java index 7b95e3371..765b309ec 100644 --- a/src/main/java/in/koreatech/koin/admin/user/repository/AdminOwnerShopRedisRepository.java +++ b/src/main/java/in/koreatech/koin/admin/owner/repository/AdminOwnerShopRedisRepository.java @@ -1,4 +1,4 @@ -package in.koreatech.koin.admin.user.repository; +package in.koreatech.koin.admin.owner.repository; import java.util.Optional; diff --git a/src/main/java/in/koreatech/koin/admin/owner/service/AdminOwnerService.java b/src/main/java/in/koreatech/koin/admin/owner/service/AdminOwnerService.java new file mode 100644 index 000000000..c22ca75ca --- /dev/null +++ b/src/main/java/in/koreatech/koin/admin/owner/service/AdminOwnerService.java @@ -0,0 +1,139 @@ +package in.koreatech.koin.admin.owner.service; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import in.koreatech.koin.admin.owner.repository.AdminOwnerRepository; +import in.koreatech.koin.admin.owner.repository.AdminOwnerShopRedisRepository; +import in.koreatech.koin.admin.shop.repository.shop.AdminShopRepository; +import in.koreatech.koin.admin.owner.dto.AdminNewOwnersResponse; +import in.koreatech.koin.admin.owner.dto.AdminOwnerResponse; +import in.koreatech.koin.admin.owner.dto.AdminOwnerUpdateRequest; +import in.koreatech.koin.admin.owner.dto.AdminOwnerUpdateResponse; +import in.koreatech.koin.admin.owner.dto.AdminOwnersResponse; +import in.koreatech.koin.admin.owner.dto.OwnersCondition; +import in.koreatech.koin.admin.user.repository.AdminUserRepository; +import in.koreatech.koin.domain.owner.model.Owner; +import in.koreatech.koin.domain.owner.model.OwnerIncludingShop; +import in.koreatech.koin.domain.shop.model.shop.Shop; +import in.koreatech.koin.domain.user.model.UserType; +import in.koreatech.koin.global.model.Criteria; +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class AdminOwnerService { + + private final AdminOwnerRepository adminOwnerRepository; + private final AdminOwnerShopRedisRepository adminOwnerShopRedisRepository; + private final AdminShopRepository adminShopRepository; + private final AdminUserRepository adminUserRepository; + + @Transactional + public void allowOwnerPermission(Integer id) { + Owner owner = adminOwnerRepository.getById(id); + owner.getUser().auth(); + adminOwnerShopRedisRepository.findById(id).ifPresent(ownerShop -> { + Integer shopId = ownerShop.getShopId(); + if (shopId != null) { + Shop shop = adminShopRepository.getById(shopId); + shop.updateOwner(owner); + owner.setGrantShop(true); + } + adminOwnerShopRedisRepository.deleteById(id); + }); + } + + public AdminOwnerResponse getOwner(Integer ownerId) { + Owner owner = adminOwnerRepository.getById(ownerId); + + List shopsId = adminShopRepository.findAllByOwnerId(ownerId) + .stream() + .map(Shop::getId) + .toList(); + + return AdminOwnerResponse.of(owner, shopsId); + } + + @Transactional + public AdminOwnerUpdateResponse updateOwner(Integer ownerId, AdminOwnerUpdateRequest request) { + Owner owner = adminOwnerRepository.getById(ownerId); + owner.update(request); + return AdminOwnerUpdateResponse.from(owner); + } + + public AdminNewOwnersResponse getNewOwners(OwnersCondition ownersCondition) { + ownersCondition.checkDataConstraintViolation(); + + Integer totalOwners = adminUserRepository.findUsersCountByUserTypeAndIsAuthed(UserType.OWNER, false); + Criteria criteria = Criteria.of(ownersCondition.page(), ownersCondition.limit(), totalOwners); + Sort.Direction direction = ownersCondition.getDirection(); + + Page result = getNewOwnersResultPage(ownersCondition, criteria, direction); + + List ownerIncludingShops = new ArrayList<>(); + for (Owner owner : result.getContent()) { + Shop shop = adminOwnerShopRedisRepository.findById(owner.getId()) + .map(ownerShop -> adminShopRepository.findById(ownerShop.getShopId()).orElse(null)) + .orElse(null); + + OwnerIncludingShop ownerIncludingShop = OwnerIncludingShop.of(owner, shop); + ownerIncludingShops.add(ownerIncludingShop); + } + + return AdminNewOwnersResponse.of(ownerIncludingShops, result, criteria); + } + + public AdminOwnersResponse getOwners(OwnersCondition ownersCondition) { + ownersCondition.checkDataConstraintViolation(); + + Integer totalOwners = adminUserRepository.findUsersCountByUserTypeAndIsAuthed(UserType.OWNER, true); + Criteria criteria = Criteria.of(ownersCondition.page(), ownersCondition.limit(), totalOwners); + Sort.Direction direction = ownersCondition.getDirection(); + + Page result = getOwnersResultPage(ownersCondition, criteria, direction); + + return AdminOwnersResponse.of(result, criteria); + } + + private Page getNewOwnersResultPage(OwnersCondition ownersCondition, Criteria criteria, + Sort.Direction direction) { + PageRequest pageRequest = PageRequest.of( + criteria.getPage(), + criteria.getLimit(), + Sort.by(direction, "user.createdAt") + ); + + if (ownersCondition.searchType() == OwnersCondition.SearchType.EMAIL) { + return adminOwnerRepository.findPageUnauthenticatedOwnersByEmail(ownersCondition.query(), pageRequest); + } + if (ownersCondition.searchType() == OwnersCondition.SearchType.NAME) { + return adminOwnerRepository.findPageUnauthenticatedOwnersByName(ownersCondition.query(), pageRequest); + } + return adminOwnerRepository.findPageUnauthenticatedOwners(pageRequest); + } + + private Page getOwnersResultPage(OwnersCondition ownersCondition, Criteria criteria, + Sort.Direction direction) { + PageRequest pageRequest = PageRequest.of( + criteria.getPage(), + criteria.getLimit(), + Sort.by(direction, "user.createdAt") + ); + + if (ownersCondition.searchType() == OwnersCondition.SearchType.EMAIL) { + return adminOwnerRepository.findPageOwnersByEmail(ownersCondition.query(), pageRequest); + } + if (ownersCondition.searchType() == OwnersCondition.SearchType.NAME) { + return adminOwnerRepository.findPageOwnersByName(ownersCondition.query(), pageRequest); + } + return adminOwnerRepository.findPageOwners(pageRequest); + } +} diff --git a/src/main/java/in/koreatech/koin/admin/student/controller/AdminStudentApi.java b/src/main/java/in/koreatech/koin/admin/student/controller/AdminStudentApi.java new file mode 100644 index 000000000..47104b683 --- /dev/null +++ b/src/main/java/in/koreatech/koin/admin/student/controller/AdminStudentApi.java @@ -0,0 +1,81 @@ +package in.koreatech.koin.admin.student.controller; + +import static in.koreatech.koin.domain.user.model.UserType.ADMIN; + +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.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +import in.koreatech.koin.admin.student.dto.AdminStudentResponse; +import in.koreatech.koin.admin.student.dto.AdminStudentUpdateRequest; +import in.koreatech.koin.admin.student.dto.AdminStudentUpdateResponse; +import in.koreatech.koin.admin.student.dto.AdminStudentsResponse; +import in.koreatech.koin.global.auth.Auth; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; + +@Tag(name = "(Admin) Student: 학생", description = "관리자 권한으로 학생 정보를 관리한다") +public interface AdminStudentApi { + + @ApiResponses( + value = { + @ApiResponse(responseCode = "200"), + @ApiResponse(responseCode = "401", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "403", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "404", content = @Content(schema = @Schema(hidden = true))), + } + ) + @Operation(summary = "학생 리스트 조회(페이지네이션)") + @SecurityRequirement(name = "Jwt Authentication") + @GetMapping("/admin/students") + ResponseEntity getStudents( + @RequestParam(required = false) Integer page, + @RequestParam(required = false) Integer limit, + @RequestParam(required = false) Boolean isAuthed, + @RequestParam(required = false) String nickname, + @RequestParam(required = false) String email, + @Auth(permit = {ADMIN}) Integer adminId + ); + + @ApiResponses( + value = { + @ApiResponse(responseCode = "200"), + @ApiResponse(responseCode = "401", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "403", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "404", content = @Content(schema = @Schema(hidden = true))), + } + ) + @Operation(summary = "학생 정보 조회") + @SecurityRequirement(name = "Jwt Authentication") + @GetMapping("/admin/users/student/{id}") + ResponseEntity getStudent( + @PathVariable Integer id, + @Auth(permit = {ADMIN}) Integer adminId + ); + + @ApiResponses( + value = { + @ApiResponse(responseCode = "200"), + @ApiResponse(responseCode = "401", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "403", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "404", content = @Content(schema = @Schema(hidden = true))), + } + ) + @Operation(summary = "학생 정보 수정") + @SecurityRequirement(name = "Jwt Authentication") + @PutMapping("/admin/users/student/{id}") + ResponseEntity updateStudent( + @Valid @RequestBody AdminStudentUpdateRequest adminRequest, + @PathVariable Integer id, + @Auth(permit = {ADMIN}) Integer adminId + ); +} diff --git a/src/main/java/in/koreatech/koin/admin/student/controller/AdminStudentController.java b/src/main/java/in/koreatech/koin/admin/student/controller/AdminStudentController.java new file mode 100644 index 000000000..3083ceb2e --- /dev/null +++ b/src/main/java/in/koreatech/koin/admin/student/controller/AdminStudentController.java @@ -0,0 +1,61 @@ +package in.koreatech.koin.admin.student.controller; + +import static in.koreatech.koin.domain.user.model.UserType.ADMIN; + +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.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import in.koreatech.koin.admin.student.service.AdminStudentService; +import in.koreatech.koin.admin.student.dto.AdminStudentResponse; +import in.koreatech.koin.admin.student.dto.AdminStudentUpdateRequest; +import in.koreatech.koin.admin.student.dto.AdminStudentUpdateResponse; +import in.koreatech.koin.admin.student.dto.AdminStudentsResponse; +import in.koreatech.koin.admin.student.dto.StudentsCondition; +import in.koreatech.koin.global.auth.Auth; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; + +@RestController +@RequiredArgsConstructor +public class AdminStudentController implements AdminStudentApi { + + private final AdminStudentService adminStudentService; + + @GetMapping("/admin/users/student/{id}") + public ResponseEntity getStudent( + @PathVariable Integer id, + @Auth(permit = {ADMIN}) Integer adminId + ) { + AdminStudentResponse response = adminStudentService.getStudent(id); + return ResponseEntity.ok().body(response); + } + + @GetMapping("/admin/students") + public ResponseEntity getStudents( + @RequestParam(required = false) Integer page, + @RequestParam(required = false) Integer limit, + @RequestParam(required = false) Boolean isAuthed, + @RequestParam(required = false) String nickname, + @RequestParam(required = false) String email, + @Auth(permit = {ADMIN}) Integer adminId + ) { + StudentsCondition studentsCondition = new StudentsCondition(page, limit, isAuthed, nickname, email); + AdminStudentsResponse response = adminStudentService.getStudents(studentsCondition); + return ResponseEntity.ok().body(response); + } + + @PutMapping("/admin/users/student/{id}") + public ResponseEntity updateStudent( + @Valid @RequestBody AdminStudentUpdateRequest adminRequest, + @PathVariable Integer id, + @Auth(permit = {ADMIN}) Integer adminId + ) { + AdminStudentUpdateResponse response = adminStudentService.updateStudent(id, adminRequest); + return ResponseEntity.ok().body(response); + } +} diff --git a/src/main/java/in/koreatech/koin/admin/user/dto/AdminStudentResponse.java b/src/main/java/in/koreatech/koin/admin/student/dto/AdminStudentResponse.java similarity index 98% rename from src/main/java/in/koreatech/koin/admin/user/dto/AdminStudentResponse.java rename to src/main/java/in/koreatech/koin/admin/student/dto/AdminStudentResponse.java index 9f34a9ba5..26acdebeb 100644 --- a/src/main/java/in/koreatech/koin/admin/user/dto/AdminStudentResponse.java +++ b/src/main/java/in/koreatech/koin/admin/student/dto/AdminStudentResponse.java @@ -1,4 +1,4 @@ -package in.koreatech.koin.admin.user.dto; +package in.koreatech.koin.admin.student.dto; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.NOT_REQUIRED; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; diff --git a/src/main/java/in/koreatech/koin/admin/user/dto/AdminStudentUpdateRequest.java b/src/main/java/in/koreatech/koin/admin/student/dto/AdminStudentUpdateRequest.java similarity index 98% rename from src/main/java/in/koreatech/koin/admin/user/dto/AdminStudentUpdateRequest.java rename to src/main/java/in/koreatech/koin/admin/student/dto/AdminStudentUpdateRequest.java index e21faf23f..f14bcd408 100644 --- a/src/main/java/in/koreatech/koin/admin/user/dto/AdminStudentUpdateRequest.java +++ b/src/main/java/in/koreatech/koin/admin/student/dto/AdminStudentUpdateRequest.java @@ -1,4 +1,4 @@ -package in.koreatech.koin.admin.user.dto; +package in.koreatech.koin.admin.student.dto; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.NOT_REQUIRED; diff --git a/src/main/java/in/koreatech/koin/admin/user/dto/AdminStudentUpdateResponse.java b/src/main/java/in/koreatech/koin/admin/student/dto/AdminStudentUpdateResponse.java similarity index 98% rename from src/main/java/in/koreatech/koin/admin/user/dto/AdminStudentUpdateResponse.java rename to src/main/java/in/koreatech/koin/admin/student/dto/AdminStudentUpdateResponse.java index d4324ebe7..df9dfdca1 100644 --- a/src/main/java/in/koreatech/koin/admin/user/dto/AdminStudentUpdateResponse.java +++ b/src/main/java/in/koreatech/koin/admin/student/dto/AdminStudentUpdateResponse.java @@ -1,4 +1,4 @@ -package in.koreatech.koin.admin.user.dto; +package in.koreatech.koin.admin.student.dto; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.NOT_REQUIRED; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; diff --git a/src/main/java/in/koreatech/koin/admin/user/dto/AdminStudentsResponse.java b/src/main/java/in/koreatech/koin/admin/student/dto/AdminStudentsResponse.java similarity index 97% rename from src/main/java/in/koreatech/koin/admin/user/dto/AdminStudentsResponse.java rename to src/main/java/in/koreatech/koin/admin/student/dto/AdminStudentsResponse.java index f9bc41eb9..01c85c2fc 100644 --- a/src/main/java/in/koreatech/koin/admin/user/dto/AdminStudentsResponse.java +++ b/src/main/java/in/koreatech/koin/admin/student/dto/AdminStudentsResponse.java @@ -1,4 +1,4 @@ -package in.koreatech.koin.admin.user.dto; +package in.koreatech.koin.admin.student.dto; import java.util.List; import java.util.stream.Collectors; diff --git a/src/main/java/in/koreatech/koin/admin/user/dto/StudentsCondition.java b/src/main/java/in/koreatech/koin/admin/student/dto/StudentsCondition.java similarity index 96% rename from src/main/java/in/koreatech/koin/admin/user/dto/StudentsCondition.java rename to src/main/java/in/koreatech/koin/admin/student/dto/StudentsCondition.java index 9da9fd9e4..643135fb4 100644 --- a/src/main/java/in/koreatech/koin/admin/user/dto/StudentsCondition.java +++ b/src/main/java/in/koreatech/koin/admin/student/dto/StudentsCondition.java @@ -1,4 +1,4 @@ -package in.koreatech.koin.admin.user.dto; +package in.koreatech.koin.admin.student.dto; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.NOT_REQUIRED; diff --git a/src/main/java/in/koreatech/koin/admin/user/repository/AdminStudentRepository.java b/src/main/java/in/koreatech/koin/admin/student/repository/AdminStudentRepository.java similarity index 92% rename from src/main/java/in/koreatech/koin/admin/user/repository/AdminStudentRepository.java rename to src/main/java/in/koreatech/koin/admin/student/repository/AdminStudentRepository.java index db32b0ddf..689411cde 100644 --- a/src/main/java/in/koreatech/koin/admin/user/repository/AdminStudentRepository.java +++ b/src/main/java/in/koreatech/koin/admin/student/repository/AdminStudentRepository.java @@ -1,4 +1,4 @@ -package in.koreatech.koin.admin.user.repository; +package in.koreatech.koin.admin.student.repository; import java.util.Optional; @@ -8,7 +8,7 @@ import org.springframework.data.repository.Repository; import org.springframework.data.repository.query.Param; -import in.koreatech.koin.admin.user.dto.StudentsCondition; +import in.koreatech.koin.admin.student.dto.StudentsCondition; import in.koreatech.koin.domain.user.exception.UserNotFoundException; import in.koreatech.koin.domain.student.model.Student; diff --git a/src/main/java/in/koreatech/koin/admin/student/service/AdminStudentService.java b/src/main/java/in/koreatech/koin/admin/student/service/AdminStudentService.java new file mode 100644 index 000000000..9234be3c2 --- /dev/null +++ b/src/main/java/in/koreatech/koin/admin/student/service/AdminStudentService.java @@ -0,0 +1,76 @@ +package in.koreatech.koin.admin.student.service; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import in.koreatech.koin.admin.student.repository.AdminStudentRepository; +import in.koreatech.koin.admin.student.dto.AdminStudentResponse; +import in.koreatech.koin.admin.student.dto.AdminStudentUpdateRequest; +import in.koreatech.koin.admin.student.dto.AdminStudentUpdateResponse; +import in.koreatech.koin.admin.student.dto.AdminStudentsResponse; +import in.koreatech.koin.admin.student.dto.StudentsCondition; +import in.koreatech.koin.admin.user.repository.AdminUserRepository; +import in.koreatech.koin.domain.student.exception.StudentDepartmentNotValidException; +import in.koreatech.koin.domain.student.model.Student; +import in.koreatech.koin.domain.student.model.StudentDepartment; +import in.koreatech.koin.domain.user.exception.DuplicationNicknameException; +import in.koreatech.koin.domain.user.model.User; +import in.koreatech.koin.domain.user.model.UserGender; +import in.koreatech.koin.global.model.Criteria; +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class AdminStudentService { + + private final AdminStudentRepository adminStudentRepository; + private final AdminUserRepository adminUserRepository; + private final PasswordEncoder passwordEncoder; + + public AdminStudentResponse getStudent(Integer userId) { + Student student = adminStudentRepository.getById(userId); + return AdminStudentResponse.from(student); + } + + public AdminStudentsResponse getStudents(StudentsCondition studentsCondition) { + Integer totalStudents = adminStudentRepository.findAllStudentCount(); + Criteria criteria = Criteria.of(studentsCondition.page(), studentsCondition.limit(), totalStudents); + + PageRequest pageRequest = PageRequest.of(criteria.getPage(), criteria.getLimit()); + Page studentsPage = adminStudentRepository.findByConditions(studentsCondition, pageRequest); + + return AdminStudentsResponse.from(studentsPage); + } + + @Transactional + public AdminStudentUpdateResponse updateStudent(Integer id, AdminStudentUpdateRequest adminRequest) { + Student student = adminStudentRepository.getById(id); + User user = student.getUser(); + validateNicknameDuplication(adminRequest.nickname(), id); + validateDepartmentValid(adminRequest.major()); + user.update(adminRequest.nickname(), adminRequest.name(), + adminRequest.phoneNumber(), UserGender.from(adminRequest.gender())); + user.updateStudentPassword(passwordEncoder, adminRequest.password()); + student.update(adminRequest.studentNumber(), adminRequest.major()); + adminStudentRepository.save(student); + + return AdminStudentUpdateResponse.from(student); + } + + private void validateNicknameDuplication(String nickname, Integer userId) { + if (nickname != null && + adminUserRepository.existsByNicknameAndIdNot(nickname, userId)) { + throw DuplicationNicknameException.withDetail("nickname : " + nickname); + } + } + + private void validateDepartmentValid(String department) { + if (department != null && !StudentDepartment.isValid(department)) { + throw StudentDepartmentNotValidException.withDetail("학부(학과) : " + department); + } + } +} diff --git a/src/main/java/in/koreatech/koin/admin/user/controller/AdminUserApi.java b/src/main/java/in/koreatech/koin/admin/user/controller/AdminUserApi.java index 26a867e36..182763adb 100644 --- a/src/main/java/in/koreatech/koin/admin/user/controller/AdminUserApi.java +++ b/src/main/java/in/koreatech/koin/admin/user/controller/AdminUserApi.java @@ -2,11 +2,9 @@ import static in.koreatech.koin.domain.user.model.UserType.ADMIN; -import org.springdoc.core.annotations.ParameterObject; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; @@ -15,24 +13,14 @@ import in.koreatech.koin.admin.user.dto.AdminLoginRequest; import in.koreatech.koin.admin.user.dto.AdminLoginResponse; -import in.koreatech.koin.admin.user.dto.AdminNewOwnersResponse; -import in.koreatech.koin.admin.user.dto.AdminOwnerResponse; -import in.koreatech.koin.admin.user.dto.AdminOwnerUpdateRequest; -import in.koreatech.koin.admin.user.dto.AdminOwnerUpdateResponse; -import in.koreatech.koin.admin.user.dto.AdminOwnersResponse; import in.koreatech.koin.admin.user.dto.AdminPasswordChangeRequest; import in.koreatech.koin.admin.user.dto.AdminPermissionUpdateRequest; import in.koreatech.koin.admin.user.dto.AdminResponse; -import in.koreatech.koin.admin.user.dto.AdminStudentResponse; -import in.koreatech.koin.admin.user.dto.AdminStudentUpdateRequest; -import in.koreatech.koin.admin.user.dto.AdminStudentUpdateResponse; -import in.koreatech.koin.admin.user.dto.AdminStudentsResponse; import in.koreatech.koin.admin.user.dto.AdminTokenRefreshRequest; import in.koreatech.koin.admin.user.dto.AdminTokenRefreshResponse; import in.koreatech.koin.admin.user.dto.AdminUpdateRequest; import in.koreatech.koin.admin.user.dto.AdminsResponse; import in.koreatech.koin.admin.user.dto.CreateAdminRequest; -import in.koreatech.koin.admin.user.dto.OwnersCondition; import in.koreatech.koin.admin.user.enums.TeamType; import in.koreatech.koin.admin.user.enums.TrackType; import in.koreatech.koin.domain.user.model.User; @@ -48,25 +36,6 @@ @Tag(name = "(Admin) User: 회원", description = "관리자 권한으로 회원 정보를 관리한다") public interface AdminUserApi { - @ApiResponses( - value = { - @ApiResponse(responseCode = "200"), - @ApiResponse(responseCode = "401", content = @Content(schema = @Schema(hidden = true))), - @ApiResponse(responseCode = "403", content = @Content(schema = @Schema(hidden = true))), - @ApiResponse(responseCode = "404", content = @Content(schema = @Schema(hidden = true))), - } - ) - @Operation(summary = "학생 리스트 조회(페이지네이션)") - @SecurityRequirement(name = "Jwt Authentication") - @GetMapping("/admin/students") - ResponseEntity getStudents( - @RequestParam(required = false) Integer page, - @RequestParam(required = false) Integer limit, - @RequestParam(required = false) Boolean isAuthed, - @RequestParam(required = false) String nickname, - @RequestParam(required = false) String email, - @Auth(permit = {ADMIN}) Integer adminId - ); @ApiResponses( value = { @@ -241,120 +210,6 @@ ResponseEntity updateAdminPermission( @Auth(permit = {ADMIN}) Integer adminId ); - @ApiResponses( - value = { - @ApiResponse(responseCode = "200"), - @ApiResponse(responseCode = "401", content = @Content(schema = @Schema(hidden = true))), - @ApiResponse(responseCode = "403", content = @Content(schema = @Schema(hidden = true))), - @ApiResponse(responseCode = "404", content = @Content(schema = @Schema(hidden = true))), - } - ) - @Operation(summary = "사장님 권한 요청 허용") - @SecurityRequirement(name = "Jwt Authentication") - @PutMapping("/admin/owner/{id}/authed") - ResponseEntity allowOwnerPermission( - @PathVariable Integer id, - @Auth(permit = {ADMIN}) Integer adminId - ); - - @ApiResponses( - value = { - @ApiResponse(responseCode = "200"), - @ApiResponse(responseCode = "401", content = @Content(schema = @Schema(hidden = true))), - @ApiResponse(responseCode = "403", content = @Content(schema = @Schema(hidden = true))), - @ApiResponse(responseCode = "404", content = @Content(schema = @Schema(hidden = true))), - } - ) - @Operation(summary = "회원 정보 조회") - @SecurityRequirement(name = "Jwt Authentication") - @GetMapping("/admin/users/student/{id}") - ResponseEntity getStudent( - @PathVariable Integer id, - @Auth(permit = {ADMIN}) Integer adminId - ); - - @ApiResponses( - value = { - @ApiResponse(responseCode = "200"), - @ApiResponse(responseCode = "401", content = @Content(schema = @Schema(hidden = true))), - @ApiResponse(responseCode = "403", content = @Content(schema = @Schema(hidden = true))), - @ApiResponse(responseCode = "404", content = @Content(schema = @Schema(hidden = true))), - } - ) - @Operation(summary = "회원 정보 수정") - @SecurityRequirement(name = "Jwt Authentication") - @PutMapping("/admin/users/student/{id}") - ResponseEntity updateStudent( - @Valid @RequestBody AdminStudentUpdateRequest adminRequest, - @PathVariable Integer id, - @Auth(permit = {ADMIN}) Integer adminId - ); - - @ApiResponses( - value = { - @ApiResponse(responseCode = "200"), - @ApiResponse(responseCode = "401", content = @Content(schema = @Schema(hidden = true))), - @ApiResponse(responseCode = "403", content = @Content(schema = @Schema(hidden = true))), - @ApiResponse(responseCode = "404", content = @Content(schema = @Schema(hidden = true))), - } - ) - @Operation(summary = "특정 사장님 조회") - @SecurityRequirement(name = "Jwt Authentication") - @GetMapping("/admin/users/owner/{id}") - ResponseEntity getOwner( - @PathVariable Integer id, - @Auth(permit = {ADMIN}) Integer adminId - ); - - @ApiResponses( - value = { - @ApiResponse(responseCode = "200"), - @ApiResponse(responseCode = "401", content = @Content(schema = @Schema(hidden = true))), - @ApiResponse(responseCode = "403", content = @Content(schema = @Schema(hidden = true))), - @ApiResponse(responseCode = "404", content = @Content(schema = @Schema(hidden = true))), - } - ) - @Operation(summary = "특정 사장님 수정") - @SecurityRequirement(name = "Jwt Authentication") - @PutMapping("/admin/users/owner/{id}") - ResponseEntity updateOwner( - @PathVariable Integer id, - @RequestBody @Valid AdminOwnerUpdateRequest request, - @Auth(permit = {ADMIN}) Integer adminId - ); - - @ApiResponses( - value = { - @ApiResponse(responseCode = "200"), - @ApiResponse(responseCode = "401", content = @Content(schema = @Schema(hidden = true))), - @ApiResponse(responseCode = "403", content = @Content(schema = @Schema(hidden = true))), - @ApiResponse(responseCode = "404", content = @Content(schema = @Schema(hidden = true))) - } - ) - @Operation(summary = "가입 신청한 사장님 리스트 조회 (페이지네이션)") - @SecurityRequirement(name = "Jwt Authentication") - @GetMapping("/admin/users/new-owners") - ResponseEntity getNewOwners( - @ParameterObject @ModelAttribute OwnersCondition ownersCondition, - @Auth(permit = {ADMIN}) Integer adminId - ); - - @ApiResponses( - value = { - @ApiResponse(responseCode = "200"), - @ApiResponse(responseCode = "401", content = @Content(schema = @Schema(hidden = true))), - @ApiResponse(responseCode = "403", content = @Content(schema = @Schema(hidden = true))), - @ApiResponse(responseCode = "404", content = @Content(schema = @Schema(hidden = true))) - } - ) - @Operation(summary = "사장 리스트 조회 (페이지네이션)") - @SecurityRequirement(name = "Jwt Authentication") - @GetMapping("/admin/users/owners") - ResponseEntity getOwners( - @ParameterObject @ModelAttribute OwnersCondition ownersCondition, - @Auth(permit = {ADMIN}) Integer adminId - ); - @ApiResponses( value = { @ApiResponse(responseCode = "200"), diff --git a/src/main/java/in/koreatech/koin/admin/user/controller/AdminUserController.java b/src/main/java/in/koreatech/koin/admin/user/controller/AdminUserController.java index db36de701..7c510d75c 100644 --- a/src/main/java/in/koreatech/koin/admin/user/controller/AdminUserController.java +++ b/src/main/java/in/koreatech/koin/admin/user/controller/AdminUserController.java @@ -4,11 +4,9 @@ import java.net.URI; -import org.springdoc.core.annotations.ParameterObject; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; @@ -18,26 +16,15 @@ import in.koreatech.koin.admin.user.dto.AdminLoginRequest; import in.koreatech.koin.admin.user.dto.AdminLoginResponse; -import in.koreatech.koin.admin.user.dto.AdminNewOwnersResponse; -import in.koreatech.koin.admin.user.dto.AdminOwnerResponse; -import in.koreatech.koin.admin.user.dto.AdminOwnerUpdateRequest; -import in.koreatech.koin.admin.user.dto.AdminOwnerUpdateResponse; -import in.koreatech.koin.admin.user.dto.AdminOwnersResponse; import in.koreatech.koin.admin.user.dto.AdminPasswordChangeRequest; import in.koreatech.koin.admin.user.dto.AdminPermissionUpdateRequest; import in.koreatech.koin.admin.user.dto.AdminResponse; -import in.koreatech.koin.admin.user.dto.AdminStudentResponse; -import in.koreatech.koin.admin.user.dto.AdminStudentUpdateRequest; -import in.koreatech.koin.admin.user.dto.AdminStudentUpdateResponse; -import in.koreatech.koin.admin.user.dto.AdminStudentsResponse; import in.koreatech.koin.admin.user.dto.AdminTokenRefreshRequest; import in.koreatech.koin.admin.user.dto.AdminTokenRefreshResponse; import in.koreatech.koin.admin.user.dto.AdminUpdateRequest; import in.koreatech.koin.admin.user.dto.AdminsCondition; import in.koreatech.koin.admin.user.dto.AdminsResponse; import in.koreatech.koin.admin.user.dto.CreateAdminRequest; -import in.koreatech.koin.admin.user.dto.OwnersCondition; -import in.koreatech.koin.admin.user.dto.StudentsCondition; import in.koreatech.koin.admin.user.enums.TeamType; import in.koreatech.koin.admin.user.enums.TrackType; import in.koreatech.koin.admin.user.service.AdminUserService; @@ -52,28 +39,6 @@ public class AdminUserController implements AdminUserApi{ private final AdminUserService adminUserService; - @PutMapping("/admin/owner/{id}/authed") - public ResponseEntity allowOwnerPermission( - @PathVariable Integer id, - @Auth(permit = {ADMIN}) Integer adminId) { - adminUserService.allowOwnerPermission(id); - return ResponseEntity.ok().build(); - } - - @GetMapping("/admin/students") - public ResponseEntity getStudents( - @RequestParam(required = false) Integer page, - @RequestParam(required = false) Integer limit, - @RequestParam(required = false) Boolean isAuthed, - @RequestParam(required = false) String nickname, - @RequestParam(required = false) String email, - @Auth(permit = {ADMIN}) Integer adminId - ) { - StudentsCondition studentsCondition = new StudentsCondition(page, limit, isAuthed, nickname, email); - AdminStudentsResponse adminStudentsResponse = adminUserService.getStudents(studentsCondition); - return ResponseEntity.ok().body(adminStudentsResponse); - } - @PostMapping("/admin") public ResponseEntity createAdmin( @RequestBody @Valid CreateAdminRequest request, @@ -178,60 +143,6 @@ public ResponseEntity updateAdminPermission( return ResponseEntity.ok().build(); } - @GetMapping("/admin/users/student/{id}") - public ResponseEntity getStudent( - @PathVariable Integer id, - @Auth(permit = {ADMIN}) Integer adminId - ) { - AdminStudentResponse adminStudentResponse = adminUserService.getStudent(id); - return ResponseEntity.ok().body(adminStudentResponse); - } - - @PutMapping("/admin/users/student/{id}") - public ResponseEntity updateStudent( - @Valid @RequestBody AdminStudentUpdateRequest adminRequest, - @PathVariable Integer id, - @Auth(permit = {ADMIN}) Integer adminId - ) { - AdminStudentUpdateResponse adminStudentUpdateResponse = adminUserService.updateStudent(id, adminRequest); - return ResponseEntity.ok().body(adminStudentUpdateResponse); - } - - @GetMapping("/admin/users/owner/{id}") - public ResponseEntity getOwner( - @PathVariable Integer id, - @Auth(permit = {ADMIN}) Integer adminId - ) { - AdminOwnerResponse adminOwnerResponse = adminUserService.getOwner(id); - return ResponseEntity.ok().body(adminOwnerResponse); - } - - @PutMapping("/admin/users/owner/{id}") - public ResponseEntity updateOwner( - @PathVariable Integer id, - @RequestBody @Valid AdminOwnerUpdateRequest request, - @Auth(permit = {ADMIN}) Integer adminId - ) { - AdminOwnerUpdateResponse adminOwnerUpdateResponse = adminUserService.updateOwner(id, request); - return ResponseEntity.ok().body(adminOwnerUpdateResponse); - } - - @GetMapping("/admin/users/new-owners") - public ResponseEntity getNewOwners( - @ParameterObject @ModelAttribute OwnersCondition ownersCondition, - @Auth(permit = {ADMIN}) Integer adminId - ) { - return ResponseEntity.ok().body(adminUserService.getNewOwners(ownersCondition)); - } - - @GetMapping("/admin/users/owners") - public ResponseEntity getOwners( - @ParameterObject @ModelAttribute OwnersCondition ownersCondition, - @Auth(permit = {ADMIN}) Integer adminId - ) { - return ResponseEntity.ok().body(adminUserService.getOwners(ownersCondition)); - } - @GetMapping("/admin/users/{id}") public ResponseEntity getUser( @PathVariable Integer id, diff --git a/src/main/java/in/koreatech/koin/admin/user/service/AdminUserService.java b/src/main/java/in/koreatech/koin/admin/user/service/AdminUserService.java index 0fc2c41d3..2f1dfe0b9 100644 --- a/src/main/java/in/koreatech/koin/admin/user/service/AdminUserService.java +++ b/src/main/java/in/koreatech/koin/admin/user/service/AdminUserService.java @@ -1,68 +1,38 @@ package in.koreatech.koin.admin.user.service; -import static in.koreatech.koin.domain.user.model.UserType.ADMIN; - import java.time.LocalDateTime; -import java.util.List; import java.util.Objects; -import java.util.Optional; import java.util.UUID; -import java.util.stream.Collectors; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Sort; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import in.koreatech.koin.admin.shop.repository.shop.AdminShopRepository; import in.koreatech.koin.admin.user.dto.AdminLoginRequest; import in.koreatech.koin.admin.user.dto.AdminLoginResponse; -import in.koreatech.koin.admin.user.dto.AdminNewOwnersResponse; -import in.koreatech.koin.admin.user.dto.AdminOwnerResponse; -import in.koreatech.koin.admin.user.dto.AdminOwnerUpdateRequest; -import in.koreatech.koin.admin.user.dto.AdminOwnerUpdateResponse; -import in.koreatech.koin.admin.user.dto.AdminOwnersResponse; import in.koreatech.koin.admin.user.dto.AdminPasswordChangeRequest; import in.koreatech.koin.admin.user.dto.AdminPermissionUpdateRequest; import in.koreatech.koin.admin.user.dto.AdminResponse; -import in.koreatech.koin.admin.user.dto.AdminStudentResponse; -import in.koreatech.koin.admin.user.dto.AdminStudentUpdateRequest; -import in.koreatech.koin.admin.user.dto.AdminStudentUpdateResponse; -import in.koreatech.koin.admin.user.dto.AdminStudentsResponse; import in.koreatech.koin.admin.user.dto.AdminTokenRefreshRequest; import in.koreatech.koin.admin.user.dto.AdminTokenRefreshResponse; import in.koreatech.koin.admin.user.dto.AdminUpdateRequest; import in.koreatech.koin.admin.user.dto.AdminsCondition; import in.koreatech.koin.admin.user.dto.AdminsResponse; import in.koreatech.koin.admin.user.dto.CreateAdminRequest; -import in.koreatech.koin.admin.user.dto.OwnersCondition; -import in.koreatech.koin.admin.user.dto.StudentsCondition; import in.koreatech.koin.admin.user.model.Admin; -import in.koreatech.koin.admin.user.repository.AdminOwnerRepository; -import in.koreatech.koin.admin.user.repository.AdminOwnerShopRedisRepository; +import in.koreatech.koin.admin.owner.repository.AdminOwnerRepository; import in.koreatech.koin.admin.user.repository.AdminRepository; -import in.koreatech.koin.admin.user.repository.AdminStudentRepository; +import in.koreatech.koin.admin.student.repository.AdminStudentRepository; import in.koreatech.koin.admin.user.repository.AdminTokenRepository; import in.koreatech.koin.admin.user.repository.AdminUserRepository; -import in.koreatech.koin.domain.owner.model.Owner; -import in.koreatech.koin.domain.owner.model.OwnerIncludingShop; -import in.koreatech.koin.domain.owner.model.OwnerShop; -import in.koreatech.koin.domain.shop.model.shop.Shop; -import in.koreatech.koin.domain.student.exception.StudentDepartmentNotValidException; -import in.koreatech.koin.domain.student.model.Student; -import in.koreatech.koin.domain.student.model.StudentDepartment; -import in.koreatech.koin.domain.user.exception.DuplicationNicknameException; -import in.koreatech.koin.domain.user.exception.UserNotFoundException; +import in.koreatech.koin.admin.user.validation.AdminUserValidation; import in.koreatech.koin.domain.user.model.User; -import in.koreatech.koin.domain.user.model.UserGender; import in.koreatech.koin.domain.user.model.UserToken; import in.koreatech.koin.domain.user.model.UserType; import in.koreatech.koin.global.auth.JwtProvider; import in.koreatech.koin.global.auth.exception.AuthorizationException; -import in.koreatech.koin.global.domain.email.exception.DuplicationEmailException; -import in.koreatech.koin.global.domain.email.model.EmailAddress; import in.koreatech.koin.global.exception.KoinIllegalArgumentException; import in.koreatech.koin.global.model.Criteria; import lombok.RequiredArgsConstructor; @@ -75,22 +45,11 @@ public class AdminUserService { private final JwtProvider jwtProvider; private final AdminStudentRepository adminStudentRepository; private final AdminOwnerRepository adminOwnerRepository; - private final AdminOwnerShopRedisRepository adminOwnerShopRedisRepository; private final AdminUserRepository adminUserRepository; - private final AdminShopRepository adminShopRepository; private final PasswordEncoder passwordEncoder; private final AdminTokenRepository adminTokenRepository; private final AdminRepository adminRepository; - - public AdminStudentsResponse getStudents(StudentsCondition studentsCondition) { - Integer totalStudents = adminStudentRepository.findAllStudentCount(); - Criteria criteria = Criteria.of(studentsCondition.page(), studentsCondition.limit(), totalStudents); - - PageRequest pageRequest = PageRequest.of(criteria.getPage(), criteria.getLimit()); - Page studentsPage = adminStudentRepository.findByConditions(studentsCondition, pageRequest); - - return AdminStudentsResponse.from(studentsPage); - } + private final AdminUserValidation adminUserValidation; @Transactional public AdminResponse createAdmin(CreateAdminRequest request, Integer adminId) { @@ -99,27 +58,12 @@ public AdminResponse createAdmin(CreateAdminRequest request, Integer adminId) { throw new AuthorizationException("어드민 계정 생성 권한이 없습니다."); } - validateAdminCreate(request); + adminUserValidation.validateEmailForAdminCreated(request.email()); Admin createAdmin = adminRepository.save(request.toEntity(passwordEncoder)); return AdminResponse.from(createAdmin); } - private void validateAdminCreate(CreateAdminRequest request) { - EmailAddress emailAddress = EmailAddress.from(request.email()); - emailAddress.validateKoreatechEmail(); - emailAddress.validateAdminEmail(); - - validateDuplicateEmail(request); - } - - private void validateDuplicateEmail(CreateAdminRequest request) { - adminUserRepository.findByEmail(request.email()) - .ifPresent(user -> { - throw DuplicationEmailException.withDetail("account: " + request.email()); - }); - } - @Transactional public void adminPasswordChange(AdminPasswordChangeRequest request, Integer adminId) { Admin admin = adminRepository.getById(adminId); @@ -133,7 +77,7 @@ public void adminPasswordChange(AdminPasswordChangeRequest request, Integer admi @Transactional public AdminLoginResponse adminLogin(AdminLoginRequest request) { User user = adminUserRepository.getByEmail(request.email()); - validateAdminLogin(user, request); + adminUserValidation.validateAdminLogin(user, request); String accessToken = jwtProvider.createToken(user); String refreshToken = String.format("%s-%d", UUID.randomUUID(), user.getId()); @@ -143,25 +87,6 @@ public AdminLoginResponse adminLogin(AdminLoginRequest request) { return AdminLoginResponse.of(accessToken, savedtoken.getRefreshToken()); } - private void validateAdminLogin(User user, AdminLoginRequest request) { - /* 어드민 권한이 없으면 없는 회원으로 간주 */ - if (user.getUserType() != ADMIN) { - throw UserNotFoundException.withDetail("account" + request.email()); - } - - if (adminRepository.findById(user.getId()).isEmpty()) { - throw UserNotFoundException.withDetail("account" + request.email()); - } - - if (!user.isSamePassword(passwordEncoder, request.password())) { - throw new KoinIllegalArgumentException("비밀번호가 틀렸습니다."); - } - - if (!user.isAuthed()) { - throw new AuthorizationException("PL 인증 대기중입니다."); - } - } - @Transactional public void adminLogout(Integer adminId) { adminTokenRepository.deleteById(adminId); @@ -232,145 +157,6 @@ public void updateAdminPermission(AdminPermissionUpdateRequest request, Integer adminRepository.getById(id).updatePermission(request); } - @Transactional - public void allowOwnerPermission(Integer id) { - Owner owner = adminOwnerRepository.getById(id); - owner.getUser().auth(); - Optional ownerShop = adminOwnerShopRedisRepository.findById(id); - if (ownerShop.isPresent()) { - Integer shopId = ownerShop.get().getShopId(); - if (shopId != null) { - Shop shop = adminShopRepository.getById(shopId); - shop.updateOwner(owner); - owner.setGrantShop(true); - } - adminOwnerShopRedisRepository.deleteById(id); - } - } - - public AdminStudentResponse getStudent(Integer userId) { - Student student = adminStudentRepository.getById(userId); - return AdminStudentResponse.from(student); - } - - @Transactional - public AdminStudentUpdateResponse updateStudent(Integer id, AdminStudentUpdateRequest adminRequest) { - Student student = adminStudentRepository.getById(id); - User user = student.getUser(); - validateNicknameDuplication(adminRequest.nickname(), id); - validateDepartmentValid(adminRequest.major()); - user.update(adminRequest.nickname(), adminRequest.name(), - adminRequest.phoneNumber(), UserGender.from(adminRequest.gender())); - user.updateStudentPassword(passwordEncoder, adminRequest.password()); - student.update(adminRequest.studentNumber(), adminRequest.major()); - adminStudentRepository.save(student); - - return AdminStudentUpdateResponse.from(student); - } - - public AdminNewOwnersResponse getNewOwners(OwnersCondition ownersCondition) { - ownersCondition.checkDataConstraintViolation(); - - Integer totalOwners = adminUserRepository.findUsersCountByUserTypeAndIsAuthed(UserType.OWNER, false); - Criteria criteria = Criteria.of(ownersCondition.page(), ownersCondition.limit(), totalOwners); - Sort.Direction direction = ownersCondition.getDirection(); - - Page result = getNewOwnersResultPage(ownersCondition, criteria, direction); - - List ownerIncludingShops = result.getContent().stream() - .map(owner -> { - Optional ownerShop = adminOwnerShopRedisRepository.findById(owner.getId()); - return ownerShop - .map(os -> { - Shop shop = adminShopRepository.findById(os.getShopId()).orElse(null); - return OwnerIncludingShop.of(owner, shop); - }) - .orElseGet(() -> OwnerIncludingShop.of(owner, null)); - }) - .toList(); - - return AdminNewOwnersResponse.of(ownerIncludingShops, result, criteria); - } - - public AdminOwnersResponse getOwners(OwnersCondition ownersCondition) { - ownersCondition.checkDataConstraintViolation(); - - Integer totalOwners = adminUserRepository.findUsersCountByUserTypeAndIsAuthed(UserType.OWNER, true); - Criteria criteria = Criteria.of(ownersCondition.page(), ownersCondition.limit(), totalOwners); - Sort.Direction direction = ownersCondition.getDirection(); - - Page result = getOwnersResultPage(ownersCondition, criteria, direction); - - return AdminOwnersResponse.of(result, criteria); - } - - private Page getOwnersResultPage(OwnersCondition ownersCondition, Criteria criteria, - Sort.Direction direction) { - PageRequest pageRequest = PageRequest.of(criteria.getPage(), criteria.getLimit(), - Sort.by(direction, "user.createdAt")); - - Page result; - - if (ownersCondition.searchType() == OwnersCondition.SearchType.EMAIL) { - result = adminOwnerRepository.findPageOwnersByEmail(ownersCondition.query(), pageRequest); - } else if (ownersCondition.searchType() == OwnersCondition.SearchType.NAME) { - result = adminOwnerRepository.findPageOwnersByName(ownersCondition.query(), pageRequest); - } else { - result = adminOwnerRepository.findPageOwners(pageRequest); - } - - return result; - } - - private Page getNewOwnersResultPage(OwnersCondition ownersCondition, Criteria criteria, - Sort.Direction direction) { - PageRequest pageRequest = PageRequest.of(criteria.getPage(), criteria.getLimit(), - Sort.by(direction, "user.createdAt")); - - Page result; - - if (ownersCondition.searchType() == OwnersCondition.SearchType.EMAIL) { - result = adminOwnerRepository.findPageUnauthenticatedOwnersByEmail(ownersCondition.query(), pageRequest); - } else if (ownersCondition.searchType() == OwnersCondition.SearchType.NAME) { - result = adminOwnerRepository.findPageUnauthenticatedOwnersByName(ownersCondition.query(), pageRequest); - } else { - result = adminOwnerRepository.findPageUnauthenticatedOwners(pageRequest); - } - - return result; - } - - private void validateNicknameDuplication(String nickname, Integer userId) { - if (nickname != null && - adminUserRepository.existsByNicknameAndIdNot(nickname, userId)) { - throw DuplicationNicknameException.withDetail("nickname : " + nickname); - } - } - - private void validateDepartmentValid(String department) { - if (department != null && !StudentDepartment.isValid(department)) { - throw StudentDepartmentNotValidException.withDetail("학부(학과) : " + department); - } - } - - public AdminOwnerResponse getOwner(Integer ownerId) { - Owner owner = adminOwnerRepository.getById(ownerId); - - List shopsId = adminShopRepository.findAllByOwnerId(ownerId) - .stream() - .map(Shop::getId) - .toList(); - - return AdminOwnerResponse.of(owner, shopsId); - } - - @Transactional - public AdminOwnerUpdateResponse updateOwner(Integer ownerId, AdminOwnerUpdateRequest request) { - Owner owner = adminOwnerRepository.getById(ownerId); - owner.update(request); - return AdminOwnerUpdateResponse.from(owner); - } - @Transactional public User getUser(Integer userId) { return adminUserRepository.getById(userId); diff --git a/src/main/java/in/koreatech/koin/admin/user/validation/AdminUserValidation.java b/src/main/java/in/koreatech/koin/admin/user/validation/AdminUserValidation.java new file mode 100644 index 000000000..e62663c9c --- /dev/null +++ b/src/main/java/in/koreatech/koin/admin/user/validation/AdminUserValidation.java @@ -0,0 +1,60 @@ +package in.koreatech.koin.admin.user.validation; + +import static in.koreatech.koin.domain.user.model.UserType.ADMIN; + +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Component; + +import in.koreatech.koin.admin.user.dto.AdminLoginRequest; +import in.koreatech.koin.admin.user.repository.AdminRepository; +import in.koreatech.koin.admin.user.repository.AdminUserRepository; +import in.koreatech.koin.domain.user.exception.UserNotFoundException; +import in.koreatech.koin.domain.user.model.User; +import in.koreatech.koin.global.auth.exception.AuthorizationException; +import in.koreatech.koin.global.domain.email.exception.DuplicationEmailException; +import in.koreatech.koin.global.domain.email.model.EmailAddress; +import in.koreatech.koin.global.exception.KoinIllegalArgumentException; +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +public class AdminUserValidation { + + private final PasswordEncoder passwordEncoder; + private final AdminRepository adminRepository; + private final AdminUserRepository adminUserRepository; + + public void validateEmailForAdminCreated(String email) { + EmailAddress emailAddress = EmailAddress.from(email); + emailAddress.validateKoreatechEmail(); + emailAddress.validateAdminEmail(); + + validateDuplicateEmail(email); + } + + private void validateDuplicateEmail(String email) { + adminUserRepository.findByEmail(email) + .ifPresent(user -> { + throw DuplicationEmailException.withDetail("account: " + email); + }); + } + + public void validateAdminLogin(User user, AdminLoginRequest request) { + /* 어드민 권한이 없으면 없는 회원으로 간주 */ + if (user.getUserType() != ADMIN) { + throw UserNotFoundException.withDetail("account" + request.email()); + } + + if (adminRepository.findById(user.getId()).isEmpty()) { + throw UserNotFoundException.withDetail("account" + request.email()); + } + + if (!user.isSamePassword(passwordEncoder, request.password())) { + throw new KoinIllegalArgumentException("비밀번호가 틀렸습니다."); + } + + if (!user.isAuthed()) { + throw new AuthorizationException("PL 인증 대기중입니다."); + } + } +} \ No newline at end of file diff --git a/src/main/java/in/koreatech/koin/domain/owner/model/Owner.java b/src/main/java/in/koreatech/koin/domain/owner/model/Owner.java index 22c380241..21700c78f 100644 --- a/src/main/java/in/koreatech/koin/domain/owner/model/Owner.java +++ b/src/main/java/in/koreatech/koin/domain/owner/model/Owner.java @@ -9,7 +9,7 @@ import java.util.ArrayList; import java.util.List; -import in.koreatech.koin.admin.user.dto.AdminOwnerUpdateRequest; +import in.koreatech.koin.admin.owner.dto.AdminOwnerUpdateRequest; import in.koreatech.koin.domain.user.model.User; import jakarta.persistence.Column; import jakarta.persistence.Entity; diff --git a/src/test/java/in/koreatech/koin/admin/acceptance/AdminOwnerApiTest.java b/src/test/java/in/koreatech/koin/admin/acceptance/AdminOwnerApiTest.java new file mode 100644 index 000000000..c6eea0431 --- /dev/null +++ b/src/test/java/in/koreatech/koin/admin/acceptance/AdminOwnerApiTest.java @@ -0,0 +1,325 @@ +package in.koreatech.koin.admin.acceptance; + +import static in.koreatech.koin.domain.user.model.UserGender.MAN; +import static in.koreatech.koin.domain.user.model.UserType.OWNER; +import static org.assertj.core.api.SoftAssertions.assertSoftly; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; + +import java.util.ArrayList; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.transaction.annotation.Transactional; + +import in.koreatech.koin.AcceptanceTest; +import in.koreatech.koin.admin.owner.repository.AdminOwnerRepository; +import in.koreatech.koin.admin.owner.repository.AdminOwnerShopRedisRepository; +import in.koreatech.koin.admin.user.model.Admin; +import in.koreatech.koin.domain.owner.model.Owner; +import in.koreatech.koin.domain.owner.model.OwnerAttachment; +import in.koreatech.koin.domain.owner.model.OwnerShop; +import in.koreatech.koin.domain.owner.repository.OwnerShopRedisRepository; +import in.koreatech.koin.domain.shop.model.shop.Shop; +import in.koreatech.koin.domain.user.model.User; +import in.koreatech.koin.fixture.ShopFixture; +import in.koreatech.koin.fixture.UserFixture; + +@SuppressWarnings("NonAsciiCharacters") +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@Transactional +public class AdminOwnerApiTest extends AcceptanceTest { + + @Autowired + private AdminOwnerRepository adminOwnerRepository; + + @Autowired + private AdminOwnerShopRedisRepository adminOwnerShopRedisRepository; + + @Autowired + private OwnerShopRedisRepository ownerShopRedisRepository; + + @Autowired + private UserFixture userFixture; + + @Autowired + private ShopFixture shopFixture; + + @Autowired + private PasswordEncoder passwordEncoder; + + @BeforeAll + void setup() { + clear(); + } + + @Test + void 관리자가_사장님_권한_요청을_허용한다() throws Exception { + Owner owner = userFixture.철수_사장님(); + Shop shop = shopFixture.마슬랜(null, null); + + Admin adminUser = userFixture.코인_운영자(); + String token = userFixture.getToken(adminUser.getUser()); + + OwnerShop ownerShop = OwnerShop.builder() + .ownerId(owner.getId()) + .shopId(shop.getId()) + .build(); + + ownerShopRedisRepository.save(ownerShop); + + mockMvc.perform( + put("/admin/owner/{id}/authed", owner.getUser().getId()) + .header("Authorization", "Bearer " + token) + ) + .andExpect(status().isOk()); + + //영속성 컨테스트 동기화 + Owner updatedOwner = adminOwnerRepository.getById(owner.getId()); + var resultOwnerShop = adminOwnerShopRedisRepository.findById(owner.getId()); + + assertSoftly( + softly -> { + softly.assertThat(updatedOwner.getUser().isAuthed()).isEqualTo(true); + softly.assertThat(updatedOwner.isGrantShop()).isEqualTo(true); + softly.assertThat(resultOwnerShop).isEmpty(); + } + ); + } + + @Test + void 관리자가_특정_사장을_조회한다() throws Exception { + Owner owner = userFixture.현수_사장님(); + Shop shop = shopFixture.마슬랜(owner, null); + + Admin adminUser = userFixture.코인_운영자(); + String token = userFixture.getToken(adminUser.getUser()); + + mockMvc.perform( + get("/admin/users/owner/{id}", owner.getUser().getId()) + .header("Authorization", "Bearer " + token) + ) + .andExpect(status().isOk()) + .andExpect(content().json(String.format(""" + { + "id": 1, + "email": "hysoo@naver.com", + "name": "테스트용_현수", + "nickname": "현수", + "company_registration_number": "123-45-67190", + "attachments_url": [ + "https://test.com/현수_사장님_인증사진_1.jpg", + "https://test.com/현수_사장님_인증사진_2.jpg" + ], + "shops_id": [ + %d + ], + "phone_number": "01098765432", + "is_authed": true, + "user_type": "OWNER", + "gender": 0, + "created_at" : "2024-01-15 12:00:00", + "updated_at" : "2024-01-15 12:00:00", + "last_logged_at" : null + } + """, shop.getId()))); + } + + @Test + void 관리자가_특정_사장을_수정한다() throws Exception { + Owner owner = userFixture.현수_사장님(); + Shop shop = shopFixture.마슬랜(owner, null); + + Admin adminUser = userFixture.코인_운영자(); + String token = userFixture.getToken(adminUser.getUser()); + + mockMvc.perform( + put("/admin/users/owner/{id}", owner.getUser().getId()) + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + .content(""" + { + "company_registration_number" : "123-45-67190", + "grant_shop" : "false", + "grant_event" : "false" + } + """) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" + { + "company_registration_number" : "123-45-67190", + "email" : "hysoo@naver.com", + "gender" : 0, + "grant_shop" : false, + "grant_event" : false, + "name" : "테스트용_현수", + "nickname" : "현수", + "phone_number" : "01098765432" + } + """)); + } + + @Test + void 관리자가_가입_신청한_사장님_리스트_조회한다() throws Exception { + Owner owner = userFixture.철수_사장님(); + Shop shop = shopFixture.마슬랜(null, null); + + Admin adminUser = userFixture.코인_운영자(); + String token = userFixture.getToken(adminUser.getUser()); + + OwnerShop ownerShop = OwnerShop.builder() + .ownerId(owner.getId()) + .shopId(shop.getId()) + .build(); + + ownerShopRedisRepository.save(ownerShop); + + mockMvc.perform( + get("/admin/users/new-owners") + .header("Authorization", "Bearer " + token) + .param("searchType", "NAME") + .param("query", "철수") + .param("sort", "CREATED_AT_DESC") + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" + { + "total_count": 1, + "current_count": 1, + "total_page": 1, + "current_page": 1, + "owners": [ + { + "id": 1, + "email": "testchulsu@gmail.com", + "name": "테스트용_철수(인증X)", + "phone_number": "01097765112", + "shop_id": 1, + "shop_name": "마슬랜 치킨", + "created_at" : "2024-01-15 12:00:00" + } + ] + } + """)); + } + + @Test + void 관리자가_가입_신청한_사장님_리스트_조회한다_V2() throws Exception { + for (int i = 0; i < 11; i++) { + User user = User.builder() + .password(passwordEncoder.encode("1234")) + .nickname("사장님" + i) + .name("테스트용(인증X)" + i) + .phoneNumber("0109776511" + i) + .userType(OWNER) + .gender(MAN) + .email("testchulsu@gmail.com" + i) + .isAuthed(false) + .isDeleted(false) + .build(); + + Owner owner = Owner.builder() + .user(user) + .companyRegistrationNumber("118-80-567" + i) + .grantShop(true) + .grantEvent(true) + .attachments(new ArrayList<>()) + .build(); + + OwnerAttachment attachment1 = OwnerAttachment.builder() + .url("https://test.com/사장님_인증사진_1" + i + ".jpg") + .isDeleted(false) + .owner(owner) + .build(); + + OwnerAttachment attachment2 = OwnerAttachment.builder() + .url("https://test.com/사장님_인증사진_2" + i + ".jpg") + .isDeleted(false) + .owner(owner) + .build(); + + owner.getAttachments().add(attachment1); + owner.getAttachments().add(attachment2); + + adminOwnerRepository.save(owner); + } + + Admin adminUser = userFixture.코인_운영자(); + String token = userFixture.getToken(adminUser.getUser()); + + mockMvc.perform( + get("/admin/users/new-owners") + .header("Authorization", "Bearer " + token) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.total_count").value(11)) + .andExpect(jsonPath("$.current_count").value(10)) + .andExpect(jsonPath("$.total_page").value(2)) + .andExpect(jsonPath("$.current_page").value(1)) + .andExpect(jsonPath("$.owners.length()").value(10)); + } + + @Test + void 관리자가_가입_사장님_리스트_조회한다() throws Exception { + for (int i = 0; i < 11; i++) { + User user = User.builder() + .password(passwordEncoder.encode("1234")) + .nickname("사장님" + i) + .name("테스트용(인증X)" + i) + .phoneNumber("0109776511" + i) + .userType(OWNER) + .gender(MAN) + .email("testchulsu@gmail.com" + i) + .isAuthed(true) + .isDeleted(false) + .build(); + + Owner owner = Owner.builder() + .user(user) + .companyRegistrationNumber("118-80-567" + i) + .grantShop(true) + .grantEvent(true) + .attachments(new ArrayList<>()) + .build(); + + OwnerAttachment attachment1 = OwnerAttachment.builder() + .url("https://test.com/사장님_인증사진_1" + i + ".jpg") + .isDeleted(false) + .owner(owner) + .build(); + + OwnerAttachment attachment2 = OwnerAttachment.builder() + .url("https://test.com/사장님_인증사진_2" + i + ".jpg") + .isDeleted(false) + .owner(owner) + .build(); + + owner.getAttachments().add(attachment1); + owner.getAttachments().add(attachment2); + + adminOwnerRepository.save(owner); + } + + Admin adminUser = userFixture.코인_운영자(); + String token = userFixture.getToken(adminUser.getUser()); + + mockMvc.perform( + get("/admin/users/owners") + .header("Authorization", "Bearer " + token) + + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.total_count").value(11)) + .andExpect(jsonPath("$.current_count").value(10)) + .andExpect(jsonPath("$.total_page").value(2)) + .andExpect(jsonPath("$.current_page").value(1)) + .andExpect(jsonPath("$.owners.length()").value(10)); + } +} diff --git a/src/test/java/in/koreatech/koin/admin/acceptance/AdminStudentApiTest.java b/src/test/java/in/koreatech/koin/admin/acceptance/AdminStudentApiTest.java new file mode 100644 index 000000000..adf17f6dd --- /dev/null +++ b/src/test/java/in/koreatech/koin/admin/acceptance/AdminStudentApiTest.java @@ -0,0 +1,271 @@ +package in.koreatech.koin.admin.acceptance; + +import static in.koreatech.koin.domain.user.model.UserGender.MAN; +import static in.koreatech.koin.domain.user.model.UserIdentity.UNDERGRADUATE; +import static in.koreatech.koin.domain.user.model.UserType.STUDENT; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionTemplate; + +import in.koreatech.koin.AcceptanceTest; +import in.koreatech.koin.admin.student.repository.AdminStudentRepository; +import in.koreatech.koin.admin.user.model.Admin; +import in.koreatech.koin.domain.student.model.Student; +import in.koreatech.koin.domain.user.model.User; +import in.koreatech.koin.domain.user.model.UserGender; +import in.koreatech.koin.fixture.UserFixture; + +@SuppressWarnings("NonAsciiCharacters") +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@Transactional +public class AdminStudentApiTest extends AcceptanceTest { + + @Autowired + private AdminStudentRepository adminStudentRepository; + + @Autowired + private TransactionTemplate transactionTemplate; + + @Autowired + private UserFixture userFixture; + + @Autowired + private PasswordEncoder passwordEncoder; + + @BeforeAll + void setup() { + clear(); + } + + @Test + void 관리자가_학생_리스트를_파라미터가_없이_조회한다_페이지네이션() throws Exception { + Student student = userFixture.준호_학생(); + Admin adminUser = userFixture.코인_운영자(); + + String token = userFixture.getToken(adminUser.getUser()); + + mockMvc.perform( + get("/admin/students") + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" + { + "current_count": 1, + "current_page": 1, + "students": [ + { + "email": "juno@koreatech.ac.kr", + "id": 1, + "major": "컴퓨터공학부", + "name": "테스트용_준호", + "nickname": "준호", + "student_number": "2019136135" + } + ], + "total_count": 1, + "total_page": 1 + } + """)); + } + + @Test + void 관리자가_학생_리스트를_페이지_수와_limits으로_조회한다_페이지네이션() throws Exception { + for (int i = 0; i < 11; i++) { + Student student = Student.builder() + .studentNumber("2019136135") + .anonymousNickname("익명" + i) + .department("컴퓨터공학부") + .userIdentity(UNDERGRADUATE) + .isGraduated(false) + .user( + User.builder() + .password(passwordEncoder.encode("1234")) + .nickname("성재" + i) + .name("테스트용_성재" + i) + .phoneNumber("01012345670") + .userType(STUDENT) + .gender(MAN) + .email("seongjae@koreatech.ac.kr") + .isAuthed(true) + .isDeleted(false) + .build() + ) + .build(); + + adminStudentRepository.save(student); + } + + Admin adminUser = userFixture.코인_운영자(); + String token = userFixture.getToken(adminUser.getUser()); + + mockMvc.perform( + get("/admin/students") + .header("Authorization", "Bearer " + token) + .param("page", "2") + .param("limit", "10") + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" + { + "current_count": 1, + "current_page": 2, + "students": [ + { + "email": "seongjae@koreatech.ac.kr", + "id": 11, + "major": "컴퓨터공학부", + "name": "테스트용_성재10", + "nickname": "성재10", + "student_number": "2019136135" + } + ], + "total_count": 11, + "total_page": 2 + } + """)); + } + + @Test + void 관리자가_학생_리스트를_닉네임으로_조회한다_페이지네이션() throws Exception { + Student student1 = userFixture.성빈_학생(); + Student student2 = userFixture.준호_학생(); + Admin adminUser = userFixture.코인_운영자(); + String token = userFixture.getToken(adminUser.getUser()); + + mockMvc.perform( + get("/admin/students") + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + .param("nickname", "준호") + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" + { + "current_count": 1, + "current_page": 1, + "students": [ + { + "email": "juno@koreatech.ac.kr", + "id": 2, + "major": "컴퓨터공학부", + "name": "테스트용_준호", + "nickname": "준호", + "student_number": "2019136135" + } + ], + "total_count": 1, + "total_page": 1 + } + """)); + } + + @Test + void 관리자가_특정_학생_정보를_조회한다_관리자가_아니면_403_반환() throws Exception { + Student student = userFixture.준호_학생(); + String token = userFixture.getToken(student.getUser()); + + mockMvc.perform( + get("/admin/users/student/{id}", student.getUser().getId()) + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isForbidden()); + } + + @Test + void 관리자가_특정_학생_정보를_조회한다() throws Exception { + Student student = userFixture.준호_학생(); + + Admin adminUser = userFixture.코인_운영자(); + String token = userFixture.getToken(adminUser.getUser()); + + mockMvc.perform( + get("/admin/users/student/{id}", student.getUser().getId()) + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" + { + "anonymous_nickname": "익명", + "created_at": "2024-01-15 12:00:00", + "email": "juno@koreatech.ac.kr", + "gender": 0, + "id": 1, + "is_authed": true, + "is_graduated": false, + "last_logged_at": null, + "major": "컴퓨터공학부", + "name": "테스트용_준호", + "nickname": "준호", + "phone_number": "01012345678", + "student_number": "2019136135", + "updated_at": "2024-01-15 12:00:00", + "user_type": "STUDENT" + } + """)); + } + + @Test + void 관리자가_특정_학생_정보를_수정한다() throws Exception { + Student student = userFixture.준호_학생(); + + Admin adminUser = userFixture.코인_운영자(); + String token = userFixture.getToken(adminUser.getUser()); + + mockMvc.perform( + put("/admin/users/student/{id}", student.getUser().getId()) + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + .content(""" + { + "gender" : 1, + "major" : "기계공학부", + "name" : "서정빈", + "password" : "0c4be6acaba1839d3433c1ccf04e1eec4d1fa841ee37cb019addc269e8bc1b77", + "nickname" : "duehee", + "phone_number" : "01023456789", + "student_number" : "2019136136" + } + """) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" + { + "anonymous_nickname": "익명", + "email": "juno@koreatech.ac.kr", + "gender": 1, + "major": "기계공학부", + "name": "서정빈", + "nickname": "duehee", + "phone_number": "01023456789", + "student_number": "2019136136" + } + """)); + + transactionTemplate.executeWithoutResult(status -> { + Student result = adminStudentRepository.getById(student.getId()); + SoftAssertions.assertSoftly( + softly -> { + softly.assertThat(result.getUser().getName()).isEqualTo("서정빈"); + softly.assertThat(result.getUser().getNickname()).isEqualTo("duehee"); + softly.assertThat(result.getUser().getGender()).isEqualTo(UserGender.from(1)); + softly.assertThat(result.getStudentNumber()).isEqualTo("2019136136"); + } + ); + }); + } +} diff --git a/src/test/java/in/koreatech/koin/admin/acceptance/AdminUserApiTest.java b/src/test/java/in/koreatech/koin/admin/acceptance/AdminUserApiTest.java index 76a8b0ea7..46ce84c2d 100644 --- a/src/test/java/in/koreatech/koin/admin/acceptance/AdminUserApiTest.java +++ b/src/test/java/in/koreatech/koin/admin/acceptance/AdminUserApiTest.java @@ -1,45 +1,25 @@ package in.koreatech.koin.admin.acceptance; -import static in.koreatech.koin.domain.user.model.UserGender.MAN; -import static in.koreatech.koin.domain.user.model.UserIdentity.UNDERGRADUATE; -import static in.koreatech.koin.domain.user.model.UserType.OWNER; -import static in.koreatech.koin.domain.user.model.UserType.STUDENT; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.SoftAssertions.assertSoftly; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; -import java.util.ArrayList; - -import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; -import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.test.web.servlet.MvcResult; import org.springframework.transaction.annotation.Transactional; -import org.springframework.transaction.support.TransactionTemplate; import com.fasterxml.jackson.databind.JsonNode; import in.koreatech.koin.AcceptanceTest; import in.koreatech.koin.admin.user.model.Admin; -import in.koreatech.koin.admin.user.repository.AdminOwnerRepository; -import in.koreatech.koin.admin.user.repository.AdminOwnerShopRedisRepository; import in.koreatech.koin.admin.user.repository.AdminRepository; -import in.koreatech.koin.admin.user.repository.AdminStudentRepository; import in.koreatech.koin.admin.user.repository.AdminUserRepository; -import in.koreatech.koin.domain.owner.model.Owner; -import in.koreatech.koin.domain.owner.model.OwnerAttachment; -import in.koreatech.koin.domain.owner.model.OwnerShop; -import in.koreatech.koin.domain.owner.repository.OwnerShopRedisRepository; -import in.koreatech.koin.domain.shop.model.shop.Shop; import in.koreatech.koin.domain.student.model.Student; import in.koreatech.koin.domain.user.model.User; -import in.koreatech.koin.domain.user.model.UserGender; -import in.koreatech.koin.fixture.ShopFixture; import in.koreatech.koin.fixture.UserFixture; import in.koreatech.koin.support.JsonAssertions; @@ -48,165 +28,20 @@ @Transactional class AdminUserApiTest extends AcceptanceTest { - @Autowired - private AdminStudentRepository adminStudentRepository; - - @Autowired - private AdminOwnerRepository adminOwnerRepository; - @Autowired private AdminUserRepository adminUserRepository; - @Autowired - private AdminOwnerShopRedisRepository adminOwnerShopRedisRepository; - - @Autowired - private OwnerShopRedisRepository ownerShopRedisRepository; - @Autowired private AdminRepository adminRepository; - @Autowired - private TransactionTemplate transactionTemplate; - @Autowired private UserFixture userFixture; - @Autowired - private ShopFixture shopFixture; - - @Autowired - private PasswordEncoder passwordEncoder; - @BeforeAll void setup() { clear(); } - @Test - void 관리자가_학생_리스트를_파라미터가_없이_조회한다_페이지네이션() throws Exception { - Student student = userFixture.준호_학생(); - Admin adminUser = userFixture.코인_운영자(); - - String token = userFixture.getToken(adminUser.getUser()); - - mockMvc.perform( - get("/admin/students") - .header("Authorization", "Bearer " + token) - .contentType(MediaType.APPLICATION_JSON) - ) - .andExpect(status().isOk()) - .andExpect(content().json(""" - { - "current_count": 1, - "current_page": 1, - "students": [ - { - "email": "juno@koreatech.ac.kr", - "id": 1, - "major": "컴퓨터공학부", - "name": "테스트용_준호", - "nickname": "준호", - "student_number": "2019136135" - } - ], - "total_count": 1, - "total_page": 1 - } - """)); - } - - @Test - void 관리자가_학생_리스트를_페이지_수와_limits으로_조회한다_페이지네이션() throws Exception { - for (int i = 0; i < 11; i++) { - Student student = Student.builder() - .studentNumber("2019136135") - .anonymousNickname("익명" + i) - .department("컴퓨터공학부") - .userIdentity(UNDERGRADUATE) - .isGraduated(false) - .user( - User.builder() - .password(passwordEncoder.encode("1234")) - .nickname("성재" + i) - .name("테스트용_성재" + i) - .phoneNumber("01012345670") - .userType(STUDENT) - .gender(MAN) - .email("seongjae@koreatech.ac.kr") - .isAuthed(true) - .isDeleted(false) - .build() - ) - .build(); - - adminStudentRepository.save(student); - } - - Admin adminUser = userFixture.코인_운영자(); - String token = userFixture.getToken(adminUser.getUser()); - - mockMvc.perform( - get("/admin/students") - .header("Authorization", "Bearer " + token) - .param("page", "2") - .param("limit", "10") - ) - .andExpect(status().isOk()) - .andExpect(content().json(""" - { - "current_count": 1, - "current_page": 2, - "students": [ - { - "email": "seongjae@koreatech.ac.kr", - "id": 11, - "major": "컴퓨터공학부", - "name": "테스트용_성재10", - "nickname": "성재10", - "student_number": "2019136135" - } - ], - "total_count": 11, - "total_page": 2 - } - """)); - } - - @Test - void 관리자가_학생_리스트를_닉네임으로_조회한다_페이지네이션() throws Exception { - Student student1 = userFixture.성빈_학생(); - Student student2 = userFixture.준호_학생(); - Admin adminUser = userFixture.코인_운영자(); - String token = userFixture.getToken(adminUser.getUser()); - - mockMvc.perform( - get("/admin/students") - .header("Authorization", "Bearer " + token) - .contentType(MediaType.APPLICATION_JSON) - .param("nickname", "준호") - ) - .andExpect(status().isOk()) - .andExpect(content().json(""" - { - "current_count": 1, - "current_page": 1, - "students": [ - { - "email": "juno@koreatech.ac.kr", - "id": 2, - "major": "컴퓨터공학부", - "name": "테스트용_준호", - "nickname": "준호", - "student_number": "2019136135" - } - ], - "total_count": 1, - "total_page": 1 - } - """)); - } - @Test void 관리자가_로그인_한다() throws Exception { Admin adminUser = userFixture.코인_운영자(); @@ -313,367 +148,6 @@ void setup() { .andExpect(status().isCreated()); } - @Test - void 관리자가_사장님_권한_요청을_허용한다() throws Exception { - Owner owner = userFixture.철수_사장님(); - Shop shop = shopFixture.마슬랜(null, null); - - Admin adminUser = userFixture.코인_운영자(); - String token = userFixture.getToken(adminUser.getUser()); - - OwnerShop ownerShop = OwnerShop.builder() - .ownerId(owner.getId()) - .shopId(shop.getId()) - .build(); - - ownerShopRedisRepository.save(ownerShop); - - mockMvc.perform( - put("/admin/owner/{id}/authed", owner.getUser().getId()) - .header("Authorization", "Bearer " + token) - ) - .andExpect(status().isOk()); - - //영속성 컨테스트 동기화 - Owner updatedOwner = adminOwnerRepository.getById(owner.getId()); - var resultOwnerShop = adminOwnerShopRedisRepository.findById(owner.getId()); - - assertSoftly( - softly -> { - softly.assertThat(updatedOwner.getUser().isAuthed()).isEqualTo(true); - softly.assertThat(updatedOwner.isGrantShop()).isEqualTo(true); - softly.assertThat(resultOwnerShop).isEmpty(); - } - ); - } - - @Test - void 관리자가_특정_학생_정보를_조회한다_관리자가_아니면_403_반환() throws Exception { - Student student = userFixture.준호_학생(); - String token = userFixture.getToken(student.getUser()); - - mockMvc.perform( - get("/admin/users/student/{id}", student.getUser().getId()) - .header("Authorization", "Bearer " + token) - .contentType(MediaType.APPLICATION_JSON) - ) - .andExpect(status().isForbidden()); - } - - @Test - void 관리자가_특정_학생_정보를_조회한다() throws Exception { - Student student = userFixture.준호_학생(); - - Admin adminUser = userFixture.코인_운영자(); - String token = userFixture.getToken(adminUser.getUser()); - - mockMvc.perform( - get("/admin/users/student/{id}", student.getUser().getId()) - .header("Authorization", "Bearer " + token) - .contentType(MediaType.APPLICATION_JSON) - ) - .andExpect(status().isOk()) - .andExpect(content().json(""" - { - "anonymous_nickname": "익명", - "created_at": "2024-01-15 12:00:00", - "email": "juno@koreatech.ac.kr", - "gender": 0, - "id": 1, - "is_authed": true, - "is_graduated": false, - "last_logged_at": null, - "major": "컴퓨터공학부", - "name": "테스트용_준호", - "nickname": "준호", - "phone_number": "01012345678", - "student_number": "2019136135", - "updated_at": "2024-01-15 12:00:00", - "user_type": "STUDENT" - } - """)); - } - - @Test - void 관리자가_특정_학생_정보를_수정한다() throws Exception { - Student student = userFixture.준호_학생(); - - Admin adminUser = userFixture.코인_운영자(); - String token = userFixture.getToken(adminUser.getUser()); - - mockMvc.perform( - put("/admin/users/student/{id}", student.getUser().getId()) - .header("Authorization", "Bearer " + token) - .contentType(MediaType.APPLICATION_JSON) - .content(""" - { - "gender" : 1, - "major" : "기계공학부", - "name" : "서정빈", - "password" : "0c4be6acaba1839d3433c1ccf04e1eec4d1fa841ee37cb019addc269e8bc1b77", - "nickname" : "duehee", - "phone_number" : "01023456789", - "student_number" : "2019136136" - } - """) - ) - .andExpect(status().isOk()) - .andExpect(content().json(""" - { - "anonymous_nickname": "익명", - "email": "juno@koreatech.ac.kr", - "gender": 1, - "major": "기계공학부", - "name": "서정빈", - "nickname": "duehee", - "phone_number": "01023456789", - "student_number": "2019136136" - } - """)); - - transactionTemplate.executeWithoutResult(status -> { - Student result = adminStudentRepository.getById(student.getId()); - SoftAssertions.assertSoftly( - softly -> { - softly.assertThat(result.getUser().getName()).isEqualTo("서정빈"); - softly.assertThat(result.getUser().getNickname()).isEqualTo("duehee"); - softly.assertThat(result.getUser().getGender()).isEqualTo(UserGender.from(1)); - softly.assertThat(result.getStudentNumber()).isEqualTo("2019136136"); - } - ); - }); - } - - @Test - void 관리자가_특정_사장을_조회한다() throws Exception { - Owner owner = userFixture.현수_사장님(); - Shop shop = shopFixture.마슬랜(owner, null); - - Admin adminUser = userFixture.코인_운영자(); - String token = userFixture.getToken(adminUser.getUser()); - - mockMvc.perform( - get("/admin/users/owner/{id}", owner.getUser().getId()) - .header("Authorization", "Bearer " + token) - ) - .andExpect(status().isOk()) - .andExpect(content().json(String.format(""" - { - "id": 1, - "email": "hysoo@naver.com", - "name": "테스트용_현수", - "nickname": "현수", - "company_registration_number": "123-45-67190", - "attachments_url": [ - "https://test.com/현수_사장님_인증사진_1.jpg", - "https://test.com/현수_사장님_인증사진_2.jpg" - ], - "shops_id": [ - %d - ], - "phone_number": "01098765432", - "is_authed": true, - "user_type": "OWNER", - "gender": 0, - "created_at" : "2024-01-15 12:00:00", - "updated_at" : "2024-01-15 12:00:00", - "last_logged_at" : null - } - """, shop.getId()))); - } - - @Test - void 관리자가_특정_사장을_수정한다() throws Exception { - Owner owner = userFixture.현수_사장님(); - Shop shop = shopFixture.마슬랜(owner, null); - - Admin adminUser = userFixture.코인_운영자(); - String token = userFixture.getToken(adminUser.getUser()); - - mockMvc.perform( - put("/admin/users/owner/{id}", owner.getUser().getId()) - .header("Authorization", "Bearer " + token) - .contentType(MediaType.APPLICATION_JSON) - .content(""" - { - "company_registration_number" : "123-45-67190", - "grant_shop" : "false", - "grant_event" : "false" - } - """) - ) - .andExpect(status().isOk()) - .andExpect(content().json(""" - { - "company_registration_number" : "123-45-67190", - "email" : "hysoo@naver.com", - "gender" : 0, - "grant_shop" : false, - "grant_event" : false, - "name" : "테스트용_현수", - "nickname" : "현수", - "phone_number" : "01098765432" - } - """)); - } - - @Test - void 관리자가_가입_신청한_사장님_리스트_조회한다() throws Exception { - Owner owner = userFixture.철수_사장님(); - Shop shop = shopFixture.마슬랜(null, null); - - Admin adminUser = userFixture.코인_운영자(); - String token = userFixture.getToken(adminUser.getUser()); - - OwnerShop ownerShop = OwnerShop.builder() - .ownerId(owner.getId()) - .shopId(shop.getId()) - .build(); - - ownerShopRedisRepository.save(ownerShop); - - mockMvc.perform( - get("/admin/users/new-owners") - .header("Authorization", "Bearer " + token) - .param("searchType", "NAME") - .param("query", "철수") - .param("sort", "CREATED_AT_DESC") - ) - .andExpect(status().isOk()) - .andExpect(content().json(""" - { - "total_count": 1, - "current_count": 1, - "total_page": 1, - "current_page": 1, - "owners": [ - { - "id": 1, - "email": "testchulsu@gmail.com", - "name": "테스트용_철수(인증X)", - "phone_number": "01097765112", - "shop_id": 1, - "shop_name": "마슬랜 치킨", - "created_at" : "2024-01-15 12:00:00" - } - ] - } - """)); - } - - @Test - void 관리자가_가입_신청한_사장님_리스트_조회한다_V2() throws Exception { - for (int i = 0; i < 11; i++) { - User user = User.builder() - .password(passwordEncoder.encode("1234")) - .nickname("사장님" + i) - .name("테스트용(인증X)" + i) - .phoneNumber("0109776511" + i) - .userType(OWNER) - .gender(MAN) - .email("testchulsu@gmail.com" + i) - .isAuthed(false) - .isDeleted(false) - .build(); - - Owner owner = Owner.builder() - .user(user) - .companyRegistrationNumber("118-80-567" + i) - .grantShop(true) - .grantEvent(true) - .attachments(new ArrayList<>()) - .build(); - - OwnerAttachment attachment1 = OwnerAttachment.builder() - .url("https://test.com/사장님_인증사진_1" + i + ".jpg") - .isDeleted(false) - .owner(owner) - .build(); - - OwnerAttachment attachment2 = OwnerAttachment.builder() - .url("https://test.com/사장님_인증사진_2" + i + ".jpg") - .isDeleted(false) - .owner(owner) - .build(); - - owner.getAttachments().add(attachment1); - owner.getAttachments().add(attachment2); - - adminOwnerRepository.save(owner); - } - - Admin adminUser = userFixture.코인_운영자(); - String token = userFixture.getToken(adminUser.getUser()); - - mockMvc.perform( - get("/admin/users/new-owners") - .header("Authorization", "Bearer " + token) - ) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.total_count").value(11)) - .andExpect(jsonPath("$.current_count").value(10)) - .andExpect(jsonPath("$.total_page").value(2)) - .andExpect(jsonPath("$.current_page").value(1)) - .andExpect(jsonPath("$.owners.length()").value(10)); - } - - @Test - void 관리자가_가입_사장님_리스트_조회한다() throws Exception { - for (int i = 0; i < 11; i++) { - User user = User.builder() - .password(passwordEncoder.encode("1234")) - .nickname("사장님" + i) - .name("테스트용(인증X)" + i) - .phoneNumber("0109776511" + i) - .userType(OWNER) - .gender(MAN) - .email("testchulsu@gmail.com" + i) - .isAuthed(true) - .isDeleted(false) - .build(); - - Owner owner = Owner.builder() - .user(user) - .companyRegistrationNumber("118-80-567" + i) - .grantShop(true) - .grantEvent(true) - .attachments(new ArrayList<>()) - .build(); - - OwnerAttachment attachment1 = OwnerAttachment.builder() - .url("https://test.com/사장님_인증사진_1" + i + ".jpg") - .isDeleted(false) - .owner(owner) - .build(); - - OwnerAttachment attachment2 = OwnerAttachment.builder() - .url("https://test.com/사장님_인증사진_2" + i + ".jpg") - .isDeleted(false) - .owner(owner) - .build(); - - owner.getAttachments().add(attachment1); - owner.getAttachments().add(attachment2); - - adminOwnerRepository.save(owner); - } - - Admin adminUser = userFixture.코인_운영자(); - String token = userFixture.getToken(adminUser.getUser()); - - mockMvc.perform( - get("/admin/users/owners") - .header("Authorization", "Bearer " + token) - - ) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.total_count").value(11)) - .andExpect(jsonPath("$.current_count").value(10)) - .andExpect(jsonPath("$.total_page").value(2)) - .andExpect(jsonPath("$.current_page").value(1)) - .andExpect(jsonPath("$.owners.length()").value(10)); - } - @Test void 관리자가_회원을_조회한다() throws Exception { Student student = userFixture.준호_학생();