diff --git a/build.gradle b/build.gradle index 6ddcb097..dc3b9517 100644 --- a/build.gradle +++ b/build.gradle @@ -35,7 +35,6 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'mysql:mysql-connector-java:8.0.32' implementation 'org.springframework.boot:spring-boot-starter-validation' - implementation 'org.openapitools:jackson-databind-nullable:0.2.1' } diff --git a/src/main/java/umc/kkijuk/server/career/service/CareerServiceImpl.java b/src/main/java/umc/kkijuk/server/career/service/CareerServiceImpl.java index 28f4c5e3..2860da17 100644 --- a/src/main/java/umc/kkijuk/server/career/service/CareerServiceImpl.java +++ b/src/main/java/umc/kkijuk/server/career/service/CareerServiceImpl.java @@ -3,7 +3,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import umc.kkijuk.server.career.controller.exception.CareerValidationException; +import umc.kkijuk.server.common.domian.exception.CareerValidationException; import umc.kkijuk.server.career.controller.response.CareerGroupedByResponse; import umc.kkijuk.server.career.controller.response.CareerResponseMessage; import umc.kkijuk.server.career.domain.Career; @@ -11,6 +11,8 @@ import umc.kkijuk.server.career.dto.converter.CareerConverter; import umc.kkijuk.server.career.repository.CareerRepository; import umc.kkijuk.server.career.repository.CategoryRepository; +import umc.kkijuk.server.common.domian.exception.ResourceNotFoundException; + import java.time.LocalDate; import java.util.List; import java.util.Map; @@ -90,7 +92,7 @@ public List getCareerGroupedBy(String status) @Override public Optional findCareer(Long careerId) { return Optional.ofNullable(careerRepository.findById(careerId).orElseThrow( - () -> new CareerValidationException(CareerResponseMessage.CAREER_NOT_FOUND.toString()))); + () -> new ResourceNotFoundException("Career",careerId))); } diff --git a/src/main/java/umc/kkijuk/server/common/LoginUser.java b/src/main/java/umc/kkijuk/server/common/LoginUser.java index 199b7762..33601ae4 100644 --- a/src/main/java/umc/kkijuk/server/common/LoginUser.java +++ b/src/main/java/umc/kkijuk/server/common/LoginUser.java @@ -4,8 +4,9 @@ @Getter public class LoginUser { + private Long id; - private static final LoginUser LOGIN_USER = new LoginUser(0L); + private static final LoginUser LOGIN_USER = new LoginUser(1L); public LoginUser(Long id) { this.id = id; diff --git a/src/main/java/umc/kkijuk/server/career/controller/exception/CareerExceptionControllerAdvice.java b/src/main/java/umc/kkijuk/server/common/controller/CareerExceptionControllerAdvice.java similarity index 92% rename from src/main/java/umc/kkijuk/server/career/controller/exception/CareerExceptionControllerAdvice.java rename to src/main/java/umc/kkijuk/server/common/controller/CareerExceptionControllerAdvice.java index 7045dbee..b15580de 100644 --- a/src/main/java/umc/kkijuk/server/career/controller/exception/CareerExceptionControllerAdvice.java +++ b/src/main/java/umc/kkijuk/server/common/controller/CareerExceptionControllerAdvice.java @@ -1,4 +1,4 @@ -package umc.kkijuk.server.career.controller.exception; +package umc.kkijuk.server.common.controller; import com.fasterxml.jackson.databind.exc.InvalidFormatException; @@ -10,6 +10,7 @@ import org.springframework.web.bind.annotation.RestControllerAdvice; import umc.kkijuk.server.career.controller.response.CareerResponse; import umc.kkijuk.server.career.controller.response.CareerResponseMessage; +import umc.kkijuk.server.common.domian.exception.CareerValidationException; @RestControllerAdvice(assignableTypes = {umc.kkijuk.server.career.controller.CareerController.class}) diff --git a/src/main/java/umc/kkijuk/server/common/controller/ExceptionControllerAdvice.java b/src/main/java/umc/kkijuk/server/common/controller/ExceptionControllerAdvice.java index a03efd20..adeb7d0e 100644 --- a/src/main/java/umc/kkijuk/server/common/controller/ExceptionControllerAdvice.java +++ b/src/main/java/umc/kkijuk/server/common/controller/ExceptionControllerAdvice.java @@ -6,6 +6,7 @@ import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; import umc.kkijuk.server.common.domian.exception.RecruitOwnerMismatchException; +import umc.kkijuk.server.common.domian.exception.InvalidTagNameException; import umc.kkijuk.server.common.domian.exception.ResourceNotFoundException; import umc.kkijuk.server.common.domian.exception.ReviewRecruitMismatchException; import umc.kkijuk.server.common.domian.response.ErrorResponse; @@ -31,4 +32,10 @@ public ErrorResponse ReviewRecruitMatchException(ReviewRecruitMismatchException public ErrorResponse RecruitOwnerMismatchException(RecruitOwnerMismatchException exception) { return new ErrorResponse(exception.getMessage()); } + + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler(InvalidTagNameException.class) + public ErrorResponse InvalidTagNameException(InvalidTagNameException exception) { + return new ErrorResponse(exception.getMessage()); + } } diff --git a/src/main/java/umc/kkijuk/server/career/controller/exception/CareerValidationException.java b/src/main/java/umc/kkijuk/server/common/domian/exception/CareerValidationException.java similarity index 73% rename from src/main/java/umc/kkijuk/server/career/controller/exception/CareerValidationException.java rename to src/main/java/umc/kkijuk/server/common/domian/exception/CareerValidationException.java index c468b0ee..2fec85e1 100644 --- a/src/main/java/umc/kkijuk/server/career/controller/exception/CareerValidationException.java +++ b/src/main/java/umc/kkijuk/server/common/domian/exception/CareerValidationException.java @@ -1,4 +1,4 @@ -package umc.kkijuk.server.career.controller.exception; +package umc.kkijuk.server.common.domian.exception; public class CareerValidationException extends RuntimeException{ diff --git a/src/main/java/umc/kkijuk/server/common/domian/exception/InvalidTagNameException.java b/src/main/java/umc/kkijuk/server/common/domian/exception/InvalidTagNameException.java new file mode 100644 index 00000000..d7a862b3 --- /dev/null +++ b/src/main/java/umc/kkijuk/server/common/domian/exception/InvalidTagNameException.java @@ -0,0 +1,8 @@ +package umc.kkijuk.server.common.domian.exception; + +public class InvalidTagNameException extends RuntimeException{ + public InvalidTagNameException(String message) { + super(message); + + } +} diff --git a/src/main/java/umc/kkijuk/server/member/controller/MemberController.java b/src/main/java/umc/kkijuk/server/member/controller/MemberController.java index 0463df36..4994fa20 100644 --- a/src/main/java/umc/kkijuk/server/member/controller/MemberController.java +++ b/src/main/java/umc/kkijuk/server/member/controller/MemberController.java @@ -1,13 +1,181 @@ package umc.kkijuk.server.member.controller; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Controller; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import umc.kkijuk.server.common.LoginUser; +import umc.kkijuk.server.member.domain.Member; +import umc.kkijuk.server.member.dto.MemberFieldDto; +import umc.kkijuk.server.member.dto.MemberInfoChangeDto; +import umc.kkijuk.server.member.dto.MemberJoinDto; import umc.kkijuk.server.member.service.MemberService; +import lombok.Data; +import lombok.RequiredArgsConstructor; + +import java.time.LocalDate; +import java.util.List; + -@Controller +@Tag(name = "member", description = "회원 관리 API") +@RestController +@RequestMapping("/member") @RequiredArgsConstructor public class MemberController { private final MemberService memberService; + @Operation( + summary = "회원가입 요청", + description = "회원가입 요청을 받아 성공/실패 여부를 반환합니다.") + @PostMapping + public ResponseEntity saveMember(@RequestBody @Valid MemberJoinDto memberJoinDto) { + String passwordConfirm = memberJoinDto.getPasswordConfirm(); + if (!passwordConfirm.equals(memberJoinDto.getPassword())) { + return ResponseEntity + .status(HttpStatus.BAD_REQUEST) + .body(new CreateMemberResponse("Passwords do not match")); + } + + try { + Long memberId = memberService.join(memberJoinDto.toEntity()); + return ResponseEntity + .status(HttpStatus.CREATED) + .body(new CreateMemberResponse(memberId, "Member created successfully")); + } catch (Exception e) { + e.printStackTrace(); + return ResponseEntity + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(new CreateMemberResponse("Member creation failed")); + } + } + + /** + 일단 RequestParam 사용, 나중에 jwt토큰으로 사용자 정보 식별할 수 있도록 변경 + */ + @Operation( + summary = "내 정보 조회", + description = "마이페이지에서 내 정보들을 가져옵니다.") + @GetMapping("/myPage/info") + public ResponseEntity getInfo() { + try { + Long loginUser = LoginUser.get().getId(); + Member member = memberService.getMemberInfo(loginUser); + MemberInfoResponse response = new MemberInfoResponse( + member.getEmail(), + member.getName(), + member.getPhoneNumber(), + member.getBirthDate() + ); + return ResponseEntity.ok(response); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); + } + } + + @Operation( + summary = "내 정보 수정", + description = "내 정보 수정 요청을 받아 성공/실패를 반환합니다.") + @PutMapping("/myPage/info") + public ResponseEntity changeMemberInfo(@RequestBody MemberInfoChangeDto memberInfoChangeDto) { + Long loginUser = LoginUser.get().getId(); + try { + memberService.updateMemberInfo(loginUser, memberInfoChangeDto); + return ResponseEntity.ok() + .body(new ResultResponse("information update success")); + }catch (Exception e){ + return ResponseEntity + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(new ResultResponse("information update failed")); + } + + } + + + @Operation( + summary = "관심분야 조회", + description = "마이페이지에서 관심분야를 조회합니다.") + @GetMapping("/myPage/field") + public ResponseEntity getField() { + try { + Long loginUser = LoginUser.get().getId(); + List memberField = memberService.getMemberField(loginUser); + return ResponseEntity.ok().body(new MemberFieldResponse(memberField)); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); + } + } + + @Operation( + summary = "관심분야 등록/수정", + description = "초기/마이페이지에서 관심분야를 등록/수정합니다.") + @PostMapping({"/field", "/myPage/field"}) + public ResponseEntity postField(@RequestBody @Valid MemberFieldDto memberFieldDto) { + try { + Long loginUserId = LoginUser.get().getId(); + List updatedMember = memberService.updateMemberField(loginUserId, memberFieldDto); + MemberFieldResponse response = new MemberFieldResponse(updatedMember); + return ResponseEntity.ok(response); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); + } + } + + + + @Data + static class CreateMemberResponse { + private Long id; + private String message; + + public CreateMemberResponse(String message) { + this.message = message; + } + + public CreateMemberResponse(Long id, String message) { + this.id = id; + this.message = message; + } + } + + @Data + static class ResultResponse{ + private String message; + + public ResultResponse(String message) { + this.message = message; + } + } + + @Data + static class MemberInfoResponse { + private String email; + private String name; + private String phoneNumber; + private LocalDate birthDate; + + public MemberInfoResponse(String email, String name, String phoneNumber, LocalDate birthDate) { + this.email = email; + this.name = name; + this.phoneNumber = phoneNumber; + this.birthDate = birthDate; + } + } + + @Data + static class MemberFieldResponse{ + private List field; + + public MemberFieldResponse(List field) { + this.field = field; + } + } } + + + + + + diff --git a/src/main/java/umc/kkijuk/server/member/domain/MarketingAgree.java b/src/main/java/umc/kkijuk/server/member/domain/MarketingAgree.java new file mode 100644 index 00000000..a35aa651 --- /dev/null +++ b/src/main/java/umc/kkijuk/server/member/domain/MarketingAgree.java @@ -0,0 +1,11 @@ +package umc.kkijuk.server.member.domain; + +public enum MarketingAgree { + BOTH, + + EMAIL, + + SMS, + + NONE +} diff --git a/src/main/java/umc/kkijuk/server/member/domain/Member.java b/src/main/java/umc/kkijuk/server/member/domain/Member.java index 2628beb1..a3e9016e 100644 --- a/src/main/java/umc/kkijuk/server/member/domain/Member.java +++ b/src/main/java/umc/kkijuk/server/member/domain/Member.java @@ -1,22 +1,23 @@ package umc.kkijuk.server.member.domain; + import jakarta.persistence.*; import jakarta.validation.constraints.NotNull; import lombok.*; -import umc.kkijuk.server.member.converter.StringListConverter; import java.time.LocalDate; import java.util.List; -import java.util.ArrayList; + +import umc.kkijuk.server.common.converter.StringListToStringConverter; @Entity +@Getter @Builder -@Getter @Setter -@NoArgsConstructor @AllArgsConstructor +@NoArgsConstructor public class Member { - @Id @GeneratedValue + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "member_id") private Long id; @@ -35,17 +36,22 @@ public class Member { @NotNull private String password; - @Convert(converter = StringListConverter.class) - private List field = new ArrayList<>(); + + @Convert(converter = StringListToStringConverter.class) + private List field; @NotNull - private Boolean marketingAgree; + @Enumerated(EnumType.STRING) + private MarketingAgree marketingAgree; @NotNull @Enumerated(EnumType.STRING) private State userState; - public Member(String email, String name, String phoneNumber, LocalDate birthDate, String password, Boolean marketingAgree, State userState) { + private LocalDate deleteDate; + + + public Member( String email, String name, String phoneNumber, LocalDate birthDate, String password, MarketingAgree marketingAgree, State userState) { this.email = email; this.name = name; this.phoneNumber = phoneNumber; @@ -54,5 +60,16 @@ public Member(String email, String name, String phoneNumber, LocalDate birthDate this.marketingAgree = marketingAgree; this.userState = userState; } -} + public void changeFieldInfo(List field){ + this.field = field; + } + + public void changeMemberInfo(String phoneNumber, LocalDate birthDate, MarketingAgree marketingAgree){ + this.phoneNumber = phoneNumber; + this.birthDate = birthDate; + this.marketingAgree = marketingAgree; + } + + +} diff --git a/src/main/java/umc/kkijuk/server/member/dto/MemberFieldDto.java b/src/main/java/umc/kkijuk/server/member/dto/MemberFieldDto.java new file mode 100644 index 00000000..f84fcb94 --- /dev/null +++ b/src/main/java/umc/kkijuk/server/member/dto/MemberFieldDto.java @@ -0,0 +1,19 @@ +package umc.kkijuk.server.member.dto; + +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + + +@Data +@NoArgsConstructor +public class MemberFieldDto { + private List field; + + @Builder + public MemberFieldDto(List field) { + this.field = field; + } +} diff --git a/src/main/java/umc/kkijuk/server/member/dto/MemberInfoChangeDto.java b/src/main/java/umc/kkijuk/server/member/dto/MemberInfoChangeDto.java new file mode 100644 index 00000000..b4df23ea --- /dev/null +++ b/src/main/java/umc/kkijuk/server/member/dto/MemberInfoChangeDto.java @@ -0,0 +1,25 @@ +package umc.kkijuk.server.member.dto; + + +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import umc.kkijuk.server.member.domain.MarketingAgree; + +import java.time.LocalDate; + +@Data +@NoArgsConstructor +public class MemberInfoChangeDto { + private Long id; //jwt토큰으로 받을땐 제거 + private String phoneNumber; + private LocalDate birthDate; + private MarketingAgree marketingAgree; + + @Builder + public MemberInfoChangeDto(String phoneNumber, LocalDate birthDate, MarketingAgree marketingAgree) { + this.phoneNumber = phoneNumber; + this.birthDate = birthDate; + this.marketingAgree = marketingAgree; + } +} diff --git a/src/main/java/umc/kkijuk/server/member/dto/MemberJoinDto.java b/src/main/java/umc/kkijuk/server/member/dto/MemberJoinDto.java new file mode 100644 index 00000000..85bd9421 --- /dev/null +++ b/src/main/java/umc/kkijuk/server/member/dto/MemberJoinDto.java @@ -0,0 +1,62 @@ +package umc.kkijuk.server.member.dto; + +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import umc.kkijuk.server.member.domain.MarketingAgree; +import umc.kkijuk.server.member.domain.Member; +import umc.kkijuk.server.member.domain.State; + +import java.time.LocalDate; + +@Data +@NoArgsConstructor +public class MemberJoinDto { + + private String email; + private String name; + private String phoneNumber; + private LocalDate birthDate; + private String password; + private String passwordConfirm; + private MarketingAgree marketingAgree; + private State userState; + +// @Builder +// public MemberJoinDto(String email, String name, String phoneNumber, LocalDate birthDate, +// String password, Boolean marketingAgree, State userState) { +// this.email = email; +// this.name = name; +// this.phoneNumber = phoneNumber; +// this.birthDate = birthDate; +// this.password = password; +// this.marketingAgree = marketingAgree; +// this.userState = userState; +// } + + @Builder + public MemberJoinDto(String email, String name, String phoneNumber, LocalDate birthDate, + String password, String passwordConfirm, MarketingAgree marketingAgree, State userState) { + this.email = email; + this.name = name; + this.phoneNumber = phoneNumber; + this.birthDate = birthDate; + this.password = password; + this.passwordConfirm = passwordConfirm; + this.marketingAgree = marketingAgree; + this.userState = userState; + } + + public Member toEntity() { + return Member.builder() + .email(email) + .name(name) + .phoneNumber(phoneNumber) + .birthDate(birthDate) + .password(password) + .marketingAgree(marketingAgree) + .userState(userState) + .build(); + } + +} diff --git a/src/main/java/umc/kkijuk/server/member/service/MemberService.java b/src/main/java/umc/kkijuk/server/member/service/MemberService.java index 5b6bd96e..3ba0a4ff 100644 --- a/src/main/java/umc/kkijuk/server/member/service/MemberService.java +++ b/src/main/java/umc/kkijuk/server/member/service/MemberService.java @@ -4,9 +4,11 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import umc.kkijuk.server.member.domain.Member; +import umc.kkijuk.server.member.dto.MemberFieldDto; +import umc.kkijuk.server.member.dto.MemberInfoChangeDto; import umc.kkijuk.server.member.repository.MemberJpaRepository; -import umc.kkijuk.server.recruit.domain.Recruit; -import umc.kkijuk.server.recruit.infrastructure.RecruitEntity; + +import java.util.List; @Service @Transactional(readOnly = true) @@ -20,9 +22,38 @@ public Member findOne(Long memberId) { //아직 인터페이스 구현 x, 추후에 구현 후 MemberService -> MemberServiceImpl로 수정 예정. + @Transactional public Long join(Member member) { memberJpaRepository.save(member); return member.getId(); } + public Member getMemberInfo(Long memberId) { + return memberJpaRepository.findById(memberId) + .orElseThrow(() -> new IllegalArgumentException("Member not found")); + } + + public List getMemberField(Long memberId){ + Member member = memberJpaRepository.findById(memberId) + .orElseThrow(() -> new IllegalArgumentException("Member not found")); + return member.getField(); + } + + @Transactional + public List updateMemberField(Long id,MemberFieldDto memberFieldDto){ + Member member = memberJpaRepository.findById(id) + .orElseThrow(() -> new IllegalArgumentException("Member not found")); + member.changeFieldInfo(memberFieldDto.getField()); + return member.getField(); + } + + @Transactional + public Long updateMemberInfo(Long id,MemberInfoChangeDto memberInfoChangeDto){ + Member member = memberJpaRepository.findById(id) + .orElseThrow(() -> new IllegalArgumentException("Member not found")); + member.changeMemberInfo(memberInfoChangeDto.getPhoneNumber(), memberInfoChangeDto.getBirthDate(), memberInfoChangeDto.getMarketingAgree()); + return member.getId(); + } + + } diff --git a/src/main/java/umc/kkijuk/server/tag/controller/TagController.java b/src/main/java/umc/kkijuk/server/tag/controller/TagController.java new file mode 100644 index 00000000..054bd093 --- /dev/null +++ b/src/main/java/umc/kkijuk/server/tag/controller/TagController.java @@ -0,0 +1,43 @@ +package umc.kkijuk.server.tag.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; +import umc.kkijuk.server.tag.controller.response.TagResponse; +import umc.kkijuk.server.tag.domain.Tag; +import umc.kkijuk.server.tag.dto.TagRequestDto; +import umc.kkijuk.server.tag.dto.TagResponseDto; +import umc.kkijuk.server.tag.dto.converter.TagConverter; +import umc.kkijuk.server.tag.service.TagService; + +@io.swagger.v3.oas.annotations.tags.Tag(name="tag",description = "태그 관련 API") +@RestController +@RequiredArgsConstructor +@RequestMapping("/career") +public class TagController { + + private final TagService tagService; + + @PostMapping("/tag") + @Operation(summary = "태그 추가 API", description = "태그 - 태그를 생성하는 API") + public TagResponse create(@RequestBody @Valid TagRequestDto.CreateTagDto request) { + Tag createTag = tagService.createTag(request); + return TagResponse.success(HttpStatus.OK,"태그를 성공적으로 생성했습니다.", TagConverter.toTagResult(createTag)); + } + @GetMapping("/tag") + @Operation(summary = "태그 조회 API", description = "태그 - 태그 조회하는 API") + public TagResponse read() { + return TagResponse.success(HttpStatus.OK, "모든 태그를 성공적으로 조회했습니다.", tagService.findAllTags()); + } + @DeleteMapping("/tag/{tagId}") + @Operation(summary = "태그 삭제 API",description = "태그 - 태그를 삭제하는 API") + @Parameter(name = "tagId",description = "태그 Id, path variable 입니다. 존재하는 태그 Id 값을 넘겨 주세요.",example = "1") + public TagResponse delete(@PathVariable Long tagId) { + tagService.delete(tagId); + return TagResponse.success(HttpStatus.OK, "태그 삭제가 성공적으로 이루어졌습니다.", null); + } + +} diff --git a/src/main/java/umc/kkijuk/server/tag/controller/response/TagResponse.java b/src/main/java/umc/kkijuk/server/tag/controller/response/TagResponse.java new file mode 100644 index 00000000..6c4ed7d4 --- /dev/null +++ b/src/main/java/umc/kkijuk/server/tag/controller/response/TagResponse.java @@ -0,0 +1,24 @@ +package umc.kkijuk.server.tag.controller.response; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Builder; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Builder +@Getter +public class TagResponse { + private int status; + private String message; + @JsonInclude(JsonInclude.Include.NON_NULL) + private T data; + + public TagResponse(final int status,final String message,T data){ + this.status = status; + this.message = message; + this.data = data; + } + public static TagResponse success(HttpStatus status, String message, T data){ + return new TagResponse<>(status.value(),message,data); + } +} diff --git a/src/main/java/umc/kkijuk/server/tag/domain/Tag.java b/src/main/java/umc/kkijuk/server/tag/domain/Tag.java new file mode 100644 index 00000000..f4d5ef09 --- /dev/null +++ b/src/main/java/umc/kkijuk/server/tag/domain/Tag.java @@ -0,0 +1,20 @@ +package umc.kkijuk.server.tag.domain; + +import jakarta.persistence.*; +import jakarta.validation.constraints.NotNull; +import lombok.*; + +@Entity +@Getter +@Builder +@AllArgsConstructor(access = AccessLevel.PROTECTED) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Tag { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name="tag_id") + private Long id; + @NotNull + @Column(name="tag_name", length = 30) + private String name; + +} diff --git a/src/main/java/umc/kkijuk/server/tag/dto/TagRequestDto.java b/src/main/java/umc/kkijuk/server/tag/dto/TagRequestDto.java new file mode 100644 index 00000000..41de90f7 --- /dev/null +++ b/src/main/java/umc/kkijuk/server/tag/dto/TagRequestDto.java @@ -0,0 +1,21 @@ +package umc.kkijuk.server.tag.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + + +public class TagRequestDto { + @Getter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class CreateTagDto{ + @Size(max = 30) + @Schema(description = "태그 이름", example = "역량 키워드", type="string") + String tagName; + } +} diff --git a/src/main/java/umc/kkijuk/server/tag/dto/TagResponseDto.java b/src/main/java/umc/kkijuk/server/tag/dto/TagResponseDto.java new file mode 100644 index 00000000..7fe6f120 --- /dev/null +++ b/src/main/java/umc/kkijuk/server/tag/dto/TagResponseDto.java @@ -0,0 +1,32 @@ +package umc.kkijuk.server.tag.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.List; + +public class TagResponseDto { + @Getter + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class ResultTagDto{ + @Schema(description = "생성된 활동 태그 Id", example = "1", type = "Long") + private Long id; + @Schema(description = "생성된 활동 태그 이름", example = "핵심 역량", type = "String") + private String tagName; + } + @Getter + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class ResultTagDtoList{ + @Schema(description = "활동 태그들의 개수",example = "3") + private int count; + @Schema(description = "활동 태그들 리스트") + private List tagList; + } +} diff --git a/src/main/java/umc/kkijuk/server/tag/dto/converter/TagConverter.java b/src/main/java/umc/kkijuk/server/tag/dto/converter/TagConverter.java new file mode 100644 index 00000000..1fa47ec3 --- /dev/null +++ b/src/main/java/umc/kkijuk/server/tag/dto/converter/TagConverter.java @@ -0,0 +1,35 @@ +package umc.kkijuk.server.tag.dto.converter; + +import umc.kkijuk.server.tag.domain.Tag; +import umc.kkijuk.server.tag.dto.TagRequestDto; +import umc.kkijuk.server.tag.dto.TagResponseDto; + +import java.util.List; +import java.util.stream.Collectors; + +public class TagConverter { + public static Tag toTag(TagRequestDto.CreateTagDto request) { + return Tag.builder() + .name(request.getTagName()) + .build(); + } + public static TagResponseDto.ResultTagDto toTagResult(Tag hashTag) { + return TagResponseDto.ResultTagDto.builder() + .id(hashTag.getId()) + .tagName(hashTag.getName()) + .build(); + } + + public static TagResponseDto.ResultTagDtoList toResultTagDtoList(List tagList) { + return TagResponseDto.ResultTagDtoList.builder() + .count(tagList.size()) + .tagList(tagList.stream().map( + value -> TagResponseDto.ResultTagDto.builder() + .tagName(value.getName()) + .id(value.getId()) + .build() + ).collect(Collectors.toList())) + .build(); + } + +} diff --git a/src/main/java/umc/kkijuk/server/tag/repository/TagRepository.java b/src/main/java/umc/kkijuk/server/tag/repository/TagRepository.java new file mode 100644 index 00000000..c0161873 --- /dev/null +++ b/src/main/java/umc/kkijuk/server/tag/repository/TagRepository.java @@ -0,0 +1,11 @@ +package umc.kkijuk.server.tag.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import umc.kkijuk.server.tag.domain.Tag; + +import java.util.List; + +public interface TagRepository extends JpaRepository { + boolean existsByName(String name); + List findAll(); +} diff --git a/src/main/java/umc/kkijuk/server/tag/service/TagService.java b/src/main/java/umc/kkijuk/server/tag/service/TagService.java new file mode 100644 index 00000000..60e4cdc6 --- /dev/null +++ b/src/main/java/umc/kkijuk/server/tag/service/TagService.java @@ -0,0 +1,12 @@ +package umc.kkijuk.server.tag.service; + +import umc.kkijuk.server.tag.domain.Tag; +import umc.kkijuk.server.tag.dto.TagRequestDto; +import umc.kkijuk.server.tag.dto.TagResponseDto; + + +public interface TagService { + Tag createTag(TagRequestDto.CreateTagDto request); + TagResponseDto.ResultTagDtoList findAllTags(); + void delete(Long tagId); +} diff --git a/src/main/java/umc/kkijuk/server/tag/service/TagServiceImpl.java b/src/main/java/umc/kkijuk/server/tag/service/TagServiceImpl.java new file mode 100644 index 00000000..87ee0fdb --- /dev/null +++ b/src/main/java/umc/kkijuk/server/tag/service/TagServiceImpl.java @@ -0,0 +1,46 @@ +package umc.kkijuk.server.tag.service; + + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import umc.kkijuk.server.common.domian.exception.InvalidTagNameException; +import umc.kkijuk.server.common.domian.exception.ResourceNotFoundException; +import umc.kkijuk.server.common.domian.response.ErrorResponse; +import umc.kkijuk.server.tag.domain.Tag; +import umc.kkijuk.server.tag.dto.TagRequestDto; +import umc.kkijuk.server.tag.dto.TagResponseDto; +import umc.kkijuk.server.tag.dto.converter.TagConverter; +import umc.kkijuk.server.tag.repository.TagRepository; + +import java.util.List; +import java.util.Optional; + +@Service +@Transactional(readOnly = true) +@RequiredArgsConstructor +public class TagServiceImpl implements TagService { + private final TagRepository tagRepository; + @Override + @Transactional + public Tag createTag(TagRequestDto.CreateTagDto request) { + String tagName = request.getTagName(); + if (tagName==null || tagName.trim().isEmpty() || tagRepository.existsByName(tagName)) { + throw new InvalidTagNameException("태그 이름은 비워둘 수 없으며, 이미 존재하는 이름은 사용할 수 없습니다."); + } + Tag hashTag = TagConverter.toTag(request); + return tagRepository.save(hashTag); + } + @Override + public TagResponseDto.ResultTagDtoList findAllTags() { + return TagConverter.toResultTagDtoList(tagRepository.findAll()); + } + @Override + @Transactional + public void delete(Long tagId) { + Tag deleteTag = tagRepository.findById(tagId).orElseThrow(() -> new ResourceNotFoundException("Tag", tagId)); + tagRepository.delete(deleteTag); + } + + +} diff --git a/src/test/java/umc/kkijuk/server/career/service/CareerServiceTest.java b/src/test/java/umc/kkijuk/server/career/service/CareerServiceTest.java index 4bb070e9..9b232090 100644 --- a/src/test/java/umc/kkijuk/server/career/service/CareerServiceTest.java +++ b/src/test/java/umc/kkijuk/server/career/service/CareerServiceTest.java @@ -10,8 +10,8 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.test.annotation.DirtiesContext; -import umc.kkijuk.server.career.controller.exception.CareerExceptionControllerAdvice; -import umc.kkijuk.server.career.controller.exception.CareerValidationException; +import umc.kkijuk.server.common.controller.CareerExceptionControllerAdvice; +import umc.kkijuk.server.common.domian.exception.CareerValidationException; import umc.kkijuk.server.career.controller.response.CareerGroupedByResponse; import umc.kkijuk.server.career.controller.response.CareerResponse; import umc.kkijuk.server.career.controller.response.CareerResponseMessage; @@ -21,6 +21,7 @@ import umc.kkijuk.server.career.dto.CareerResponseDto; import umc.kkijuk.server.career.repository.CareerRepository; import umc.kkijuk.server.career.repository.CategoryRepository; +import umc.kkijuk.server.common.domian.exception.ResourceNotFoundException; import java.time.LocalDate; import java.util.List; @@ -172,7 +173,7 @@ void init() { .build(); //when //then - assertThrows(CareerValidationException.class, () -> careerService.updateCareer(999L,updateCareerDto)); + assertThrows(ResourceNotFoundException.class, () -> careerService.updateCareer(999L,updateCareerDto)); } @Test void update_수정시_날짜_형식이_잘못된_경우_에러() { @@ -225,7 +226,7 @@ void init() { //given //when //then - assertThrows(CareerValidationException.class, () -> careerService.deleteCareer(999L)); + assertThrows(ResourceNotFoundException.class, () -> careerService.deleteCareer(999L)); } diff --git a/src/test/java/umc/kkijuk/server/career/service/CareerServiceUnitTest.java b/src/test/java/umc/kkijuk/server/career/service/CareerServiceUnitTest.java index 1b2f479d..b25fc4b6 100644 --- a/src/test/java/umc/kkijuk/server/career/service/CareerServiceUnitTest.java +++ b/src/test/java/umc/kkijuk/server/career/service/CareerServiceUnitTest.java @@ -6,12 +6,14 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import umc.kkijuk.server.career.controller.exception.CareerValidationException; +import umc.kkijuk.server.common.domian.exception.CareerValidationException; import umc.kkijuk.server.career.domain.Career; import umc.kkijuk.server.career.domain.Category; import umc.kkijuk.server.career.dto.CareerRequestDto; import umc.kkijuk.server.career.dto.CareerResponseDto; import umc.kkijuk.server.career.repository.CareerRepository; +import umc.kkijuk.server.common.domian.exception.ResourceNotFoundException; + import java.time.LocalDate; import java.util.Arrays; import java.util.List; @@ -96,7 +98,7 @@ void init() { when(careerRepository.findById(anyLong())).thenReturn(Optional.empty()); //when //then - assertThrows(CareerValidationException.class, ()->{ + assertThrows(ResourceNotFoundException.class, ()->{ careerService.deleteCareer(1L);}); verify(careerRepository,never()).delete(any(Career.class)); } @@ -115,7 +117,7 @@ void init() { // when // then - assertThrows(CareerValidationException.class, () -> careerService.updateCareer(999L, updateCareerDto)); + assertThrows(ResourceNotFoundException.class, () -> careerService.updateCareer(999L, updateCareerDto)); verify(careerRepository, never()).save(any(Career.class)); } diff --git a/src/test/java/umc/kkijuk/server/member/service/MemberServiceTest.java b/src/test/java/umc/kkijuk/server/member/service/MemberServiceTest.java index 879f0bb3..a25613f9 100644 --- a/src/test/java/umc/kkijuk/server/member/service/MemberServiceTest.java +++ b/src/test/java/umc/kkijuk/server/member/service/MemberServiceTest.java @@ -1,39 +1,98 @@ package umc.kkijuk.server.member.service; - import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.transaction.annotation.Transactional; +import umc.kkijuk.server.member.domain.MarketingAgree; import umc.kkijuk.server.member.domain.Member; import umc.kkijuk.server.member.domain.State; +import umc.kkijuk.server.member.dto.MemberJoinDto; import umc.kkijuk.server.member.repository.MemberJpaRepository; import java.time.LocalDate; -import java.util.List; import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; - -@SpringBootTest +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @Transactional public class MemberServiceTest { - @Autowired MemberService memberService; + @Autowired + MemberService memberService; + @Autowired MemberJpaRepository memberJpaRepository; + @Autowired + private TestRestTemplate restTemplate; + @Test public void 유저정보db저장및조회() throws Exception { - //given - Member member1 = new Member("asd@naver.com", "홍길동", "010-7444-1768", LocalDate.now(), "passwordTest", Boolean.TRUE, State.ACTIVATE); - //when + // given + Member member1 = new Member("asd@naver.com", "홍길동", "010-7444-1768", LocalDate.now(), "passwordTest", MarketingAgree.BOTH, State.ACTIVATE); + + // when Long savedId = memberService.join(member1); Optional member2 = memberJpaRepository.findById(savedId); - //then + + // then assertEquals(member1, member2.get()); } + + @Test + public void 회원가입성공() { + // given + MemberJoinDto memberJoinDto = MemberJoinDto.builder() + .email("asd@naver.com") + .name("홍길동") + .phoneNumber("010-7444-1768") + .birthDate(LocalDate.now()) + .password("passwordTest") + .passwordConfirm("passwordTest") + .marketingAgree(MarketingAgree.BOTH) + .userState(State.ACTIVATE) + .build(); + + HttpEntity request = new HttpEntity<>(memberJoinDto); + + // when + ResponseEntity response = restTemplate.postForEntity("/member", request, String.class); + + // then + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CREATED); + assertThat(response.getBody()).contains("Member created successfully"); + } + + @Test + public void 회원가입실패_비밀번호불일치() { + // given + MemberJoinDto memberJoinDto = MemberJoinDto.builder() + .email("asd@naver.com") + .name("홍길동") + .phoneNumber("010-7444-1768") + .birthDate(LocalDate.now()) + .password("passwordTest") + .passwordConfirm("wrongPassword") + .marketingAgree(MarketingAgree.BOTH) + .userState(State.ACTIVATE) + .build(); + + HttpEntity request = new HttpEntity<>(memberJoinDto); + + // when + ResponseEntity response = restTemplate.postForEntity("/member", request, String.class); + + // then + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST); + assertThat(response.getBody()).contains("Passwords do not match"); + } + + } diff --git a/src/test/java/umc/kkijuk/server/tag/service/TagServiceTest.java b/src/test/java/umc/kkijuk/server/tag/service/TagServiceTest.java new file mode 100644 index 00000000..82f80dd8 --- /dev/null +++ b/src/test/java/umc/kkijuk/server/tag/service/TagServiceTest.java @@ -0,0 +1,138 @@ +package umc.kkijuk.server.tag.service; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.annotation.DirtiesContext; +import umc.kkijuk.server.common.domian.exception.InvalidTagNameException; +import umc.kkijuk.server.common.domian.exception.ResourceNotFoundException; +import umc.kkijuk.server.tag.domain.Tag; +import umc.kkijuk.server.tag.dto.TagRequestDto; +import umc.kkijuk.server.tag.dto.TagResponseDto; +import umc.kkijuk.server.tag.repository.TagRepository; + +import java.util.List; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@SpringBootTest +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) +public class TagServiceTest { + @Autowired + private TagService tagService; + @Autowired + private TagRepository tagRepository; + + private Tag tag1; + private Tag tag2; + @BeforeEach + void init() { + tag1 = Tag.builder() + .name("test tag1") + .build(); + tag2 = Tag.builder() + .name("test tag2") + .build(); + tagRepository.save(tag1); + tagRepository.save(tag2); + } + + @Test + void create_새로운_tag_만들기() { + //given + TagRequestDto.CreateTagDto request = TagRequestDto.CreateTagDto.builder() + .tagName("test tag3") + .build(); + //when + Tag newTag = tagService.createTag(request); + //then + assertAll( + ()->assertThat(newTag.getId()).isEqualTo(3L), + ()->assertThat(newTag.getName()).isEqualTo("test tag3") + ); + } + @Test + void create_새로운_tag_만들기_null_입력시_에러() { + //given + TagRequestDto.CreateTagDto request = TagRequestDto.CreateTagDto.builder() + .tagName("") + .build(); + //when + //then + assertThrows(InvalidTagNameException.class, () -> tagService.createTag(request)); + + } + @Test + void create_새로운_tag_만들기_빈_문자열_입력시_에러() { + //given + TagRequestDto.CreateTagDto request = TagRequestDto.CreateTagDto.builder() + .tagName(" ") + .build(); + //when + //then + assertThrows(InvalidTagNameException.class, () -> tagService.createTag(request)); + + } + @Test + void create_새로운_tag_만들기_존재하는_tagName_입력시_에러() { + //given + TagRequestDto.CreateTagDto request = TagRequestDto.CreateTagDto.builder() + .tagName("test tag1") + .build(); + //when + //then + assertThrows(InvalidTagNameException.class, () -> tagService.createTag(request)); + + + } + @Test + void delete_기존_tag_삭제하기() { + //given + Long tagId = 1L; + //when + tagService.delete(tagId); + //then + Optional deletedTag = tagRepository.findById(tagId); + assertThat(deletedTag).isEmpty(); + + } + @Test + void delete_기존_tag_삭제하기_없는_tagId_입력시_에러() { + //given + Long tagId = 999L; + //when + //given + assertThrows(ResourceNotFoundException.class, () -> tagService.delete(tagId)); + + } + @Test + void read_태그_전부_조회하기() { + //given + //when + TagResponseDto.ResultTagDtoList tagListResult = tagService.findAllTags(); + List tagList = tagListResult.getTagList(); + + TagResponseDto.ResultTagDto tag1 = tagList.get(0); + TagResponseDto.ResultTagDto tag2 = tagList.get(1); + + //then + assertAll( + () -> assertThat(tagListResult.getCount()).isEqualTo(2), + () -> assertThat(tagListResult.getTagList().size()).isEqualTo(2), + () -> assertThat(tagList).isNotEmpty(), + () -> assertThat(tagList.size()).isEqualTo(2), + () -> assertThat(tag1.getId()).isEqualTo(1L), + () -> assertThat(tag1.getTagName()).isEqualTo("test tag1"), + () -> assertThat(tag2.getId()).isEqualTo(2L), + () -> assertThat(tag2.getTagName()).isEqualTo("test tag2") + + ); + + + } + +}