From 193045eddf7d41ac3791de6cbeb839f398837f64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=EB=8F=99=EB=AF=BC?= <114240463+oznchex@users.noreply.github.com> Date: Sun, 15 Sep 2024 16:42:57 +0900 Subject: [PATCH] Revert "[remove] : Remove Attach" --- .../profile/domain/attach/QAttachUrl.java | 55 ++++ .../team/domain/miniprofile/QTeamKeyword.java | 53 ++++ .../profile/domain/attach/AttachFile.java | 52 ++++ .../profile/domain/attach/AttachUrl.java | 54 ++++ .../attach/AttachFileRepository.java | 17 ++ .../attach/AttachUrlRepository.java | 8 + .../attach/AttachUrlRepositoryCustom.java | 13 + .../attach/AttachUrlRepositoryCustomImpl.java | 61 ++++ .../profile/dto/response/ProfileResponse.java | 11 +- .../response/attach/AttachFileResponse.java | 23 ++ .../dto/response/attach/AttachResponse.java | 29 ++ .../response/attach/AttachUrlResponse.java | 21 ++ .../browse/BrowsePrivateProfileResponse.java | 8 +- .../presentation/AttachController.java | 63 ++++ .../BrowsePrivateProfileController.java | 20 +- .../presentation/ProfileController.java | 17 +- .../linkit/profile/service/AttachService.java | 289 ++++++++++++++++++ .../service/BrowsePrivateProfileService.java | 15 +- .../profile/service/ProfileService.java | 26 +- .../presentation/AttachControllerTest.java | 268 ++++++++++++++++ .../BrowsePrivateProfileControllerTest.java | 30 +- .../presentation/ProfileControllerTest.java | 49 ++- 22 files changed, 1156 insertions(+), 26 deletions(-) create mode 100644 src/main/generated/liaison/linkit/profile/domain/attach/QAttachUrl.java create mode 100644 src/main/generated/liaison/linkit/team/domain/miniprofile/QTeamKeyword.java create mode 100644 src/main/java/liaison/linkit/profile/domain/attach/AttachFile.java create mode 100644 src/main/java/liaison/linkit/profile/domain/attach/AttachUrl.java create mode 100644 src/main/java/liaison/linkit/profile/domain/repository/attach/AttachFileRepository.java create mode 100644 src/main/java/liaison/linkit/profile/domain/repository/attach/AttachUrlRepository.java create mode 100644 src/main/java/liaison/linkit/profile/domain/repository/attach/AttachUrlRepositoryCustom.java create mode 100644 src/main/java/liaison/linkit/profile/domain/repository/attach/AttachUrlRepositoryCustomImpl.java create mode 100644 src/main/java/liaison/linkit/profile/dto/response/attach/AttachFileResponse.java create mode 100644 src/main/java/liaison/linkit/profile/dto/response/attach/AttachResponse.java create mode 100644 src/main/java/liaison/linkit/profile/dto/response/attach/AttachUrlResponse.java create mode 100644 src/main/java/liaison/linkit/profile/presentation/AttachController.java create mode 100644 src/main/java/liaison/linkit/profile/service/AttachService.java create mode 100644 src/test/java/liaison/linkit/profile/presentation/AttachControllerTest.java diff --git a/src/main/generated/liaison/linkit/profile/domain/attach/QAttachUrl.java b/src/main/generated/liaison/linkit/profile/domain/attach/QAttachUrl.java new file mode 100644 index 00000000..d970edfc --- /dev/null +++ b/src/main/generated/liaison/linkit/profile/domain/attach/QAttachUrl.java @@ -0,0 +1,55 @@ +package liaison.linkit.profile.domain.attach; + +import static com.querydsl.core.types.PathMetadataFactory.*; + +import com.querydsl.core.types.dsl.*; + +import com.querydsl.core.types.PathMetadata; +import javax.annotation.processing.Generated; +import com.querydsl.core.types.Path; +import com.querydsl.core.types.dsl.PathInits; + + +/** + * QAttachUrl is a Querydsl query type for AttachUrl + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QAttachUrl extends EntityPathBase { + + private static final long serialVersionUID = 1845519294L; + + private static final PathInits INITS = PathInits.DIRECT2; + + public static final QAttachUrl attachUrl = new QAttachUrl("attachUrl"); + + public final StringPath attachUrlName = createString("attachUrlName"); + + public final StringPath attachUrlPath = createString("attachUrlPath"); + + public final NumberPath id = createNumber("id", Long.class); + + public final liaison.linkit.profile.domain.QProfile profile; + + public QAttachUrl(String variable) { + this(AttachUrl.class, forVariable(variable), INITS); + } + + public QAttachUrl(Path path) { + this(path.getType(), path.getMetadata(), PathInits.getFor(path.getMetadata(), INITS)); + } + + public QAttachUrl(PathMetadata metadata) { + this(metadata, PathInits.getFor(metadata, INITS)); + } + + public QAttachUrl(PathMetadata metadata, PathInits inits) { + this(AttachUrl.class, metadata, inits); + } + + public QAttachUrl(Class type, PathMetadata metadata, PathInits inits) { + super(type, metadata, inits); + this.profile = inits.isInitialized("profile") ? new liaison.linkit.profile.domain.QProfile(forProperty("profile"), inits.get("profile")) : null; + } + +} + diff --git a/src/main/generated/liaison/linkit/team/domain/miniprofile/QTeamKeyword.java b/src/main/generated/liaison/linkit/team/domain/miniprofile/QTeamKeyword.java new file mode 100644 index 00000000..28e629c8 --- /dev/null +++ b/src/main/generated/liaison/linkit/team/domain/miniprofile/QTeamKeyword.java @@ -0,0 +1,53 @@ +package liaison.linkit.team.domain.miniprofile; + +import static com.querydsl.core.types.PathMetadataFactory.*; + +import com.querydsl.core.types.dsl.*; + +import com.querydsl.core.types.PathMetadata; +import javax.annotation.processing.Generated; +import com.querydsl.core.types.Path; +import com.querydsl.core.types.dsl.PathInits; + + +/** + * QTeamKeyword is a Querydsl query type for TeamKeyword + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QTeamKeyword extends EntityPathBase { + + private static final long serialVersionUID = -1972105873L; + + private static final PathInits INITS = PathInits.DIRECT2; + + public static final QTeamKeyword teamKeyword = new QTeamKeyword("teamKeyword"); + + public final NumberPath id = createNumber("id", Long.class); + + public final liaison.linkit.team.domain.QTeam team; + + public final StringPath teamKeywordNames = createString("teamKeywordNames"); + + public QTeamKeyword(String variable) { + this(TeamKeyword.class, forVariable(variable), INITS); + } + + public QTeamKeyword(Path path) { + this(path.getType(), path.getMetadata(), PathInits.getFor(path.getMetadata(), INITS)); + } + + public QTeamKeyword(PathMetadata metadata) { + this(metadata, PathInits.getFor(metadata, INITS)); + } + + public QTeamKeyword(PathMetadata metadata, PathInits inits) { + this(TeamKeyword.class, metadata, inits); + } + + public QTeamKeyword(Class type, PathMetadata metadata, PathInits inits) { + super(type, metadata, inits); + this.team = inits.isInitialized("team") ? new liaison.linkit.team.domain.QTeam(forProperty("team"), inits.get("team")) : null; + } + +} + diff --git a/src/main/java/liaison/linkit/profile/domain/attach/AttachFile.java b/src/main/java/liaison/linkit/profile/domain/attach/AttachFile.java new file mode 100644 index 00000000..13bf6863 --- /dev/null +++ b/src/main/java/liaison/linkit/profile/domain/attach/AttachFile.java @@ -0,0 +1,52 @@ +//package liaison.linkit.profile.domain.attach; +// +//import jakarta.persistence.*; +//import liaison.linkit.profile.domain.Profile; +//import liaison.linkit.profile.dto.request.attach.AttachFileUpdateRequest; +//import lombok.AccessLevel; +//import lombok.AllArgsConstructor; +//import lombok.Getter; +//import lombok.NoArgsConstructor; +// +//import static jakarta.persistence.CascadeType.ALL; +//import static jakarta.persistence.FetchType.LAZY; +// +//@Entity +//@Getter +//@AllArgsConstructor +//@NoArgsConstructor(access = AccessLevel.PROTECTED) +//@Table(name = "attach_file") +//public class AttachFile { +// +// @Id +// @GeneratedValue(strategy = GenerationType.IDENTITY) +// @Column(name = "attach_file_id") +// private Long id; +// +// @ManyToOne(fetch = LAZY, cascade = ALL) +// @JoinColumn(name = "profile_id") +// private Profile profile; +// +// @Column(nullable = false) +// private String attachFileName; +// +// @Column(nullable = false) +// private String attachFilePath; +// +// public static AttachFile of( +// final Profile profile, +// final String attachFileName, +// final String attachFilePath +// ) { +// return new AttachFile( +// null, +// profile, +// attachFileName, +// attachFilePath +// ); +// } +// +// public void update(final AttachFileUpdateRequest updateRequest) { +// this.attachFilePath = updateRequest.getAttachFilePath(); +// } +//} diff --git a/src/main/java/liaison/linkit/profile/domain/attach/AttachUrl.java b/src/main/java/liaison/linkit/profile/domain/attach/AttachUrl.java new file mode 100644 index 00000000..8b70aa68 --- /dev/null +++ b/src/main/java/liaison/linkit/profile/domain/attach/AttachUrl.java @@ -0,0 +1,54 @@ +package liaison.linkit.profile.domain.attach; + +import jakarta.persistence.*; +import liaison.linkit.profile.domain.Profile; +import liaison.linkit.profile.dto.request.attach.AttachUrlUpdateRequest; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import static jakarta.persistence.FetchType.LAZY; +import static jakarta.persistence.GenerationType.IDENTITY; + +@Entity +@Getter +@AllArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Table(name = "attach_url") +public class AttachUrl { + + @Id + @GeneratedValue(strategy = IDENTITY) + @Column(name = "attach_url_id") + private Long id; + + @ManyToOne(fetch = LAZY) + @JoinColumn(name = "profile_id") + private Profile profile; + + // 웹 링크 이름 + @Column(nullable = false) + private String attachUrlName; + + @Column(nullable = false) + private String attachUrlPath; + + public static AttachUrl of( + final Profile profile, + final String attachUrlName, + final String attachUrlPath + ) { + return new AttachUrl( + null, + profile, + attachUrlName, + attachUrlPath + ); + } + + public void update(final AttachUrlUpdateRequest updateRequest) { + this.attachUrlName = updateRequest.getAttachUrlName(); + this.attachUrlPath = updateRequest.getAttachUrlPath(); + } +} diff --git a/src/main/java/liaison/linkit/profile/domain/repository/attach/AttachFileRepository.java b/src/main/java/liaison/linkit/profile/domain/repository/attach/AttachFileRepository.java new file mode 100644 index 00000000..4e8ef601 --- /dev/null +++ b/src/main/java/liaison/linkit/profile/domain/repository/attach/AttachFileRepository.java @@ -0,0 +1,17 @@ +//package liaison.linkit.profile.domain.repository.attach; +// +//import liaison.linkit.profile.domain.attach.AttachFile; +//import org.springframework.data.jpa.repository.JpaRepository; +//import org.springframework.data.jpa.repository.Query; +//import org.springframework.data.repository.query.Param; +// +//import java.util.List; +// +//public interface AttachFileRepository extends JpaRepository { +// boolean existsByProfileId(final Long profileId); +// +// AttachFile findByProfileId(@Param("profileId") final Long profileId); +// +// @Query("SELECT attachFile FROM AttachFile attachFile WHERE attachFile.profile.id = :profileId") +// List findAllByProfileId(@Param("profileId") Long profileId); +//} diff --git a/src/main/java/liaison/linkit/profile/domain/repository/attach/AttachUrlRepository.java b/src/main/java/liaison/linkit/profile/domain/repository/attach/AttachUrlRepository.java new file mode 100644 index 00000000..b689a9cb --- /dev/null +++ b/src/main/java/liaison/linkit/profile/domain/repository/attach/AttachUrlRepository.java @@ -0,0 +1,8 @@ +package liaison.linkit.profile.domain.repository.attach; + +import liaison.linkit.profile.domain.attach.AttachUrl; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface AttachUrlRepository extends JpaRepository, AttachUrlRepositoryCustom{ + +} diff --git a/src/main/java/liaison/linkit/profile/domain/repository/attach/AttachUrlRepositoryCustom.java b/src/main/java/liaison/linkit/profile/domain/repository/attach/AttachUrlRepositoryCustom.java new file mode 100644 index 00000000..f2e9cc4c --- /dev/null +++ b/src/main/java/liaison/linkit/profile/domain/repository/attach/AttachUrlRepositoryCustom.java @@ -0,0 +1,13 @@ +package liaison.linkit.profile.domain.repository.attach; + +import liaison.linkit.profile.domain.attach.AttachUrl; + +import java.util.List; +import java.util.Optional; + +public interface AttachUrlRepositoryCustom { + Optional findByProfileId(final Long profileId); + boolean existsByProfileId(final Long profileId); + List findAllByProfileId(final Long profileId); + void deleteAllByProfileId(final Long profileId); +} diff --git a/src/main/java/liaison/linkit/profile/domain/repository/attach/AttachUrlRepositoryCustomImpl.java b/src/main/java/liaison/linkit/profile/domain/repository/attach/AttachUrlRepositoryCustomImpl.java new file mode 100644 index 00000000..c09d5775 --- /dev/null +++ b/src/main/java/liaison/linkit/profile/domain/repository/attach/AttachUrlRepositoryCustomImpl.java @@ -0,0 +1,61 @@ +package liaison.linkit.profile.domain.repository.attach; + +import com.querydsl.jpa.impl.JPAQueryFactory; +import liaison.linkit.profile.domain.attach.AttachUrl; +import liaison.linkit.profile.domain.attach.QAttachUrl; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Optional; + +@RequiredArgsConstructor +@Slf4j +public class AttachUrlRepositoryCustomImpl implements AttachUrlRepositoryCustom { + + private final JPAQueryFactory jpaQueryFactory; + + @Override + public Optional findByProfileId(Long profileId) { + QAttachUrl attachUrl = QAttachUrl.attachUrl; + + AttachUrl result = jpaQueryFactory + .selectFrom(attachUrl) + .where(attachUrl.profile.id.eq(profileId)) + .fetchOne(); + + return Optional.ofNullable(result); + } + + @Override + public boolean existsByProfileId(final Long profileId) { + QAttachUrl attachUrl = QAttachUrl.attachUrl; + + long count = jpaQueryFactory + .selectFrom(attachUrl) + .where(attachUrl.profile.id.eq(profileId)) + .fetchCount(); // 레코드의 수를 반환 + + return count > 0; // 0보다 크면 true, 그렇지 않으면 false + } + + @Override + public List findAllByProfileId(final Long profileId){ + QAttachUrl attachUrl = QAttachUrl.attachUrl; + return jpaQueryFactory.selectFrom(attachUrl) + .where(attachUrl.profile.id.eq(profileId)) + .fetch(); + } + + + @Override + @Transactional + public void deleteAllByProfileId(final Long profileId){ + log.info("Deleting all AttachUrls for profileId: {}", profileId); + QAttachUrl attachUrl = QAttachUrl.attachUrl; + jpaQueryFactory.delete(attachUrl) + .where(attachUrl.profile.id.eq(profileId)) + .execute(); + } +} diff --git a/src/main/java/liaison/linkit/profile/dto/response/ProfileResponse.java b/src/main/java/liaison/linkit/profile/dto/response/ProfileResponse.java index d417c605..b603c415 100644 --- a/src/main/java/liaison/linkit/profile/dto/response/ProfileResponse.java +++ b/src/main/java/liaison/linkit/profile/dto/response/ProfileResponse.java @@ -1,13 +1,14 @@ package liaison.linkit.profile.dto.response; import liaison.linkit.profile.dto.response.antecedents.AntecedentsResponse; +import liaison.linkit.profile.dto.response.attach.AttachResponse; import liaison.linkit.profile.dto.response.awards.AwardsResponse; import liaison.linkit.profile.dto.response.completion.CompletionResponse; import liaison.linkit.profile.dto.response.education.EducationResponse; import liaison.linkit.profile.dto.response.miniProfile.MiniProfileResponse; import liaison.linkit.profile.dto.response.onBoarding.JobAndSkillResponse; -import liaison.linkit.profile.dto.response.profileRegion.ProfileRegionResponse; import liaison.linkit.profile.dto.response.teamBuilding.ProfileTeamBuildingFieldResponse; +import liaison.linkit.profile.dto.response.profileRegion.ProfileRegionResponse; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -28,6 +29,7 @@ public class ProfileResponse { private final List antecedentsResponse; private final List educationResponse; private final List awardsResponse; + private final AttachResponse attachResponse; public ProfileResponse() { this.isPrivateProfileEssential = false; @@ -40,6 +42,7 @@ public ProfileResponse() { this.antecedentsResponse = null; this.educationResponse = null; this.awardsResponse = null; + this.attachResponse = null; } public static ProfileResponse profileItems( @@ -52,7 +55,8 @@ public static ProfileResponse profileItems( final ProfileRegionResponse profileRegionResponse, final List antecedentsResponses, final List educationResponses, - final List awardsResponses + final List awardsResponses, + final AttachResponse attachResponse ) { return new ProfileResponse( @@ -65,7 +69,8 @@ public static ProfileResponse profileItems( profileRegionResponse, antecedentsResponses, educationResponses, - awardsResponses + awardsResponses, + attachResponse ); } } diff --git a/src/main/java/liaison/linkit/profile/dto/response/attach/AttachFileResponse.java b/src/main/java/liaison/linkit/profile/dto/response/attach/AttachFileResponse.java new file mode 100644 index 00000000..6cc9d0b1 --- /dev/null +++ b/src/main/java/liaison/linkit/profile/dto/response/attach/AttachFileResponse.java @@ -0,0 +1,23 @@ +//package liaison.linkit.profile.dto.response.attach; +// +//import liaison.linkit.profile.domain.attach.AttachFile; +//import lombok.AllArgsConstructor; +//import lombok.Getter; +// +//@Getter +//@AllArgsConstructor +//public class AttachFileResponse { +// private Long id; +// private String attachFileName; +// private String attachFilePath; +// +// public static AttachFileResponse personalAttachFile( +// final AttachFile attachFile +// ) { +// return new AttachFileResponse( +// attachFile.getId(), +// attachFile.getAttachFileName(), +// attachFile.getAttachFilePath() +// ); +// } +//} diff --git a/src/main/java/liaison/linkit/profile/dto/response/attach/AttachResponse.java b/src/main/java/liaison/linkit/profile/dto/response/attach/AttachResponse.java new file mode 100644 index 00000000..4e01c126 --- /dev/null +++ b/src/main/java/liaison/linkit/profile/dto/response/attach/AttachResponse.java @@ -0,0 +1,29 @@ +package liaison.linkit.profile.dto.response.attach; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.List; + +@Getter +@AllArgsConstructor +public class AttachResponse { + + private List attachUrlResponseList; +// private List attachFileResponseList; + + public AttachResponse() { + this.attachUrlResponseList = null; +// this.attachFileResponseList = null; + } + + public static AttachResponse getAttachResponse( + final List attachUrlResponses) +// final List attachFileResponses) + { + return new AttachResponse( + attachUrlResponses +// attachFileResponses + ); + } +} diff --git a/src/main/java/liaison/linkit/profile/dto/response/attach/AttachUrlResponse.java b/src/main/java/liaison/linkit/profile/dto/response/attach/AttachUrlResponse.java new file mode 100644 index 00000000..0ef60bdb --- /dev/null +++ b/src/main/java/liaison/linkit/profile/dto/response/attach/AttachUrlResponse.java @@ -0,0 +1,21 @@ +package liaison.linkit.profile.dto.response.attach; + +import liaison.linkit.profile.domain.attach.AttachUrl; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class AttachUrlResponse { + private final Long id; + private final String attachUrlName; + private final String attachUrlPath; + + public static AttachUrlResponse personalAttachUrl(final AttachUrl attachUrl) { + return new AttachUrlResponse( + attachUrl.getId(), + attachUrl.getAttachUrlName(), + attachUrl.getAttachUrlPath() + ); + } +} diff --git a/src/main/java/liaison/linkit/profile/dto/response/browse/BrowsePrivateProfileResponse.java b/src/main/java/liaison/linkit/profile/dto/response/browse/BrowsePrivateProfileResponse.java index aecd1189..6d082287 100644 --- a/src/main/java/liaison/linkit/profile/dto/response/browse/BrowsePrivateProfileResponse.java +++ b/src/main/java/liaison/linkit/profile/dto/response/browse/BrowsePrivateProfileResponse.java @@ -2,6 +2,7 @@ import liaison.linkit.profile.dto.response.ProfileIntroductionResponse; import liaison.linkit.profile.dto.response.antecedents.AntecedentsResponse; +import liaison.linkit.profile.dto.response.attach.AttachResponse; import liaison.linkit.profile.dto.response.awards.AwardsResponse; import liaison.linkit.profile.dto.response.completion.CompletionResponse; import liaison.linkit.profile.dto.response.education.EducationResponse; @@ -28,6 +29,7 @@ public class BrowsePrivateProfileResponse { private final List antecedentsResponse; private final List educationResponse; private final List awardsResponse; + private final AttachResponse attachResponse; public static BrowsePrivateProfileResponse privateProfile( final Long profileId, @@ -39,7 +41,8 @@ public static BrowsePrivateProfileResponse privateProfile( final ProfileRegionResponse profileRegionResponse, final List antecedentsResponses, final List educationResponses, - final List awardsResponses + final List awardsResponses, + final AttachResponse attachResponse ) { return new BrowsePrivateProfileResponse( profileId, @@ -51,7 +54,8 @@ public static BrowsePrivateProfileResponse privateProfile( profileRegionResponse, antecedentsResponses, educationResponses, - awardsResponses + awardsResponses, + attachResponse ); } } diff --git a/src/main/java/liaison/linkit/profile/presentation/AttachController.java b/src/main/java/liaison/linkit/profile/presentation/AttachController.java new file mode 100644 index 00000000..408ece22 --- /dev/null +++ b/src/main/java/liaison/linkit/profile/presentation/AttachController.java @@ -0,0 +1,63 @@ +package liaison.linkit.profile.presentation; + +import jakarta.validation.Valid; +import liaison.linkit.auth.Auth; +import liaison.linkit.auth.MemberOnly; +import liaison.linkit.auth.domain.Accessor; +import liaison.linkit.profile.dto.request.attach.AttachUrlCreateRequest; +import liaison.linkit.profile.dto.response.attach.AttachResponse; +import liaison.linkit.profile.service.AttachService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping +@Slf4j +public class AttachController { + + private final AttachService attachService; + + // 외부 링크 1개 생성 요청 + @PostMapping("/private/attach/url") + @MemberOnly + public ResponseEntity createAttachUrl( + @Auth final Accessor accessor, + @RequestBody @Valid List attachUrlCreateRequests + ) { + if (attachUrlCreateRequests.isEmpty()) { + log.info("attachUrlCreateRequests 비어있습니다."); + attachService.deleteAllUrl(accessor.getMemberId()); + } else { + attachService.saveUrl(accessor.getMemberId(), attachUrlCreateRequests); + } + return ResponseEntity.status(HttpStatus.CREATED).build(); + } + + // 외부 링크 1개 삭제 요청 + @DeleteMapping("/private/attach/url/{attachUrlId}") + @MemberOnly + public ResponseEntity deleteAttachUrl( + @Auth final Accessor accessor, + @PathVariable final Long attachUrlId + ) { + attachService.validateAttachUrlByMember(accessor.getMemberId()); + attachService.deleteUrl(accessor.getMemberId(), attachUrlId); + return ResponseEntity.noContent().build(); + } + + @GetMapping("/private/attach/url/list") + @MemberOnly + public ResponseEntity getAttachList( + @Auth final Accessor accessor + ) { + + final AttachResponse attachResponse = attachService.getAttachList(accessor.getMemberId()); + return ResponseEntity.ok().body(attachResponse); + } +} diff --git a/src/main/java/liaison/linkit/profile/presentation/BrowsePrivateProfileController.java b/src/main/java/liaison/linkit/profile/presentation/BrowsePrivateProfileController.java index 0e7e5698..3ba811a7 100644 --- a/src/main/java/liaison/linkit/profile/presentation/BrowsePrivateProfileController.java +++ b/src/main/java/liaison/linkit/profile/presentation/BrowsePrivateProfileController.java @@ -7,6 +7,7 @@ import liaison.linkit.global.exception.ExceptionCode; import liaison.linkit.profile.dto.response.ProfileIntroductionResponse; import liaison.linkit.profile.dto.response.antecedents.AntecedentsResponse; +import liaison.linkit.profile.dto.response.attach.AttachResponse; import liaison.linkit.profile.dto.response.awards.AwardsResponse; import liaison.linkit.profile.dto.response.browse.BrowsePrivateProfileResponse; import liaison.linkit.profile.dto.response.completion.CompletionResponse; @@ -43,6 +44,7 @@ public class BrowsePrivateProfileController { public final AntecedentsService antecedentsService; public final EducationService educationService; public final AwardsService awardsService; + public final AttachService attachService; public final ProfileRegionService profileRegionService; public final BrowsePrivateProfileService browsePrivateProfileService; @@ -99,6 +101,9 @@ public ResponseEntity getBrowsePrivateProfile( final List awardsResponses = getAwardsResponses(browseTargetPrivateProfileId, profileIsValueResponse.isAwards()); log.info("awardsResponses={}", awardsResponses); + final AttachResponse attachResponse = getAttachResponses(browseTargetPrivateProfileId, profileIsValueResponse.isAttachUrl()); + log.info("attachResponse={}", attachResponse); + final BrowsePrivateProfileResponse browsePrivateProfileResponse = browsePrivateProfileService.getProfileResponse( browseTargetPrivateProfileId, miniProfileResponse, @@ -109,7 +114,8 @@ public ResponseEntity getBrowsePrivateProfile( profileRegionResponse, antecedentsResponses, educationResponses, - awardsResponses + awardsResponses, + attachResponse ); return ResponseEntity.ok().body(browsePrivateProfileResponse); @@ -118,6 +124,18 @@ public ResponseEntity getBrowsePrivateProfile( } } + private AttachResponse getAttachResponses( + final Long browseTargetPrivateProfileId, + final boolean isAttachUrl + ) { + if (isAttachUrl) { + attachService.validateAttachUrlByProfile(browseTargetPrivateProfileId); + return attachService.getBrowseAttachList(browseTargetPrivateProfileId); + } else { + return new AttachResponse(); + } + } + private List getAwardsResponses( final Long browseTargetPrivateProfileId, final boolean isAwards diff --git a/src/main/java/liaison/linkit/profile/presentation/ProfileController.java b/src/main/java/liaison/linkit/profile/presentation/ProfileController.java index 3b4a4476..a4c41c02 100644 --- a/src/main/java/liaison/linkit/profile/presentation/ProfileController.java +++ b/src/main/java/liaison/linkit/profile/presentation/ProfileController.java @@ -9,6 +9,7 @@ import liaison.linkit.profile.dto.response.ProfileIntroductionResponse; import liaison.linkit.profile.dto.response.ProfileResponse; import liaison.linkit.profile.dto.response.antecedents.AntecedentsResponse; +import liaison.linkit.profile.dto.response.attach.AttachResponse; import liaison.linkit.profile.dto.response.awards.AwardsResponse; import liaison.linkit.profile.dto.response.completion.CompletionResponse; import liaison.linkit.profile.dto.response.education.EducationResponse; @@ -43,6 +44,7 @@ public class ProfileController { public final AntecedentsService antecedentsService; public final EducationService educationService; public final AwardsService awardsService; + public final AttachService attachService; public final ProfileRegionService profileRegionService; // 자기소개 생성/수정 메서드 @@ -88,6 +90,7 @@ public ResponseEntity getMyProfile(@Auth final Accessor accessor) { final List antecedentsResponses = getAntecedentsResponses(accessor.getMemberId(), profileIsValueResponse.isAntecedents()); final List educationResponses = getEducationResponses(accessor.getMemberId(), profileIsValueResponse.isEducation()); final List awardsResponses = getAwardsResponses(accessor.getMemberId(), profileIsValueResponse.isAwards()); + final AttachResponse attachResponse = getAttachResponses(accessor.getMemberId(), profileIsValueResponse.isAttachUrl()); final ProfileResponse profileResponse = profileService.getProfileResponse( isPrivateProfileEssential, @@ -99,7 +102,8 @@ public ResponseEntity getMyProfile(@Auth final Accessor accessor) { profileRegionResponse, antecedentsResponses, educationResponses, - awardsResponses + awardsResponses, + attachResponse ); return ResponseEntity.ok().body(profileResponse); @@ -213,4 +217,15 @@ private List getAntecedentsResponses( return null; } } + + private AttachResponse getAttachResponses( + final Long memberId, + final boolean isAttachUrl + ) { + if (isAttachUrl) { + return attachService.getAttachList(memberId); + } else { + return new AttachResponse(); + } + } } diff --git a/src/main/java/liaison/linkit/profile/service/AttachService.java b/src/main/java/liaison/linkit/profile/service/AttachService.java new file mode 100644 index 00000000..c4965e64 --- /dev/null +++ b/src/main/java/liaison/linkit/profile/service/AttachService.java @@ -0,0 +1,289 @@ +package liaison.linkit.profile.service; + +import liaison.linkit.global.exception.AuthException; +import liaison.linkit.global.exception.BadRequestException; +import liaison.linkit.global.exception.FileException; +import liaison.linkit.image.infrastructure.S3Uploader; +import liaison.linkit.profile.domain.Profile; +import liaison.linkit.profile.domain.attach.AttachUrl; +import liaison.linkit.profile.domain.repository.profile.ProfileRepository; +import liaison.linkit.profile.domain.repository.attach.AttachUrlRepository; +import liaison.linkit.profile.dto.request.attach.AttachUrlCreateRequest; +import liaison.linkit.profile.dto.request.attach.AttachUrlUpdateRequest; +import liaison.linkit.profile.dto.response.attach.AttachResponse; +import liaison.linkit.profile.dto.response.attach.AttachUrlResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +import static liaison.linkit.global.exception.ExceptionCode.*; + +@Service +@RequiredArgsConstructor +@Transactional +@Slf4j +public class AttachService { + private final ProfileRepository profileRepository; + private final AttachUrlRepository attachUrlRepository; +// private final AttachFileRepository attachFileRepository; + private final S3Uploader s3Uploader; + private final ApplicationEventPublisher publisher; + + // 모든 "내 이력서" 서비스 계층에 필요한 profile 조회 메서드 + private Profile getProfile(final Long memberId) { + return profileRepository.findByMemberId(memberId) + .orElseThrow(() -> new BadRequestException(NOT_FOUND_PROFILE_BY_MEMBER_ID)); + } + + // 단일 첨부 URL 조회 + private AttachUrl getAttachUrl(final Long attachUrlId) { + return attachUrlRepository.findById(attachUrlId) + .orElseThrow(() -> new BadRequestException(NOT_FOUND_ATTACH_URL_BY_ID)); + } + + // 전체 첨부 URL 조회 + private List getAttachUrls(final Long profileId) { + try { + return attachUrlRepository.findAllByProfileId(profileId); + } catch (Exception e) { + throw new BadRequestException(NOT_FOUND_ATTACH_URLS_BY_PROFILE_ID); + } + } + + // 해당 회원이 1개라도 Attach URL 보유하고 있는지 + public void validateAttachUrlByMember(final Long memberId) { + if (!attachUrlRepository.existsByProfileId(getProfile(memberId).getId())) { + throw new AuthException(INVALID_ATTACH_URL_WITH_PROFILE); + } + } + + public void validateAttachUrlByProfile(final Long profileId) { + if (!attachUrlRepository.existsByProfileId(profileId)) { + throw new AuthException(INVALID_ATTACH_URL_WITH_PROFILE); + } + } + + // 단일 첨부 File 조회 +// private AttachFile getAttachFile(final Long attachFileId) { +// return attachFileRepository.findById(attachFileId) +// .orElseThrow(() -> new BadRequestException(NOT_FOUND_ATTACH_FILE_BY_ID)); +// } +// + // 전체 첨부 File 조회 +// private List getAttachFiles(final Long profileId) { +// try { +// return attachFileRepository.findAllByProfileId(profileId); +// } catch (Exception e) { +// throw new BadRequestException(NOT_FOUND_ATTACH_URLS_BY_PROFILE_ID); +// } +// } + + // 해당 회원이 1개라도 Attach File 보유하고 있는지 +// public void validateAttachFileByMember(final Long memberId) { +// if (!attachFileRepository.existsByProfileId(getProfile(memberId).getId())) { +// throw new AuthException(INVALID_ATTACH_FILE_WITH_PROFILE); +// } +// } + + // validate 및 실제 비즈니스 로직 구분 라인 ------------------------------------------------------------- + + public void saveUrl( + final Long memberId, + final List attachUrlCreateRequests + ) { + final Profile profile = getProfile(memberId); + + // 기존에 존재 이력이 있다면 + if (attachUrlRepository.existsByProfileId(profile.getId())) { + attachUrlRepository.deleteAllByProfileId(profile.getId()); + profile.updateIsAttachUrl(false); + profile.updateMemberProfileTypeByCompletion(); + } + + attachUrlCreateRequests.forEach(request -> { + saveAttachUrl(profile, request); + }); + + profile.updateIsAttachUrl(true); + profile.updateMemberProfileTypeByCompletion(); + } + + private void saveAttachUrl(final Profile profile, final AttachUrlCreateRequest attachUrlCreateRequest) { + final AttachUrl newAttachUrl = AttachUrl.of( + profile, + attachUrlCreateRequest.getAttachUrlName(), + attachUrlCreateRequest.getAttachUrlPath() + ); + attachUrlRepository.save(newAttachUrl); + } + + public AttachUrlResponse getAttachUrlDetail(final Long attachUrlId) { + final AttachUrl attachUrl = attachUrlRepository.findById(attachUrlId) + .orElseThrow(() -> new BadRequestException(NOT_FOUND_ATTACH_URL_ID)); + return getAttachUrlResponse(attachUrl); + } + + private AttachUrlResponse getAttachUrlResponse(final AttachUrl attachUrl) { + return AttachUrlResponse.personalAttachUrl(attachUrl); + } + + // 수정 메서드 + // 특정 URL 수정 + public void updateUrl(final Long attachUrlId, final AttachUrlUpdateRequest updateRequest) { + final AttachUrl attachUrl = getAttachUrl(attachUrlId); + attachUrl.update(updateRequest); + } + + public void deleteAllUrl(final Long memberId) { + final Profile profile = getProfile(memberId); + if (attachUrlRepository.existsByProfileId(profile.getId())) { + attachUrlRepository.deleteAllByProfileId(profile.getId()); + profile.updateIsAttachUrl(false); + profile.updateMemberProfileTypeByCompletion(); + } + } + + // 삭제 메서드 + public void deleteUrl(final Long memberId, final Long attachUrlId) { + final Profile profile = getProfile(memberId); + final AttachUrl attachUrl = getAttachUrl(attachUrlId); + + attachUrlRepository.deleteById(attachUrl.getId()); + + if (!attachUrlRepository.existsByProfileId(profile.getId())) { + profile.cancelPerfectionTen(); + profile.updateMemberProfileTypeByCompletion(); + } + } + +// public void saveFile( +// final Long memberId, +// final MultipartFile attachFile +// ) { +// final Profile profile = getProfile(memberId); +// final String attachFileUrl = saveFileS3(attachFile); +// final AttachFile newAttachFile = AttachFile.of( +// profile, +// attachFile.getOriginalFilename(), +// attachFileUrl +// ); +// +// attachFileRepository.save(newAttachFile); +// profile.updateIsAttachFile(true); +// // 프로필 상태 관리 첨부용으로 추가 필요 +// } +// +// private String saveFileS3(final MultipartFile attachFile) { +// validateSizeofFile(attachFile); +// final PortfolioFile portfolioFile = new PortfolioFile(attachFile); +// return uploadPortfolioFile(portfolioFile); +// } + +// private String uploadPortfolioFile(final PortfolioFile portfolioFile) { +// try { +// return s3Uploader.uploadPortfolioFile(portfolioFile); +// } catch (final Exception e) { +// publisher.publishEvent(new S3PortfolioEvent(portfolioFile.getHashedName())); +// throw e; +// } +// } + + private void validateSizeofFile(final MultipartFile attachFile) { + if (attachFile == null || attachFile.isEmpty()) { + throw new FileException(EMPTY_ATTACH_FILE); + } + } + + // 조회 메서드 +// public AttachFileResponse getAttachFileDetail(final Long attachFileId) { +// final AttachFile attachFile = attachFileRepository.findById(attachFileId) +// .orElseThrow(() -> new BadRequestException(NOT_FOUND_ATTACH_FILE_ID)); +// return getAttachFileResponse(attachFile); +// } + +// private AttachFileResponse getAttachFileResponse(final AttachFile attachFile) { +// return AttachFileResponse.personalAttachFile(attachFile); +// } + +// // 수정 메서드 +// public void updateFile(final Long memberId, final AttachFileUpdateRequest updateRequest) { +// final Profile profile = getProfile(memberId); +// final Long attachFileId = validateAttachFileByMember(memberId); +// +// final AttachFile attachFile = attachFileRepository.findById(attachFileId) +// .orElseThrow(() -> new BadRequestException(NOT_FOUND_ATTACH_FILE_ID)); +// +// attachFile.update(updateRequest); +// attachFileRepository.save(attachFile); +// } +// +// public void deleteFile(final Long memberId) { +// final Profile profile = profileRepository.findByMemberId(memberId); +// final Long attachFileId = validateAttachFileByMember(memberId); +// +// if (!attachFileRepository.existsByProfileId(attachFileId)) { +// throw new BadRequestException(NOT_FOUND_ATTACH_FILE_ID); +// } +// +// attachFileRepository.deleteById(attachFileId); +// +// // 프로그레스바 상태 관련 함수 추가 필요 +// } + + public AttachResponse getAttachList(final Long memberId) { + final Profile profile = getProfile(memberId); + + final List attachUrls = attachUrlRepository.findAllByProfileId(profile.getId()); + log.info("attachUrls={}", attachUrls); + +// final List attachFiles = attachFileRepository.findAllByProfileId(profile.getId()); +// log.info("attachFiles={}", attachFiles); + + final List attachUrlResponses = attachUrls.stream().map(this::getAttachUrlResponse).toList(); + log.info("attachUrlResponses={}", attachUrlResponses); + +// final List attachFileResponses = attachFiles.stream().map(this::getAttachFileResponse).toList(); +// log.info("attachFileResponses={}", attachFileResponses); + + return AttachResponse.getAttachResponse(attachUrlResponses); + } + @Transactional(readOnly = true) + public AttachResponse getBrowseAttachList(final Long profileId) { + final Profile profile = profileRepository.findById(profileId) + .orElseThrow(() -> new BadRequestException(NOT_FOUND_PROFILE_BY_ID)); + + final List attachUrls = attachUrlRepository.findAllByProfileId(profile.getId()); + log.info("attachUrls={}", attachUrls); + +// final List attachFiles = attachFileRepository.findAllByProfileId(profile.getId()); +// log.info("attachFiles={}", attachFiles); + + final List attachUrlResponses = attachUrls.stream().map(this::getAttachUrlResponse).toList(); + log.info("attachUrlResponses={}", attachUrlResponses); + +// final List attachFileResponses = attachFiles.stream().map(this::getAttachFileResponse).toList(); +// log.info("attachFileResponses={}", attachFileResponses); + + return AttachResponse.getAttachResponse(attachUrlResponses); + } + + + + +// public void deleteFile( +// final Long memberId, +// final Long attachFileUrlId +// ) { +// final Profile profile = getProfile(memberId); +// attachFileRepository.deleteById(attachFileUrlId); +// if (!attachFileRepository.existsByProfileId(profile.getId()) && !attachUrlRepository.existsByProfileId(profile.getId())) { +// profile.cancelPerfectionTen(); +// profile.updateMemberProfileTypeByCompletion(); +// } +// } +} diff --git a/src/main/java/liaison/linkit/profile/service/BrowsePrivateProfileService.java b/src/main/java/liaison/linkit/profile/service/BrowsePrivateProfileService.java index f2f047e2..09f2cb2a 100644 --- a/src/main/java/liaison/linkit/profile/service/BrowsePrivateProfileService.java +++ b/src/main/java/liaison/linkit/profile/service/BrowsePrivateProfileService.java @@ -7,16 +7,18 @@ import liaison.linkit.profile.domain.Profile; import liaison.linkit.profile.domain.miniProfile.MiniProfile; import liaison.linkit.profile.domain.repository.antecedents.AntecedentsRepository; -import liaison.linkit.profile.domain.repository.education.DegreeRepository; -import liaison.linkit.profile.domain.repository.education.EducationRepository; -import liaison.linkit.profile.domain.repository.miniProfile.MiniProfileRepository; import liaison.linkit.profile.domain.repository.profile.ProfileRepository; import liaison.linkit.profile.domain.repository.skill.ProfileSkillRepository; import liaison.linkit.profile.domain.repository.skill.SkillRepository; +import liaison.linkit.profile.domain.repository.attach.AttachUrlRepository; +import liaison.linkit.profile.domain.repository.education.DegreeRepository; +import liaison.linkit.profile.domain.repository.education.EducationRepository; +import liaison.linkit.profile.domain.repository.miniProfile.MiniProfileRepository; import liaison.linkit.profile.domain.repository.teambuilding.ProfileTeamBuildingFieldRepository; import liaison.linkit.profile.domain.repository.teambuilding.TeamBuildingFieldRepository; import liaison.linkit.profile.dto.response.ProfileIntroductionResponse; import liaison.linkit.profile.dto.response.antecedents.AntecedentsResponse; +import liaison.linkit.profile.dto.response.attach.AttachResponse; import liaison.linkit.profile.dto.response.awards.AwardsResponse; import liaison.linkit.profile.dto.response.browse.BrowsePrivateProfileResponse; import liaison.linkit.profile.dto.response.completion.CompletionResponse; @@ -54,6 +56,7 @@ public class BrowsePrivateProfileService { private final AntecedentsRepository antecedentsRepository; private final EducationRepository educationRepository; private final DegreeRepository degreeRepository; + private final AttachUrlRepository attachUrlRepository; // 회원 조회 private Member getMember(final Long memberId) { @@ -108,7 +111,8 @@ public BrowsePrivateProfileResponse getProfileResponse( final ProfileRegionResponse profileRegionResponse, final List antecedentsResponses, final List educationResponses, - final List awardsResponses + final List awardsResponses, + final AttachResponse attachResponse ) { return BrowsePrivateProfileResponse.privateProfile( profileId, @@ -120,7 +124,8 @@ public BrowsePrivateProfileResponse getProfileResponse( profileRegionResponse, antecedentsResponses, educationResponses, - awardsResponses + awardsResponses, + attachResponse ); } diff --git a/src/main/java/liaison/linkit/profile/service/ProfileService.java b/src/main/java/liaison/linkit/profile/service/ProfileService.java index 1ff2389b..463afbad 100644 --- a/src/main/java/liaison/linkit/profile/service/ProfileService.java +++ b/src/main/java/liaison/linkit/profile/service/ProfileService.java @@ -6,18 +6,20 @@ import liaison.linkit.profile.domain.Profile; import liaison.linkit.profile.domain.miniProfile.MiniProfile; import liaison.linkit.profile.domain.repository.antecedents.AntecedentsRepository; -import liaison.linkit.profile.domain.repository.education.DegreeRepository; -import liaison.linkit.profile.domain.repository.education.EducationRepository; -import liaison.linkit.profile.domain.repository.miniProfile.MiniProfileRepository; import liaison.linkit.profile.domain.repository.profile.ProfileRepository; import liaison.linkit.profile.domain.repository.skill.ProfileSkillRepository; import liaison.linkit.profile.domain.repository.skill.SkillRepository; +import liaison.linkit.profile.domain.repository.attach.AttachUrlRepository; +import liaison.linkit.profile.domain.repository.education.DegreeRepository; +import liaison.linkit.profile.domain.repository.education.EducationRepository; +import liaison.linkit.profile.domain.repository.miniProfile.MiniProfileRepository; import liaison.linkit.profile.domain.repository.teambuilding.ProfileTeamBuildingFieldRepository; import liaison.linkit.profile.domain.repository.teambuilding.TeamBuildingFieldRepository; import liaison.linkit.profile.dto.request.IntroductionRequest; import liaison.linkit.profile.dto.response.ProfileIntroductionResponse; import liaison.linkit.profile.dto.response.ProfileResponse; import liaison.linkit.profile.dto.response.antecedents.AntecedentsResponse; +import liaison.linkit.profile.dto.response.attach.AttachResponse; import liaison.linkit.profile.dto.response.awards.AwardsResponse; import liaison.linkit.profile.dto.response.completion.CompletionResponse; import liaison.linkit.profile.dto.response.education.EducationResponse; @@ -42,14 +44,26 @@ public class ProfileService { private final ProfileRepository profileRepository; // 미니 프로필 정보 담당 private final MiniProfileRepository miniProfileRepository; + // 보유 기술 정보 담당 private final ProfileSkillRepository profileSkillRepository; private final SkillRepository skillRepository; + // 희망 팀빌딩 분야 정보 담당 private final ProfileTeamBuildingFieldRepository profileTeamBuildingFieldRepository; private final TeamBuildingFieldRepository teamBuildingFieldRepository; + + // 이력 정보 담당 private final AntecedentsRepository antecedentsRepository; + + // 학력 정보 담당 private final EducationRepository educationRepository; private final DegreeRepository degreeRepository; + // 첨부 링크 정보 담당 + private final AttachUrlRepository attachUrlRepository; + + // 첨부 이미지(파일 경로) 정보 담당 +// private final AttachFileRepository attachFileRepository; + // 프로필 (내 이력서) 1개 조회 private Profile getProfileByMember(final Long memberId) { return profileRepository.findByMemberId(memberId) @@ -108,7 +122,8 @@ public ProfileResponse getProfileResponse( final ProfileRegionResponse profileRegionResponse, final List antecedentsResponses, final List educationResponses, - final List awardsResponses + final List awardsResponses, + final AttachResponse attachResponse ) { return ProfileResponse.profileItems( isPrivateProfileEssential, @@ -120,7 +135,8 @@ public ProfileResponse getProfileResponse( profileRegionResponse, antecedentsResponses, educationResponses, - awardsResponses + awardsResponses, + attachResponse ); } diff --git a/src/test/java/liaison/linkit/profile/presentation/AttachControllerTest.java b/src/test/java/liaison/linkit/profile/presentation/AttachControllerTest.java new file mode 100644 index 00000000..7b5d613e --- /dev/null +++ b/src/test/java/liaison/linkit/profile/presentation/AttachControllerTest.java @@ -0,0 +1,268 @@ +package liaison.linkit.profile.presentation; + +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.servlet.http.Cookie; +import liaison.linkit.global.ControllerTest; +import liaison.linkit.login.domain.MemberTokens; +import liaison.linkit.profile.dto.request.attach.AttachFileCreateRequest; +import liaison.linkit.profile.dto.request.attach.AttachUrlCreateRequest; +import liaison.linkit.profile.service.AttachService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.data.jpa.mapping.JpaMetamodelMappingContext; +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; +import org.springframework.restdocs.payload.JsonFieldType; +import org.springframework.test.web.servlet.ResultActions; + +import java.util.Arrays; +import java.util.List; + +import static liaison.linkit.global.restdocs.RestDocsConfiguration.field; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.verify; +import static org.springframework.http.HttpHeaders.AUTHORIZATION; +import static org.springframework.http.MediaType.APPLICATION_JSON; +import static org.springframework.restdocs.cookies.CookieDocumentation.cookieWithName; +import static org.springframework.restdocs.cookies.CookieDocumentation.requestCookies; +import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(AttachController.class) +@MockBean(JpaMetamodelMappingContext.class) +@AutoConfigureRestDocs +public class AttachControllerTest extends ControllerTest { + + private static final MemberTokens MEMBER_TOKENS = new MemberTokens("refreshToken", "accessToken"); + private static final Cookie COOKIE = new Cookie("refresh-token", MEMBER_TOKENS.getRefreshToken()); + + @Autowired + private ObjectMapper objectMapper; + + @MockBean + private AttachService attachService; + + @BeforeEach + void setUp() { + given(refreshTokenRepository.existsById(any())).willReturn(true); + doNothing().when(jwtProvider).validateTokens(any()); + given(jwtProvider.getSubject(any())).willReturn("1"); + doNothing().when(attachService).validateAttachUrlByMember(anyLong()); + } + + private void makeAttachUrl() throws Exception { + final AttachUrlCreateRequest attachUrlCreateRequest1 = new AttachUrlCreateRequest( + "깃허브", + "https://github.com/TEAM-LIAISON" + ); + + final AttachUrlCreateRequest attachUrlCreateRequest2 = new AttachUrlCreateRequest( + "노션", + "https://www.notion.no" + ); + + final List attachUrlCreateRequestList = Arrays.asList(attachUrlCreateRequest1, attachUrlCreateRequest2); + + doNothing().when(attachService).saveUrl(1L, attachUrlCreateRequestList); + performPostUrlRequest(attachUrlCreateRequestList); + } + + private ResultActions performPostUrlRequest(final List attachUrlCreateRequests) throws Exception { + return mockMvc.perform( + post("/private/attach/url") + .header(AUTHORIZATION, MEMBER_TOKENS.getAccessToken()) + .cookie(COOKIE) + .contentType(APPLICATION_JSON) + .content(objectMapper.writeValueAsString(attachUrlCreateRequests)) + ); + } + +// private ResultActions performPutUrlRequest( +// final int attachUrlId, +// final AttachUrlUpdateRequest attachUrlUpdateRequest +// ) throws Exception { +// return mockMvc.perform( +// RestDocumentationRequestBuilders.put("/attach/url/{attachUrlId}", attachUrlId) +// .header(AUTHORIZATION, MEMBER_TOKENS.getAccessToken()) +// .cookie(COOKIE) +// .contentType(APPLICATION_JSON) +// .content(objectMapper.writeValueAsString(attachUrlUpdateRequest)) +// ); +// } + + private ResultActions performDeleteUrlRequest( + final int attachUrlId + ) throws Exception { + return mockMvc.perform( + RestDocumentationRequestBuilders.delete("/private/attach/url/{attachUrlId}", attachUrlId) + .header(AUTHORIZATION, MEMBER_TOKENS.getAccessToken()) + .cookie(COOKIE) + .contentType(APPLICATION_JSON) + ); + } + + private ResultActions performPostFileRequest(final AttachFileCreateRequest attachFileCreateRequest) throws Exception { + return mockMvc.perform( + post("/attach/file") + .header(AUTHORIZATION, MEMBER_TOKENS.getAccessToken()) + .cookie(COOKIE) + .contentType(APPLICATION_JSON) + .content(objectMapper.writeValueAsString(attachFileCreateRequest)) + ); + } + + @DisplayName("첨부(웹 링크) 리스트를 생성할 수 있다.") + @Test + void createAttachUrl() throws Exception { + // given + final AttachUrlCreateRequest attachUrlCreateRequest1 = new AttachUrlCreateRequest( + "깃허브", + "https://github.com/TEAM-LIAISON" + ); + + final AttachUrlCreateRequest attachUrlCreateRequest2 = new AttachUrlCreateRequest( + "노션", + "https://www.notion.no" + ); + + final List attachUrlCreateRequestList = Arrays.asList(attachUrlCreateRequest1, attachUrlCreateRequest2); + +// doNothing().when(attachService).saveUrl(anyLong(), attachUrlCreateRequestList); + // when + final ResultActions resultActions = performPostUrlRequest(attachUrlCreateRequestList); + + // then + resultActions.andExpect(status().isCreated()) + .andDo( + restDocs.document( + requestCookies( + cookieWithName("refresh-token") + .description("갱신 토큰") + ), + requestHeaders( + headerWithName("Authorization") + .description("access token") + .attributes(field("constraint", "문자열(jwt)")) + ), + requestFields( + fieldWithPath("[].attachUrlName") + .type(JsonFieldType.STRING) + .description("웹 링크 이름") + .attributes(field("constraint", "문자열")), + fieldWithPath("[].attachUrlPath") + .type(JsonFieldType.STRING) + .description("웹 링크 경로") + .attributes(field("constraint", "문자열")) + ) + ) + ); + } + +// @DisplayName("단일 첨부(웹 링크)를 수정할 수 있다.") +// @Test +// void updateAttachUrl() throws Exception { +// // given +// final AttachUrlUpdateRequest attachUrlUpdateRequest = new AttachUrlUpdateRequest( +// "웹페이지", +// "https://knowhow.ceo/" +// ); +// +// // when +// final ResultActions resultActions = performPutUrlRequest(1, attachUrlUpdateRequest); +// +// // then +// resultActions.andExpect(status().isNoContent()) +// .andDo( +// restDocs.document( +// pathParameters( +// parameterWithName("attachUrlId") +// .description("첨부 URL ID") +// ), +// requestFields( +// fieldWithPath("attachUrlName") +// .type(JsonFieldType.STRING) +// .description("웹 링크 이름") +// .attributes(field("constraint", "문자열")), +// fieldWithPath("attachUrlPath") +// .type(JsonFieldType.STRING) +// .description("웹 링크") +// .attributes(field("constraint", "문자열")) +// ) +// ) +// ); +// } + + @DisplayName("단일 첨부(웹 링크)를 삭제할 수 있다.") + @Test + void deleteAttachUrl() throws Exception { + // given + makeAttachUrl(); + doNothing().when(attachService).validateAttachUrlByMember(anyLong()); + + // when + final ResultActions resultActions = performDeleteUrlRequest(1); + + // then + verify(attachService).deleteUrl(1L, 1L); + + resultActions.andExpect(status().isNoContent()) + .andDo(restDocs.document( + pathParameters( + parameterWithName("attachUrlId") + .description("첨부 URL ID") + ) + )); + } + +// @DisplayName("첨부(웹 파일)를 생성할 수 있다.") +// @Test +// void createAttachFile() throws Exception { +// // given +// final MockMultipartFile attachFile = new MockMultipartFile( +// "attachFile", +// "poster.pdf", +// "multipart/form-data", +// "PDF file content".getBytes() +// ); +// +// MockMultipartHttpServletRequestBuilder customRestDocumentationRequestBuilder = +// RestDocumentationRequestBuilders.multipart("/attach/file", attachFile); +// // when +// +// final ResultActions resultActions = mockMvc.perform(multipart(HttpMethod.POST, "/attach/file") +// .file(attachFile) +// .accept(APPLICATION_JSON) +// .contentType(MediaType.MULTIPART_FORM_DATA) +// .characterEncoding("UTF-8") +// .header(AUTHORIZATION, MEMBER_TOKENS.getAccessToken()) +// .cookie(COOKIE) +// ); +// +// // then +// resultActions.andExpect(status().isCreated()) +// .andDo( +// restDocs.document( +// requestHeaders( +// headerWithName("Authorization").description("인증 토큰").attributes(field("constraint", "문자열(jwt)")) +// ), +// requestParts( +// partWithName("attachFile").description("첨부 파일. 지원되는 형식은 .pdf, .docx 등이 있습니다.") +// ) +// ) +// ); +// } + +} diff --git a/src/test/java/liaison/linkit/profile/presentation/BrowsePrivateProfileControllerTest.java b/src/test/java/liaison/linkit/profile/presentation/BrowsePrivateProfileControllerTest.java index 80449a07..ee1315e9 100644 --- a/src/test/java/liaison/linkit/profile/presentation/BrowsePrivateProfileControllerTest.java +++ b/src/test/java/liaison/linkit/profile/presentation/BrowsePrivateProfileControllerTest.java @@ -6,6 +6,8 @@ import liaison.linkit.login.domain.MemberTokens; import liaison.linkit.profile.dto.response.ProfileIntroductionResponse; import liaison.linkit.profile.dto.response.antecedents.AntecedentsResponse; +import liaison.linkit.profile.dto.response.attach.AttachResponse; +import liaison.linkit.profile.dto.response.attach.AttachUrlResponse; import liaison.linkit.profile.dto.response.awards.AwardsResponse; import liaison.linkit.profile.dto.response.browse.BrowsePrivateProfileResponse; import liaison.linkit.profile.dto.response.completion.CompletionResponse; @@ -68,6 +70,8 @@ public class BrowsePrivateProfileControllerTest extends ControllerTest { @MockBean public AwardsService awardsService; @MockBean + public AttachService attachService; + @MockBean public ProfileRegionService profileRegionService; @MockBean public BrowsePrivateProfileService browsePrivateProfileService; @@ -163,8 +167,16 @@ void getBrowsePrivateProfile() throws Exception { final List awardsResponses = Arrays.asList(firstAwardsResponse, secondAwardsResponse); given(awardsService.getAllAwards(1L)).willReturn(awardsResponses); + final AttachResponse attachResponse = new AttachResponse( + Arrays.asList( + new AttachUrlResponse(2L, "깃허브", "https://github.com/TEAM-LIAISON"), + new AttachUrlResponse(3L, "노션", "https://www.notion.so/ko-kr") + ) + ); + given(attachService.getAttachList(1L)).willReturn(attachResponse); + when(browsePrivateProfileService.getProfileResponse( - any(), any(), any(), any(), any(), any(), any(), any(), any(), any() + any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any() )).thenReturn(BrowsePrivateProfileResponse.privateProfile( 1L, miniProfileResponse, @@ -175,9 +187,9 @@ void getBrowsePrivateProfile() throws Exception { profileRegionResponse, antecedentsResponses, educationResponses, - awardsResponses + awardsResponses, + attachResponse )); - // when final ResultActions resultActions = performGetBrowsePrivateProfile(miniProfileId); @@ -256,7 +268,15 @@ void getBrowsePrivateProfile() throws Exception { fieldWithPath("awardsResponse[].organizer").type(JsonFieldType.STRING).description("주최자"), fieldWithPath("awardsResponse[].awardsYear").type(JsonFieldType.NUMBER).description("수상 연도"), fieldWithPath("awardsResponse[].awardsMonth").type(JsonFieldType.NUMBER).description("수상 월"), - fieldWithPath("awardsResponse[].awardsDescription").type(JsonFieldType.STRING).description("수상 내용") - ))); + fieldWithPath("awardsResponse[].awardsDescription").type(JsonFieldType.STRING).description("수상 내용"), + + // attachResponse + subsectionWithPath("attachResponse").description("첨부 파일 정보"), + fieldWithPath("attachResponse.attachUrlResponseList[].id").type(JsonFieldType.NUMBER).description("첨부 URL ID"), + fieldWithPath("attachResponse.attachUrlResponseList[].attachUrlName").type(JsonFieldType.STRING).description("첨부된 URL 이름"), + fieldWithPath("attachResponse.attachUrlResponseList[].attachUrlPath").type(JsonFieldType.STRING).description("첨부된 URL") +// fieldWithPath("attachResponse.attachFileResponseList[].id").type(JsonFieldType.NUMBER).description("첨부 파일 ID"), +// fieldWithPath("attachResponse.attachFileResponseList[].attachFilePath").type(JsonFieldType.STRING).description("첨부 파일 URL") + ))); } } diff --git a/src/test/java/liaison/linkit/profile/presentation/ProfileControllerTest.java b/src/test/java/liaison/linkit/profile/presentation/ProfileControllerTest.java index 643dc982..86ae2481 100644 --- a/src/test/java/liaison/linkit/profile/presentation/ProfileControllerTest.java +++ b/src/test/java/liaison/linkit/profile/presentation/ProfileControllerTest.java @@ -10,6 +10,8 @@ import liaison.linkit.profile.dto.response.ProfileIntroductionResponse; import liaison.linkit.profile.dto.response.ProfileResponse; import liaison.linkit.profile.dto.response.antecedents.AntecedentsResponse; +import liaison.linkit.profile.dto.response.attach.AttachResponse; +import liaison.linkit.profile.dto.response.attach.AttachUrlResponse; import liaison.linkit.profile.dto.response.awards.AwardsResponse; import liaison.linkit.profile.dto.response.completion.CompletionResponse; import liaison.linkit.profile.dto.response.education.EducationResponse; @@ -79,6 +81,8 @@ class ProfileControllerTest extends ControllerTest { @MockBean private AwardsService awardsService; @MockBean + private AttachService attachService; + @MockBean private ProfileRegionService profileRegionService; @BeforeEach @@ -292,6 +296,34 @@ void getProfile() throws Exception { final List awardsResponses = Arrays.asList(firstAwardsResponse, secondAwardsResponse); given(awardsService.getAllAwards(1L)).willReturn(awardsResponses); + // 10. 첨부 + final AttachUrlResponse firstAttachUrlResponse = new AttachUrlResponse( + 1L, + "깃허브", + "https://github.com/TEAM-LIAISON" + ); + + final AttachUrlResponse secondAttachUrlResponse = new AttachUrlResponse( + 2L, + "노션", + "https://www.notion.so/ko-kr" + ); + +// final AttachFileResponse firstAttachFileResponse = new AttachFileResponse( +// 1L, +// "A4+-=1.pdf", +// "https://linkit-dev-env-bucket.s3.ap-northeast-1.amazonaws.com/files/A4+-+1.pdf" +// ); + + final List attachUrlResponseList = Arrays.asList(firstAttachUrlResponse, secondAttachUrlResponse); +// final List attachFileResponseList = Arrays.asList(firstAttachFileResponse); + final AttachResponse attachResponses = new AttachResponse( + attachUrlResponseList +// attachFileResponseList + ); + + given(attachService.getAttachList(1L)).willReturn(attachResponses); + final ProfileResponse profileResponse = new ProfileResponse( isPrivateProfileEssential, miniProfileResponse, @@ -302,7 +334,8 @@ void getProfile() throws Exception { profileRegionResponse, antecedentsResponses, educationResponses, - awardsResponses + awardsResponses, + attachResponses ); given(profileService.getProfileResponse( @@ -315,7 +348,8 @@ void getProfile() throws Exception { profileRegionResponse, antecedentsResponses, educationResponses, - awardsResponses + awardsResponses, + attachResponses )).willReturn(profileResponse); // when @@ -401,10 +435,17 @@ void getProfile() throws Exception { fieldWithPath("awardsResponse[].organizer").type(JsonFieldType.STRING).description("주최자"), fieldWithPath("awardsResponse[].awardsYear").type(JsonFieldType.NUMBER).description("수상 연도"), fieldWithPath("awardsResponse[].awardsMonth").type(JsonFieldType.NUMBER).description("수상 월"), - fieldWithPath("awardsResponse[].awardsDescription").type(JsonFieldType.STRING).description("수상 내용") + fieldWithPath("awardsResponse[].awardsDescription").type(JsonFieldType.STRING).description("수상 내용"), + + // attachResponse + subsectionWithPath("attachResponse").description("첨부 파일 정보"), + fieldWithPath("attachResponse.attachUrlResponseList[].id").type(JsonFieldType.NUMBER).description("첨부 URL ID"), + fieldWithPath("attachResponse.attachUrlResponseList[].attachUrlName").type(JsonFieldType.STRING).description("첨부된 URL 이름"), + fieldWithPath("attachResponse.attachUrlResponseList[].attachUrlPath").type(JsonFieldType.STRING).description("첨부된 URL") +// fieldWithPath("attachResponse.attachFileResponseList[].id").type(JsonFieldType.NUMBER).description("첨부 파일 ID"), +// fieldWithPath("attachResponse.attachFileResponseList[].attachFilePath").type(JsonFieldType.STRING).description("첨부 파일 URL") ) )); - } @DisplayName("프로필 자기소개 항목을 생성할 수 있다.")