-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: 마이프로필 및 리크루트 데이터포맷 수정 #248
Changes from all commits
5d720fa
cd83093
0a18a07
119c74b
04c0233
98e776a
209822d
581f7ad
0ba7abd
3d593ca
e76c221
de7e250
02a634a
7c0cc82
ed520f2
0a66929
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,78 +1,139 @@ | ||
package com.ssafy.ssafsound.domain.recruit.repository; | ||
|
||
import com.querydsl.core.types.dsl.BooleanExpression; | ||
import com.querydsl.jpa.JPAExpressions; | ||
import com.querydsl.jpa.JPQLQuery; | ||
import com.querydsl.jpa.impl.JPAQuery; | ||
import com.querydsl.jpa.impl.JPAQueryFactory; | ||
import com.ssafy.ssafsound.domain.meta.domain.MetaData; | ||
import com.ssafy.ssafsound.domain.meta.domain.MetaDataType; | ||
import com.ssafy.ssafsound.domain.meta.service.MetaDataConsumer; | ||
import com.ssafy.ssafsound.domain.recruit.domain.Category; | ||
import com.ssafy.ssafsound.domain.recruit.domain.Recruit; | ||
import com.ssafy.ssafsound.domain.recruit.dto.GetRecruitsReqDto; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import com.ssafy.ssafsound.domain.recruitapplication.domain.MatchStatus; | ||
import lombok.RequiredArgsConstructor; | ||
|
||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.data.domain.Pageable; | ||
import org.springframework.data.domain.Slice; | ||
import org.springframework.data.domain.SliceImpl; | ||
import org.springframework.stereotype.Repository; | ||
import org.springframework.util.StringUtils; | ||
|
||
import javax.persistence.EntityManager; | ||
import javax.persistence.PersistenceContext; | ||
import javax.persistence.TypedQuery; | ||
import javax.persistence.criteria.CriteriaBuilder; | ||
import javax.persistence.criteria.CriteriaQuery; | ||
import javax.persistence.criteria.Predicate; | ||
import javax.persistence.criteria.Root; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
|
||
import static com.ssafy.ssafsound.domain.recruit.domain.QRecruit.recruit; | ||
import static com.ssafy.ssafsound.domain.recruit.domain.QRecruitSkill.recruitSkill; | ||
import static com.ssafy.ssafsound.domain.recruit.domain.QRecruitLimitation.recruitLimitation; | ||
import static com.ssafy.ssafsound.domain.recruit.domain.QRecruitScrap.recruitScrap; | ||
import static com.ssafy.ssafsound.domain.recruitapplication.domain.QRecruitApplication.recruitApplication; | ||
import static com.ssafy.ssafsound.domain.member.domain.QMember.member; | ||
|
||
|
||
@Repository | ||
@RequiredArgsConstructor | ||
@Slf4j | ||
public class RecruitDynamicQueryRepositoryImpl implements RecruitDynamicQueryRepository { | ||
|
||
@Autowired | ||
MetaDataConsumer metaDataConsumer; | ||
private final MetaDataConsumer metaDataConsumer; | ||
|
||
@PersistenceContext | ||
private EntityManager entityManager; | ||
private final JPAQueryFactory jpaQueryFactory; | ||
|
||
@Override | ||
public Slice<Recruit> findRecruitByGetRecruitsReqDto(GetRecruitsReqDto dto, Pageable pageable) { | ||
CriteriaBuilder cb = entityManager.getCriteriaBuilder(); | ||
CriteriaQuery<Recruit> cq = cb.createQuery(Recruit.class); | ||
|
||
Root<Recruit> root = cq.from(Recruit.class); | ||
List<Predicate> predicates = new ArrayList<>(); | ||
|
||
/* 정적 검색 조건 처리 -> 카테고리 */ | ||
Predicate category = cb.equal(root.get("category"), Category.valueOf(dto.getCategory().toUpperCase())); | ||
predicates.add(category); | ||
/* 동적 검색 조건 처리 -> 커서, 검색 키워드, 모집파트, 모집중 여부 */ | ||
if(dto.getCursor() != null) { | ||
Predicate cursorInfo = cb.lessThan(root.get("id"), dto.getCursor()); | ||
predicates.add(cursorInfo); | ||
} | ||
// cursor base pagination (value -1 or null ignore search condition) | ||
Long cursor = dto.getCursor(); | ||
|
||
if(StringUtils.hasText(dto.getKeyword())) { | ||
Predicate titleContainKeyword = cb.like(root.get("title"), "%"+dto.getKeyword()+"%"); | ||
predicates.add(titleContainKeyword); | ||
} | ||
// recruit category (STUDY | PROJECT) | ||
BooleanExpression categoryEq = recruit.category.eq(Category.valueOf(dto.getCategory().toUpperCase())); | ||
|
||
// recruit title contains search keyword | ||
String keyword = dto.getKeyword(); | ||
BooleanExpression titleEq = StringUtils.hasText(keyword) ? recruit.title.contains(keyword) : null; | ||
|
||
JPAQuery<Recruit> recruitDynamicQuery = jpaQueryFactory.selectFrom(recruit) | ||
.where(recruitIdLtThanCursor(cursor), categoryEq, titleEq); | ||
|
||
// recruit skill | ||
List<String> skills = dto.getSkills(); | ||
if(skills!=null && !skills.isEmpty()) { | ||
predicates.add(root.get("skills").in(skills)); | ||
if(skills!=null && skills.size() > 0) { | ||
String metaDataType = MetaDataType.SKILL.name(); | ||
List<MetaData> containSkills = skills.stream() | ||
.map(skillName->metaDataConsumer.getMetaData(metaDataType, skillName)) | ||
.collect(Collectors.toList()); | ||
|
||
JPQLQuery<Long> recruitSkillContainRecruitIds = JPAExpressions | ||
.select(recruitSkill.recruit.id) | ||
.from(recruitSkill) | ||
.innerJoin(recruitSkill.recruit, recruit) | ||
.where(recruitSkill.skill.in(containSkills)); | ||
|
||
recruitDynamicQuery.where(recruit.id.in(recruitSkillContainRecruitIds)); | ||
} | ||
|
||
// recruit types limitation | ||
List<String> recruitTypes = dto.getRecruitTypes(); | ||
if(recruitTypes!=null && !recruitTypes.isEmpty()) { | ||
List<MetaData> limitTypes = recruitTypes.stream().map( | ||
type->metaDataConsumer.getMetaData(MetaDataType.RECRUIT_TYPE.name(), type) | ||
).collect(Collectors.toList()); | ||
predicates.add(root.get("limitations").get("type").in(limitTypes)); | ||
if(dto.getCategory().toUpperCase().equals(Category.PROJECT.name()) && recruitTypes!=null && !recruitTypes.isEmpty()) { | ||
String metaDataType = MetaDataType.RECRUIT_TYPE.name(); | ||
List<MetaData> containRecruitTypes = recruitTypes.stream() | ||
.map(recruitType->metaDataConsumer.getMetaData(metaDataType, recruitType)) | ||
.collect(Collectors.toList()); | ||
|
||
JPQLQuery<Long> limitationContainRecruitIds = JPAExpressions | ||
.select(recruitLimitation.recruit.id) | ||
.from(recruitLimitation) | ||
.innerJoin(recruitLimitation.recruit, recruit) | ||
.where(recruitLimitation.type.in(containRecruitTypes)); | ||
|
||
recruitDynamicQuery.where(recruit.id.in(limitationContainRecruitIds)); | ||
} | ||
cq.where(predicates.toArray(new Predicate[0])); | ||
cq.orderBy(cb.desc(root.get("id"))); | ||
|
||
TypedQuery<Recruit> query = entityManager.createQuery(cq); | ||
List<Recruit> recruits = query.setMaxResults(pageable.getPageSize()+1) | ||
.getResultList(); | ||
List<Recruit> recruits = recruitDynamicQuery | ||
.limit(pageable.getPageSize()+1) | ||
.orderBy(recruit.id.desc()) | ||
.fetch(); | ||
boolean hasNext = pageable.isPaged() && recruits.size() > pageable.getPageSize(); | ||
return new SliceImpl<>(hasNext ? recruits.subList(0, pageable.getPageSize()) : recruits, pageable, hasNext); | ||
} | ||
|
||
@Override | ||
public Slice<Recruit> findMemberJoinRecruitWithCursorAndPageable(Long memberId, Long cursor, Pageable pageable) { | ||
List<Long> memberJoinRecruitIds = jpaQueryFactory.select(recruitApplication.recruit.id) | ||
.from(recruitApplication) | ||
.innerJoin(recruitApplication.recruit, recruit) | ||
.innerJoin(recruitApplication.member, member) | ||
.where(recruitApplication.member.id.eq(memberId), recruitApplication.matchStatus.eq(MatchStatus.DONE)) | ||
.fetch(); | ||
|
||
List<Recruit> recruits = jpaQueryFactory.selectFrom(recruit) | ||
.innerJoin(recruit.member, member) | ||
.where(recruitIdLtThanCursor(cursor), recruit.id.in(memberJoinRecruitIds), recruit.member.id.eq(memberId)) | ||
.limit(pageable.getPageSize()+1) | ||
.orderBy(recruit.id.desc()) | ||
.fetch(); | ||
|
||
boolean hasNext = pageable.isPaged() && recruits.size() > pageable.getPageSize(); | ||
return new SliceImpl<>(hasNext ? recruits.subList(0, pageable.getPageSize()) : recruits, pageable, hasNext); | ||
} | ||
|
||
@Override | ||
public Slice<Recruit> findMemberScrapRecruits(Long memberId, Long cursor, Pageable pageable) { | ||
List<Recruit> recruits = jpaQueryFactory.select(recruitScrap.recruit) | ||
.from(recruitScrap) | ||
.innerJoin(recruitScrap.recruit, recruit) | ||
.innerJoin(recruitScrap.member, member) | ||
.where(recruitIdLtThanCursor(cursor), recruitScrap.member.id.eq(memberId)) | ||
.limit(pageable.getPageSize()+1) | ||
.orderBy(recruit.id.desc()) | ||
.fetch(); | ||
|
||
boolean hasNext = pageable.isPaged() && recruits.size() > pageable.getPageSize(); | ||
return new SliceImpl<>(hasNext ? recruits.subList(0, pageable.getPageSize()) : recruits, pageable, hasNext); | ||
} | ||
|
||
private BooleanExpression recruitIdLtThanCursor(Long cursor) { | ||
return ((cursor != null) && (cursor != -1)) ? recruit.id.lt(cursor) : null; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,8 @@ | ||
package com.ssafy.ssafsound.domain.recruit.service; | ||
|
||
import com.ssafy.ssafsound.domain.member.domain.Member; | ||
import com.ssafy.ssafsound.domain.member.exception.MemberErrorInfo; | ||
import com.ssafy.ssafsound.domain.member.exception.MemberException; | ||
import com.ssafy.ssafsound.domain.member.repository.MemberRepository; | ||
import com.ssafy.ssafsound.domain.meta.domain.MetaData; | ||
import com.ssafy.ssafsound.domain.meta.domain.MetaDataType; | ||
|
@@ -72,8 +74,10 @@ public GetRecruitDetailResDto getRecruitDetail(Long recruitId, Long memberId) { | |
.orElseThrow(()->new ResourceNotFoundException(GlobalErrorInfo.NOT_FOUND)); | ||
long scrapCount = recruitScrapRepository.countByRecruitId(recruitId); | ||
Boolean scraped = recruitScrapRepository.existsByRecruitIdAndMemberId(recruitId, memberId); | ||
|
||
RecruitApplication recruitApplication = recruitApplicationRepository.findTopByRecruitIdAndMemberIdOrderByIdDesc(recruitId, memberId); | ||
recruit.increaseView(); | ||
return GetRecruitDetailResDto.of(recruit, scrapCount, scraped); | ||
return GetRecruitDetailResDto.of(recruit, scrapCount, scraped, recruitApplication); | ||
} | ||
|
||
@Transactional | ||
|
@@ -123,16 +127,37 @@ public void deleteRecruit(Long recruitId, Long memberId) { | |
} | ||
|
||
@Transactional(readOnly = true) | ||
public GetRecruitsResDto getRecruits(GetRecruitsReqDto getRecruitsReqDto, Pageable pageable) { | ||
public GetRecruitsResDto getRecruits(GetRecruitsReqDto getRecruitsReqDto, Pageable pageable, Long loginMemberId) { | ||
// 페이지네이션 조건에 따라 프로젝트/스터디 글 목록을 조회한다. | ||
Slice<Recruit> recruitPages = recruitRepository.findRecruitByGetRecruitsReqDto(getRecruitsReqDto, pageable); | ||
GetRecruitsResDto recruitsResDto = GetRecruitsResDto.fromPage(recruitPages); | ||
Slice<Recruit> recruitPages; | ||
Long memberId = getRecruitsReqDto.getMemberId(); | ||
|
||
// 사용자 Id 여부로 프로필, 사용자가 참여한 리크루트 목록 조회, 일반 글 목록 조회를 구분한다. | ||
if(memberId != null) { | ||
Member member = memberRepository.findById(memberId).orElseThrow(()->new ResourceNotFoundException(GlobalErrorInfo.NOT_FOUND)); | ||
if(!member.getPublicProfile() && !memberId.equals(loginMemberId)) { | ||
throw new MemberException(MemberErrorInfo.MEMBER_PROFILE_SECRET); | ||
} | ||
|
||
recruitPages = recruitRepository.findMemberJoinRecruitWithCursorAndPageable(memberId, getRecruitsReqDto.getCursor(), pageable); | ||
} else { | ||
recruitPages = recruitRepository.findRecruitByGetRecruitsReqDto(getRecruitsReqDto, pageable); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 마이프로필쪽 api라 아직 확정되진 않았습니다. 다음 pr때 프론트랑 연동하면서 작업방향 정해지면 수정될 수 있습니다. |
||
} | ||
|
||
GetRecruitsResDto recruitsResDto = GetRecruitsResDto.fromPageAndMemberId(recruitPages, memberId); | ||
if(!recruitsResDto.getRecruits().isEmpty()) { | ||
addRecruitParticipants(recruitsResDto); | ||
} | ||
return recruitsResDto; | ||
} | ||
|
||
@Transactional(readOnly = true) | ||
public GetRecruitsResDto getScrapedRecruits(Long memberId, Long cursor, Pageable pageable) { | ||
Slice<Recruit> recruitPages = recruitRepository.findMemberScrapRecruits(memberId, cursor, pageable); | ||
GetRecruitsResDto recruitsResDto = GetRecruitsResDto.fromPageAndMemberId(recruitPages, memberId); | ||
return recruitsResDto; | ||
} | ||
|
||
@Transactional | ||
public void expiredRecruit(Long recruitId, Long memberId) { | ||
Recruit recruit = recruitRepository.findByIdUsingFetchJoinRegister(recruitId) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
package com.ssafy.ssafsound.domain.recruitapplication.domain; | ||
|
||
public enum MatchStatus { | ||
WAITING_REGISTER_APPROVE, DONE, REJECT, CANCEL | ||
PENDING, DONE, REJECT, CANCEL, INITIAL | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
가 낫지 않을까요