diff --git a/build.gradle b/build.gradle index a65a59e5..ecaf6ff8 100644 --- a/build.gradle +++ b/build.gradle @@ -116,6 +116,12 @@ dependencies { implementation("com.blazebit:blaze-persistence-integration-querydsl-expressions-jakarta:1.6.15") implementation("com.blazebit:blaze-persistence-integration-hibernate-6.2:1.6.15") implementation("com.blazebit:blaze-persistence-core-impl-jakarta:1.6.15") + + + //elastic search 지원용 + implementation "org.springframework.data:spring-data-elasticsearch:5.5.3" + implementation 'co.elastic.clients:elasticsearch-java:8.18.5' + } test { diff --git a/src/main/java/org/myteam/server/admin/document/ContentDocument.java b/src/main/java/org/myteam/server/admin/document/ContentDocument.java new file mode 100644 index 00000000..a65825eb --- /dev/null +++ b/src/main/java/org/myteam/server/admin/document/ContentDocument.java @@ -0,0 +1,69 @@ +package org.myteam.server.admin.document; + + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.myteam.server.admin.utill.enums.AdminControlType; +import org.myteam.server.admin.utill.enums.StaticDataType; +import org.springframework.data.annotation.Id; +import org.springframework.data.elasticsearch.annotations.*; + +import java.time.LocalDateTime; +import java.util.Date; +import java.util.UUID; + +@Getter +@NoArgsConstructor +@Setting(replicas = 0) +@Document(indexName = "contentdocument") +public class ContentDocument { + @Id + private String id; + + @Field(type=FieldType.Keyword,index = false) + private String email; + + @Field(type=FieldType.Text) + private String nickName; + + @Field(type=FieldType.Keyword) + private StaticDataType staticDataType; + + @Field(type=FieldType.Keyword) + private AdminControlType adminControlType; + + @Field(type=FieldType.Boolean) + private Boolean isReported; + + @Field(type=FieldType.Long,index = false) + private Long contentId; + + @Field(type = FieldType.Text) + private String title; + + @Field(type = FieldType.Text) + private String content; + + @Field(type=FieldType.Date,format= DateFormat.date_hour_minute_second,index = false) + private LocalDateTime createDate; + + + @Builder + public ContentDocument(String id,String email, String nickName, StaticDataType staticDataType, AdminControlType adminControlType, + Boolean isReported, Long contentId, String title, String content, LocalDateTime createDate) { + this.id=id; + this.email = email; + this.nickName = nickName; + this.staticDataType = staticDataType; + this.adminControlType = adminControlType; + this.isReported=isReported; + this.contentId = contentId; + this.title = title; + this.content = content; + this.createDate = createDate; + } + + +} diff --git a/src/main/java/org/myteam/server/admin/document/ImprovementDocument.java b/src/main/java/org/myteam/server/admin/document/ImprovementDocument.java new file mode 100644 index 00000000..be36f608 --- /dev/null +++ b/src/main/java/org/myteam/server/admin/document/ImprovementDocument.java @@ -0,0 +1,64 @@ +package org.myteam.server.admin.document; + +import jakarta.persistence.*; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.myteam.server.improvement.domain.ImportantStatus; +import org.myteam.server.improvement.domain.ImprovementCount; +import org.myteam.server.improvement.domain.ImprovementStatus; +import org.myteam.server.member.entity.Member; +import org.springframework.data.elasticsearch.annotations.*; + +import java.time.LocalDateTime; +import java.util.UUID; + +import static org.myteam.server.improvement.domain.ImprovementStatus.PENDING; + +@Getter +@NoArgsConstructor +@Setting(replicas = 0) +@Document(indexName = "improvedocument") +public class ImprovementDocument { + + + @Id + @Field(type = FieldType.Long,index = false) + public Long id; + + @Field(type = FieldType.Keyword,index = false) + private UUID memberId; + + @Field(type = FieldType.Text) + private String title; + + @Field(type = FieldType.Text) + private String content; + + @Field(type = FieldType.Text) + private String nickName; + + @Field(type = FieldType.Keyword) + private ImprovementStatus improvementStatus; + + @Field(type = FieldType.Keyword) + private ImportantStatus importantStatus; + + @Field(type=FieldType.Date,format= DateFormat.date_hour_minute_second) + private LocalDateTime createDate; + + + @Builder + public ImprovementDocument(Long id, UUID memberId, String title, String content, String nickName, + ImprovementStatus improvementStatus, ImportantStatus importantStatus, + LocalDateTime createDate) { + this.id = id; + this.memberId = memberId; + this.title = title; + this.content = content; + this.nickName = nickName; + this.improvementStatus = improvementStatus; + this.importantStatus = importantStatus; + this.createDate = createDate; + } +} diff --git a/src/main/java/org/myteam/server/admin/document/InquiryDocument.java b/src/main/java/org/myteam/server/admin/document/InquiryDocument.java new file mode 100644 index 00000000..8ab07a13 --- /dev/null +++ b/src/main/java/org/myteam/server/admin/document/InquiryDocument.java @@ -0,0 +1,55 @@ +package org.myteam.server.admin.document; + +import com.esotericsoftware.kryo.serializers.FieldSerializer; +import jakarta.persistence.*; +import jakarta.validation.constraints.NotNull; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.myteam.server.inquiry.domain.InquiryCount; +import org.myteam.server.member.entity.Member; +import org.springframework.data.elasticsearch.annotations.*; + +import java.time.LocalDateTime; +import java.util.UUID; + +@Getter +@NoArgsConstructor +@Setting(replicas = 0) +@Document(indexName = "inquirydocument") +public class InquiryDocument { + + @Id + @Field(type = FieldType.Long,index = false) + private Long id; + + @Field(type= FieldType.Text) + private String content; + + @Field(type=FieldType.Text) + private String nickName; + + @Field(type=FieldType.Date,format= DateFormat.date_hour_minute_second) + private LocalDateTime createDate; + + @Field(type=FieldType.Text) + private String email; + + @Field(type=FieldType.Boolean) + private Boolean isAdminAnswered; + + @Field(type=FieldType.Boolean) + private Boolean isMember; + + @Builder + public InquiryDocument(Long id, String content, String nickName, + LocalDateTime createDate, String email, Boolean isAdminAnswered, Boolean isMember) { + this.id = id; + this.content = content; + this.nickName = nickName; + this.createDate = createDate; + this.email = email; + this.isAdminAnswered = isAdminAnswered; + this.isMember = isMember; + } +} diff --git a/src/main/java/org/myteam/server/admin/dto/request/ContentRequestDto.java b/src/main/java/org/myteam/server/admin/dto/request/ContentRequestDto.java index 280f8e8f..00dfb958 100644 --- a/src/main/java/org/myteam/server/admin/dto/request/ContentRequestDto.java +++ b/src/main/java/org/myteam/server/admin/dto/request/ContentRequestDto.java @@ -5,13 +5,16 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import org.hibernate.type.descriptor.DateTimeUtils; import org.myteam.server.admin.utill.enums.AdminControlType; import org.myteam.server.admin.utill.enums.StaticDataType; import org.myteam.server.board.domain.BoardSearchType; import org.myteam.server.global.util.date.DateFormatUtil; +import org.springframework.data.elasticsearch.annotations.DateFormat; import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.LocalTime; public record ContentRequestDto() { @Getter @@ -56,6 +59,7 @@ public LocalDateTime provideStartTime() { if (this.startTime == null) { return null; } + LocalDate localDate = LocalDate.parse(startTime, DateFormatUtil.formatByDot); return localDate.atStartOfDay(); } @@ -64,6 +68,7 @@ public LocalDateTime provideEndTime() { if (this.endTime == null) { return null; } + LocalDate localDate = LocalDate.parse(endTime, DateFormatUtil.formatByDot); return localDate.atStartOfDay(); } diff --git a/src/main/java/org/myteam/server/admin/dto/request/ImproveRequestDto.java b/src/main/java/org/myteam/server/admin/dto/request/ImproveRequestDto.java index 6ed88a40..53cfecde 100644 --- a/src/main/java/org/myteam/server/admin/dto/request/ImproveRequestDto.java +++ b/src/main/java/org/myteam/server/admin/dto/request/ImproveRequestDto.java @@ -26,7 +26,6 @@ public final static class RequestImprovementList { private String title; private String content; private String nickName; - private String email; @Schema(example = "2025.06.06") private String startTime; @Schema(example = "2025.06.06") @@ -39,7 +38,7 @@ public final static class RequestImprovementList { @Builder public RequestImprovementList(ImprovementStatus improvementStatus, String title, String content, String nickName, - String startTime, String endTime, String email, int offset + String startTime, String endTime,int offset , ImportantStatus importantStatus) { this.improvementStatus = improvementStatus; this.title = title; @@ -47,7 +46,6 @@ public RequestImprovementList(ImprovementStatus improvementStatus, this.nickName = nickName; this.startTime = startTime; this.endTime = endTime; - this.email = email; this.offset = offset; this.importantStatus = importantStatus; } diff --git a/src/main/java/org/myteam/server/admin/dto/response/ResponseContentDto.java b/src/main/java/org/myteam/server/admin/dto/response/ResponseContentDto.java index 362738fb..e2470f4a 100644 --- a/src/main/java/org/myteam/server/admin/dto/response/ResponseContentDto.java +++ b/src/main/java/org/myteam/server/admin/dto/response/ResponseContentDto.java @@ -37,7 +37,6 @@ public ResponseContentSearch(Long contentId, String nickName, String staticDataT this.reportCount = reportCount; this.reported = reported; } - public void updateCountReported(Long count, String reported) { this.reported = reported; this.reportCount = count; diff --git a/src/main/java/org/myteam/server/admin/repository/AdminImprovementSearchRepo.java b/src/main/java/org/myteam/server/admin/repository/AdminImprovementSearchRepo.java index 24ca9931..7cf5c6a4 100644 --- a/src/main/java/org/myteam/server/admin/repository/AdminImprovementSearchRepo.java +++ b/src/main/java/org/myteam/server/admin/repository/AdminImprovementSearchRepo.java @@ -1,27 +1,42 @@ package org.myteam.server.admin.repository; +import co.elastic.clients.elasticsearch._types.SortOptions; +import co.elastic.clients.elasticsearch._types.SortOrder; +import co.elastic.clients.elasticsearch._types.query_dsl.*; import com.querydsl.core.types.Predicate; import com.querydsl.core.types.Projections; import com.querydsl.core.types.dsl.CaseBuilder; import com.querydsl.jpa.impl.JPAQueryFactory; import lombok.RequiredArgsConstructor; +import org.myteam.server.admin.document.ImprovementDocument; +import org.myteam.server.admin.document.InquiryDocument; +import org.myteam.server.admin.dto.request.InquiryRequestDto; +import org.myteam.server.admin.dto.response.InquiryResponseDto; import org.myteam.server.admin.utill.CreateAdminMemo; import org.myteam.server.admin.utill.enums.DateFormatEnum; import org.myteam.server.admin.utill.enums.StaticDataType; import org.myteam.server.global.util.date.DateFormatUtil; import org.myteam.server.improvement.domain.ImportantStatus; import org.myteam.server.improvement.domain.ImprovementStatus; +import org.myteam.server.improvement.dto.response.ImprovementResponse; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.data.elasticsearch.client.elc.NativeQuery; +import org.springframework.data.elasticsearch.core.ElasticsearchOperations; +import org.springframework.data.elasticsearch.core.SearchHits; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; import static org.myteam.server.admin.dto.request.AdminMemoRequestDto.AdminMemoImprovementRequest; import static org.myteam.server.admin.dto.response.ImprovementResponseDto.*; @@ -79,7 +94,6 @@ public Page getImprovementList(RequestImprovementList reque , contentSearchCond(requestImprovementList.getContent()), titleSearchCond(requestImprovementList.getTitle()), processStatusCond(requestImprovementList.getImprovementStatus()), - searchByEmail(requestImprovementList.getEmail()), searchByImportantStatus(requestImprovementList.getImportantStatus())) .orderBy(improvement.createDate.desc()) .offset(pageable.getOffset()) @@ -97,12 +111,14 @@ public Page getImprovementList(RequestImprovementList reque , contentSearchCond(requestImprovementList.getContent()), titleSearchCond(requestImprovementList.getTitle()), processStatusCond(requestImprovementList.getImprovementStatus()), - searchByEmail(requestImprovementList.getEmail()), searchByImportantStatus(requestImprovementList.getImportantStatus())) .fetchOne()).orElse(0L); return new PageImpl<>(responseImprovementList, pageable, count); } + + + public ResponseImprovementDetail getImprovementDetail(Long contentId) { ResponseImprovementDetail responseImprovementDetail = queryFactory .select( @@ -269,6 +285,8 @@ private Predicate searchByTimeLine(LocalDateTime startTime, LocalDateTime endTim return null; } return improvement.createDate.between(startTime, endTime); - } + + + } diff --git a/src/main/java/org/myteam/server/admin/repository/ContentElasticRepository.java b/src/main/java/org/myteam/server/admin/repository/ContentElasticRepository.java new file mode 100644 index 00000000..93836990 --- /dev/null +++ b/src/main/java/org/myteam/server/admin/repository/ContentElasticRepository.java @@ -0,0 +1,172 @@ +package org.myteam.server.admin.repository; + +import co.elastic.clients.elasticsearch._types.SortOptions; +import co.elastic.clients.elasticsearch._types.SortOrder; +import co.elastic.clients.elasticsearch._types.query_dsl.*; +import com.blazebit.persistence.querydsl.BlazeJPAQueryFactory; +import lombok.RequiredArgsConstructor; +import org.myteam.server.admin.document.ContentDocument; +import org.myteam.server.admin.dto.request.ContentRequestDto; +import org.myteam.server.admin.dto.response.ResponseContentDto; +import org.myteam.server.admin.utill.enums.DateFormatEnum; +import org.myteam.server.admin.utill.enums.StaticDataType; +import org.myteam.server.board.domain.BoardSearchType; +import org.myteam.server.global.util.date.DateFormatUtil; +import org.myteam.server.member.domain.MemberStatus; +import org.myteam.server.member.entity.Member; +import org.myteam.server.member.service.MemberReadService; +import org.myteam.server.report.domain.ReportType; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.elasticsearch.client.elc.NativeQuery; +import org.springframework.data.elasticsearch.core.ElasticsearchOperations; +import org.springframework.data.elasticsearch.core.SearchHits; +import org.springframework.stereotype.Repository; + +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import static org.myteam.server.admin.dto.response.ResponseContentDto.*; +import static org.myteam.server.report.domain.QReport.report; + +@RequiredArgsConstructor +@Repository +public class ContentElasticRepository { + + private final ElasticsearchOperations elasticsearchOperations; + private final MemberReadService memberReadService; + private final BlazeJPAQueryFactory blazeJPAQueryFactory; + + public Page useElasticSearchForUnionQuery(ContentRequestDto.RequestContentData requestContentData){ + List mustQuery=makeMustQuery(requestContentData); + List filterQuery=makeFilterQuery(requestContentData); + PageRequest pageRequest=PageRequest.of(requestContentData.getOffset(),10); + Query query=new BoolQuery.Builder() + .filter(filterQuery) + .must(mustQuery) + .build() + ._toQuery(); + NativeQuery nativeQuery=NativeQuery.builder() + .withQuery(query) + .withSort(List.of(SortOptions.of(s->s.field(f->f.field("createDate") + .order(SortOrder.Desc))))) + .withPageable(pageRequest) + .build(); + + + SearchHits datas= elasticsearchOperations.search(nativeQuery, ContentDocument.class); + List responseContentSearches=datas.stream().map(x->{ + return mappingDocToDto(x.getContent()); + }).collect(Collectors.toList()); + + DateFormatUtil.makeElasticTimeByFormatter(responseContentSearches, DateFormatEnum.formatByDotReq); + return new PageImpl<>(responseContentSearches,pageRequest,datas.getTotalHits()); + } + + + + private List makeFilterQuery(ContentRequestDto.RequestContentData requestContentData){ + List filterQuery=new ArrayList<>(); + if(requestContentData.getAdminControlType()!=null){ + TermQuery termQuery=TermQuery.of(t->t.field("adminControlType") + .value(requestContentData.getAdminControlType().name())); + filterQuery.add(termQuery._toQuery()); + } + if(requestContentData.provideEndTime()!=null && requestContentData.provideEndTime()!=null){ + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"); + RangeQuery rangeQuery=new RangeQuery.Builder() + .date(v->v.field("createDate") + .gte(requestContentData.provideStartTime().format(formatter)) + .lte(requestContentData.provideEndTime().format(formatter))) + .build(); + filterQuery.add(rangeQuery._toQuery()); + } + if(requestContentData.getIsReported()!=null){ + TermQuery termQuery=TermQuery.of(t->t.field("isReported") + .value(requestContentData.getIsReported())); + filterQuery.add(termQuery._toQuery()); + } + if(requestContentData.getStaticDataType()!=null){ + TermQuery termQuery=TermQuery.of(t->t.field("staticDataType") + .value(requestContentData.getStaticDataType().name())); + filterQuery.add(termQuery._toQuery()); + } + + return filterQuery; + } + private List makeMustQuery(ContentRequestDto.RequestContentData requestContentData){ + List mustQuery=new ArrayList<>(); + BoardSearchType boardSearchType=requestContentData.getBoardSearchType(); + String searchKeyWord=requestContentData.getSearchKeyWord(); + if(boardSearchType!=null){ + if(boardSearchType==BoardSearchType.CONTENT||boardSearchType==BoardSearchType.COMMENT){ + MatchQuery matchQuery=MatchQuery.of(m-> + m.field("content") + .query(searchKeyWord)); + mustQuery.add(matchQuery._toQuery()); + } + if(boardSearchType==BoardSearchType.TITLE_CONTENT){ + MultiMatchQuery multiMatchQuery=MultiMatchQuery.of( + m->m.fields(List.of("content","title")) + .query(searchKeyWord) + ); + mustQuery.add(multiMatchQuery._toQuery()); + } + if(boardSearchType==BoardSearchType.TITLE){ + MatchQuery matchQuery=MatchQuery.of(m-> + m.field("title") + .query(searchKeyWord)); + mustQuery.add(matchQuery._toQuery()); + } + if(requestContentData.getBoardSearchType().equals(BoardSearchType.NICKNAME)){ + MatchQuery matchQuery=MatchQuery.of(m->m.field("nickName") + .query(requestContentData.getSearchKeyWord()) + .fuzziness("AUTO")); + mustQuery.add(matchQuery._toQuery()); + } + } + return mustQuery; + } + + + private ResponseContentSearch mappingDocToDto(ContentDocument contentDocument){ + Member member1=memberReadService.findByEmail(contentDocument.getEmail()); + String status=member1.getStatus().equals(MemberStatus.ACTIVE) ? "정상" : + member1.getStatus().equals(MemberStatus.WARNED) ? "경고" : + member1.getStatus().equals(MemberStatus.PENDING) ? "대기중":"정지"; + String reported=contentDocument.getIsReported() ? "신고" :"미신고"; + + String staticDataType=contentDocument.getStaticDataType().equals(StaticDataType.BOARD) ? "게시글": + contentDocument.getStaticDataType().equals(StaticDataType.COMMENT) ? "댓글" + : "채팅"; + Long reportNum=0L; + if(contentDocument.getIsReported()!=null&&contentDocument.getIsReported()) { + if (contentDocument.getStaticDataType().equals(StaticDataType.COMMENT)) { + reportNum = Optional.ofNullable(blazeJPAQueryFactory.select(report.count()) + .from(report) + .where(report.reportedContentId.eq(contentDocument.getContentId()) + .and(report.reportType.eq(ReportType.COMMENT))) + .fetchOne()) + .orElse(0L); + } + if (contentDocument.getStaticDataType().equals(StaticDataType.BOARD)) { + reportNum = Optional.ofNullable(blazeJPAQueryFactory.select(report.count()) + .from(report) + .where(report.reportedContentId.eq(contentDocument.getContentId()) + .and(report.reportType.eq(ReportType.BOARD))) + .fetchOne()) + .orElse(0L); + } + } + ResponseContentSearch responseContentSearch=new ResponseContentSearch( + contentDocument.getContentId(),member1.getNickname(),staticDataType, + contentDocument.getContent(),contentDocument.getCreateDate().toString(),status, + contentDocument.getAdminControlType().name(),reportNum,reported + ); + return responseContentSearch; + } +} diff --git a/src/main/java/org/myteam/server/admin/repository/ContentSearchRepository.java b/src/main/java/org/myteam/server/admin/repository/ContentSearchRepository.java index 67fdad47..bae4bdce 100644 --- a/src/main/java/org/myteam/server/admin/repository/ContentSearchRepository.java +++ b/src/main/java/org/myteam/server/admin/repository/ContentSearchRepository.java @@ -1,5 +1,8 @@ package org.myteam.server.admin.repository; +import co.elastic.clients.elasticsearch._types.SortOptions; +import co.elastic.clients.elasticsearch._types.SortOrder; +import co.elastic.clients.elasticsearch._types.query_dsl.*; import com.blazebit.persistence.querydsl.BlazeJPAQuery; import com.blazebit.persistence.querydsl.BlazeJPAQueryFactory; import com.querydsl.core.types.Predicate; @@ -9,10 +12,15 @@ import com.querydsl.jpa.JPAExpressions; import com.querydsl.jpa.impl.JPAQueryFactory; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.hibernate.type.descriptor.DateTimeUtils; +import org.myteam.server.admin.document.ContentDocument; import org.myteam.server.admin.dto.ctes.QContentIdCte; import org.myteam.server.admin.dto.ctes.QContentTotInfoCte; import org.myteam.server.admin.dto.ctes.QSimpleReportCte; import org.myteam.server.admin.dto.response.CommonResponseDto; +import org.myteam.server.admin.dto.response.ResponseContentDto; +import org.myteam.server.admin.service.ContentSearchService; import org.myteam.server.admin.utill.enums.AdminControlType; import org.myteam.server.admin.utill.CreateAdminMemo; import org.myteam.server.admin.utill.enums.DateFormatEnum; @@ -21,15 +29,24 @@ import org.myteam.server.chat.block.domain.BanReason; import org.myteam.server.global.util.date.DateFormatUtil; import org.myteam.server.member.domain.MemberStatus; +import org.myteam.server.member.entity.Member; +import org.myteam.server.member.service.MemberReadService; +import org.myteam.server.report.domain.Report; import org.myteam.server.report.domain.ReportType; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.data.elasticsearch.client.elc.NativeQuery; +import org.springframework.data.elasticsearch.core.ElasticsearchOperations; +import org.springframework.data.elasticsearch.core.SearchHits; +import org.springframework.security.core.parameters.P; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @@ -48,13 +65,13 @@ @Transactional @RequiredArgsConstructor @Repository +@Slf4j public class ContentSearchRepository { private final JPAQueryFactory queryFactory; private final BlazeJPAQueryFactory blazeJPAQueryFactory; private final CreateAdminMemo createAdminMemo; - public void addAdminMemo(AdminMemoContentRequest adminMemoContentRequest) { createAdminMemo.createContentAdminMemo(adminMemoContentRequest,queryFactory); } @@ -573,30 +590,30 @@ private Predicate searchByTimeLine(LocalDateTime startTime, LocalDateTime endTim } if (startTime != null & endTime == null) { - return board.createDate.after(startTime); + return board.createDate.goe(startTime); } if (startTime == null) { - return board.createDate.before(endTime); + return board.createDate.lt(endTime); } - return board.createDate.between(startTime, endTime); + return board.createDate.goe(startTime).and(board.createDate.lt(endTime)); } if (startTime != null & endTime == null) { - return comment1.createDate.after(startTime); + return comment1.createDate.goe(startTime); } if (endTime != null & startTime == null) { - return comment1.createDate.before(endTime); + return comment1.createDate.lt(endTime); } if (startTime == null & endTime == null) { return null; } - return comment1.createDate.between(startTime, endTime); + return comment1.createDate.goe(startTime).and(comment1.createDate.lt(endTime)); } @@ -721,4 +738,5 @@ private ResponseDetail editDataTimeAndAdminMemo(StaticDataType staticDataType, R return responseDetail; } + } diff --git a/src/main/java/org/myteam/server/admin/repository/ImproveElasticRepository.java b/src/main/java/org/myteam/server/admin/repository/ImproveElasticRepository.java new file mode 100644 index 00000000..36603462 --- /dev/null +++ b/src/main/java/org/myteam/server/admin/repository/ImproveElasticRepository.java @@ -0,0 +1,118 @@ +package org.myteam.server.admin.repository; + +import co.elastic.clients.elasticsearch._types.SortOptions; +import co.elastic.clients.elasticsearch._types.SortOrder; +import co.elastic.clients.elasticsearch._types.query_dsl.*; +import lombok.RequiredArgsConstructor; +import org.myteam.server.admin.document.ImprovementDocument; +import org.myteam.server.admin.dto.request.ImproveRequestDto; +import org.myteam.server.admin.dto.response.ImprovementResponseDto; +import org.myteam.server.admin.utill.enums.DateFormatEnum; +import org.myteam.server.global.util.date.DateFormatUtil; +import org.myteam.server.improvement.domain.ImportantStatus; +import org.myteam.server.improvement.domain.ImprovementStatus; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.elasticsearch.client.elc.NativeQuery; +import org.springframework.data.elasticsearch.core.ElasticsearchOperations; +import org.springframework.data.elasticsearch.core.SearchHits; +import org.springframework.stereotype.Repository; + +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import static org.myteam.server.admin.dto.response.ImprovementResponseDto.*; + +@RequiredArgsConstructor +@Repository +public class ImproveElasticRepository { + + + + private final ElasticsearchOperations elasticsearchOperations; + + public Page getImprovementByElasticSearch(ImproveRequestDto.RequestImprovementList requestImprovementList){ + List must=makeMustQuery(requestImprovementList); + List filter=filterQuery(requestImprovementList); + PageRequest pageRequest=PageRequest.of(requestImprovementList.getOffset(),10); + + Query query=new BoolQuery.Builder() + .filter(filter) + .must(must) + .build() + ._toQuery(); + + NativeQuery nativeQuery=NativeQuery.builder() + .withQuery(query) + .withSort(List.of(SortOptions.of(s->s.field(f->f.field("createDate") + .order(SortOrder.Desc))))) + .withPageable(pageRequest) + .build(); + SearchHits data=elasticsearchOperations.search(nativeQuery, ImprovementDocument.class); + + List improvements=data.stream().map(x->{ + ImprovementDocument doc=x.getContent(); + String importantStatus= doc.getImportantStatus().equals(ImportantStatus.LOW) ? "낮음" + :doc.getImportantStatus().equals(ImportantStatus.HIGH) ? "높음 ":"중간"; + String improveStatus=doc.getImprovementStatus().equals(ImprovementStatus.PENDING) ?"접수전" + :doc.getImprovementStatus().equals(ImprovementStatus.RECEIVED) ? "접수완료": "개선완료"; + return new ResponseImprovement( + doc.getId(), doc.getMemberId(),importantStatus,improveStatus,0 + ,doc.getNickName(),doc.getTitle(), + doc.getContent(),doc.getCreateDate().toString() + ); + }).collect(Collectors.toList()); + DateFormatUtil.makeElasticTimeByFormatter(improvements, DateFormatEnum.formatByDotReq); + return new PageImpl<>(improvements,pageRequest,data.getTotalHits()); + } + + private List filterQuery(ImproveRequestDto.RequestImprovementList requestImprovementList){ + List filter=new ArrayList<>(); + if(requestImprovementList.getImportantStatus()!=null){ + TermQuery termQuery=TermQuery.of(t->t.field("importantStatus") + .value(requestImprovementList.getImportantStatus().name())); + filter.add(termQuery._toQuery()); + } + if(requestImprovementList.getImprovementStatus()!=null){ + TermQuery termQuery=TermQuery.of(t->t.field("improvementStatus") + .value(requestImprovementList.getImprovementStatus().name())); + filter.add(termQuery._toQuery()); + } + if(requestImprovementList.provideEndTime()!=null&&requestImprovementList.provideStartTime()!=null){ + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"); + RangeQuery rangeQuery=RangeQuery.of(r->r.date( + v->v.field("createDate") + .gte(requestImprovementList.provideStartTime().format(formatter)) + .lt(requestImprovementList.provideEndTime().format(formatter)) + )); + filter.add(rangeQuery._toQuery()); + } + return filter; + } + + private List makeMustQuery(ImproveRequestDto.RequestImprovementList requestImprovementList){ + List mustQuery=new ArrayList<>(); + + if(requestImprovementList.getContent()!=null){ + MatchQuery matchQuery=MatchQuery.of(m->m.field("content") + .query(requestImprovementList.getContent())); + mustQuery.add(matchQuery._toQuery()); + } + if(requestImprovementList.getTitle()!=null){ + MatchQuery matchQuery=MatchQuery.of(m->m.field("title") + .query(requestImprovementList.getTitle())); + mustQuery.add(matchQuery._toQuery()); + } + if(requestImprovementList.getNickName()!=null){ + MatchQuery matchQuery=MatchQuery.of(m->m.field("nickName") + .query(requestImprovementList.getNickName()) + .fuzziness("AUTO")); + mustQuery.add(matchQuery._toQuery()); + } + + return mustQuery; + } +} diff --git a/src/main/java/org/myteam/server/admin/repository/InquiryElasticRepository.java b/src/main/java/org/myteam/server/admin/repository/InquiryElasticRepository.java new file mode 100644 index 00000000..80730a20 --- /dev/null +++ b/src/main/java/org/myteam/server/admin/repository/InquiryElasticRepository.java @@ -0,0 +1,114 @@ +package org.myteam.server.admin.repository; + +import co.elastic.clients.elasticsearch._types.SortOptions; +import co.elastic.clients.elasticsearch._types.SortOrder; +import co.elastic.clients.elasticsearch._types.query_dsl.*; +import lombok.RequiredArgsConstructor; +import org.myteam.server.admin.document.InquiryDocument; +import org.myteam.server.admin.utill.enums.DateFormatEnum; +import org.myteam.server.global.util.date.DateFormatUtil; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.elasticsearch.client.elc.NativeQuery; +import org.springframework.data.elasticsearch.core.ElasticsearchOperations; +import org.springframework.data.elasticsearch.core.SearchHits; +import org.springframework.stereotype.Repository; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import static org.myteam.server.admin.dto.request.InquiryRequestDto.*; +import static org.myteam.server.admin.dto.response.InquiryResponseDto.*; + +@Repository +@RequiredArgsConstructor +public class InquiryElasticRepository { + + private final ElasticsearchOperations elasticsearchOperations; + + public Page getInquiryListByContElasticSearch(RequestInquiryListCond requestInquiryListCond){ + List filter=makeFilterQuery(requestInquiryListCond); + List must=makeMustQuery(requestInquiryListCond); + PageRequest pageRequest=PageRequest.of(requestInquiryListCond.getOffset(),10); + + Query query=new BoolQuery.Builder() + .filter(filter) + .must(must) + .build() + ._toQuery(); + + NativeQuery nativeQuery=NativeQuery.builder() + .withQuery(query) + .withSort(List.of(SortOptions.of(s->s.field(f->f.field("createDate") + .order(SortOrder.Desc))))) + .withPageable(pageRequest) + .build(); + SearchHits data=elasticsearchOperations.search(nativeQuery, InquiryDocument.class); + + List responseInquiryListConds=data.stream().map(x->{ + InquiryDocument doc=x.getContent(); + String isAnswered= doc.getIsAdminAnswered() ? "답변완료" :"답변대기"; + String isMember=doc.getIsMember() ? "회원":"비회원"; + String nicknameEmail=doc.getNickName()==null ? doc.getEmail() : doc.getNickName(); + + return new ResponseInquiryListCond( + doc.getId(),isAnswered,isMember,nicknameEmail,doc.getContent(),doc.getEmail(), + doc.getCreateDate().toString() + ); + }).collect(Collectors.toList()); + + DateFormatUtil.makeElasticTimeByFormatter(responseInquiryListConds, DateFormatEnum.formatByDotReq); + + return new PageImpl<>(responseInquiryListConds,pageRequest,data.getTotalHits()); + + } + + private List makeFilterQuery(RequestInquiryListCond requestInquiryListCond){ + List filterQuery=new ArrayList<>(); + if(requestInquiryListCond.getIsMember()!=null){ + TermQuery termQuery=TermQuery.of(t->t.field("isMember") + .value(requestInquiryListCond.getIsMember())); + filterQuery.add(termQuery._toQuery()); + } + if(requestInquiryListCond.getIsAnswered()!=null){ + TermQuery termQuery=TermQuery.of(t->t.field("isAdminAnswered") + .value(requestInquiryListCond.getIsAnswered())); + filterQuery.add(termQuery._toQuery()); + } + if(requestInquiryListCond.provideStartTime()!=null&&requestInquiryListCond.provideEndTime()!=null){ + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"); + RangeQuery rangeQuery=RangeQuery.of(r->r.date( + v->v.field("createDate") + .gte(requestInquiryListCond.provideStartTime().format(formatter)) + .lt(requestInquiryListCond.provideEndTime().format(formatter)) + )); + filterQuery.add(rangeQuery._toQuery()); + } + if(requestInquiryListCond.getEmail()!=null){ + MatchQuery matchQuery=MatchQuery.of(m->m.field("email") + .query(requestInquiryListCond.getEmail()) + .fuzziness("AUTO")); + filterQuery.add(matchQuery._toQuery()); + } + + return filterQuery; + } + private List makeMustQuery(RequestInquiryListCond requestInquiryListCond){ + List mustQuery=new ArrayList<>(); + + if(requestInquiryListCond.getContent()!=null){ + MatchQuery matchQuery=MatchQuery.of(m->m.field("content") + .query(requestInquiryListCond.getContent())); + mustQuery.add(matchQuery._toQuery()); + } + + if(requestInquiryListCond.getNickName()!=null){ + MatchQuery matchQuery=MatchQuery.of(m->m.field("nickName") + .query(requestInquiryListCond.getNickName()) + .fuzziness("AUTO")); + mustQuery.add(matchQuery._toQuery()); + } + return mustQuery; + } +} diff --git a/src/main/java/org/myteam/server/admin/repository/InquirySearchRepo.java b/src/main/java/org/myteam/server/admin/repository/InquirySearchRepo.java index 4753a46e..0bed1454 100644 --- a/src/main/java/org/myteam/server/admin/repository/InquirySearchRepo.java +++ b/src/main/java/org/myteam/server/admin/repository/InquirySearchRepo.java @@ -1,25 +1,38 @@ package org.myteam.server.admin.repository; +import co.elastic.clients.elasticsearch._types.SortOptions; +import co.elastic.clients.elasticsearch._types.SortOrder; +import co.elastic.clients.elasticsearch._types.query_dsl.*; import com.querydsl.core.types.Predicate; import com.querydsl.core.types.Projections; import com.querydsl.core.types.dsl.CaseBuilder; import com.querydsl.jpa.impl.JPAQueryFactory; import lombok.RequiredArgsConstructor; +import org.myteam.server.admin.document.ContentDocument; +import org.myteam.server.admin.document.InquiryDocument; import org.myteam.server.admin.entity.AdminContentMemo; import org.myteam.server.admin.utill.CreateAdminMemo; import org.myteam.server.admin.utill.enums.DateFormatEnum; import org.myteam.server.admin.utill.enums.StaticDataType; import org.myteam.server.global.util.date.DateFormatUtil; +import org.myteam.server.match.match.domain.Match; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.data.elasticsearch.client.elc.NativeQuery; +import org.springframework.data.elasticsearch.core.ElasticsearchOperations; +import org.springframework.data.elasticsearch.core.SearchHits; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; + import static org.myteam.server.admin.dto.request.AdminMemoRequestDto.AdminMemoInquiryRequest; import static org.myteam.server.admin.dto.response.InquiryResponseDto.*; import static org.myteam.server.admin.dto.response.InquiryResponseDto.ResponseInquiryList; @@ -35,7 +48,6 @@ public class InquirySearchRepo { private final JPAQueryFactory queryFactory; private final CreateAdminMemo createAdminMemo; - public AdminContentMemo createAdminMemo(AdminMemoInquiryRequest adminMemoRequest) { return createAdminMemo.createInquiryAdminMemo(adminMemoRequest, queryFactory); @@ -179,6 +191,8 @@ public Page getInquiryListByCond(RequestInquiryListCond } + + private Predicate memberOrNot(Boolean isMember) { if (isMember == null) { return null; @@ -240,4 +254,6 @@ private Predicate searchByTimeLine(LocalDateTime startTime, LocalDateTime endTim } + + } diff --git a/src/main/java/org/myteam/server/admin/repository/simpleRepo/ElasticContentRepository.java b/src/main/java/org/myteam/server/admin/repository/simpleRepo/ElasticContentRepository.java new file mode 100644 index 00000000..e7731680 --- /dev/null +++ b/src/main/java/org/myteam/server/admin/repository/simpleRepo/ElasticContentRepository.java @@ -0,0 +1,11 @@ +package org.myteam.server.admin.repository.simpleRepo; + +import org.myteam.server.admin.document.ContentDocument; +import org.myteam.server.admin.utill.enums.StaticDataType; +import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; + +import java.util.Optional; + +public interface ElasticContentRepository extends ElasticsearchRepository { + Optional findByContentIdAndStaticDataType(Long contentId, StaticDataType staticDataType); +} diff --git a/src/main/java/org/myteam/server/admin/repository/simpleRepo/ElasticImproverRepository.java b/src/main/java/org/myteam/server/admin/repository/simpleRepo/ElasticImproverRepository.java new file mode 100644 index 00000000..dfcf5e9e --- /dev/null +++ b/src/main/java/org/myteam/server/admin/repository/simpleRepo/ElasticImproverRepository.java @@ -0,0 +1,7 @@ +package org.myteam.server.admin.repository.simpleRepo; + +import org.myteam.server.admin.document.ImprovementDocument; +import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; + +public interface ElasticImproverRepository extends ElasticsearchRepository { +} diff --git a/src/main/java/org/myteam/server/admin/repository/simpleRepo/ElasticInquiryRepository.java b/src/main/java/org/myteam/server/admin/repository/simpleRepo/ElasticInquiryRepository.java new file mode 100644 index 00000000..01baefb8 --- /dev/null +++ b/src/main/java/org/myteam/server/admin/repository/simpleRepo/ElasticInquiryRepository.java @@ -0,0 +1,7 @@ +package org.myteam.server.admin.repository.simpleRepo; + +import org.myteam.server.admin.document.InquiryDocument; +import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; + +public interface ElasticInquiryRepository extends ElasticsearchRepository { +} diff --git a/src/main/java/org/myteam/server/global/config/ElasticConfig.java b/src/main/java/org/myteam/server/global/config/ElasticConfig.java new file mode 100644 index 00000000..c528605c --- /dev/null +++ b/src/main/java/org/myteam/server/global/config/ElasticConfig.java @@ -0,0 +1,28 @@ +package org.myteam.server.global.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.elasticsearch.client.ClientConfiguration; +import org.springframework.data.elasticsearch.client.elc.ElasticsearchConfiguration; + + +@Configuration +public class ElasticConfig extends ElasticsearchConfiguration { + + @Value("${spring.elasticsearch.username}") + private String username; + + @Value("${spring.elasticsearch.password}") + private String password; + + @Value("${spring.elasticsearch.uris}") + private String[] esHost; + + @Override + public ClientConfiguration clientConfiguration() { + return ClientConfiguration.builder() + .connectedTo(esHost) + .withBasicAuth(username,password) + .build(); + } +} diff --git a/src/main/java/org/myteam/server/global/elastic/ElasticSearchSaveHandler.java b/src/main/java/org/myteam/server/global/elastic/ElasticSearchSaveHandler.java new file mode 100644 index 00000000..071210e4 --- /dev/null +++ b/src/main/java/org/myteam/server/global/elastic/ElasticSearchSaveHandler.java @@ -0,0 +1,60 @@ +package org.myteam.server.global.elastic; + + +import lombok.RequiredArgsConstructor; +import org.myteam.server.admin.document.ContentDocument; +import org.myteam.server.admin.document.ImprovementDocument; +import org.myteam.server.admin.document.InquiryDocument; +import org.myteam.server.admin.repository.simpleRepo.ElasticContentRepository; +import org.myteam.server.admin.repository.simpleRepo.ElasticImproverRepository; +import org.myteam.server.admin.repository.simpleRepo.ElasticInquiryRepository; +import org.myteam.server.global.elastic.event.*; +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +@RequiredArgsConstructor +@Component +public class ElasticSearchSaveHandler { + + private final ElasticContentRepository elasticContentRepository; + private final ElasticImproverRepository elasticImproverRepository; + private final ElasticInquiryRepository elasticInquiryRepository; + + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) + public void saveContentAfterCommit(ElasticContentEvent contentEvent){ + elasticContentRepository.save(contentEvent.getDoc()); + } + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) + public void saveInquiryAfterCommit(ElasticInquiryEvent inquiryEvent){ + elasticInquiryRepository.save(inquiryEvent.getDoc()); + } + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) + public void saveImproveAfterCommit(ElasticImproveEvent improveEvent){ + elasticImproverRepository.save(improveEvent.getDoc()); + } + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) + public void updateContentAfterCommit(ElasticContentUpdateEvent contentEvent){ + ContentDocument contentDocument= + elasticContentRepository.findByContentIdAndStaticDataType(contentEvent.getContentId(),contentEvent.getStaticDataType()).get(); + ContentDocument toUpdateDoc=contentEvent.updateDoc(contentDocument); + elasticContentRepository.save(toUpdateDoc); + + } + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) + public void updateInquiryAfterCommit(ElasticInquiryUpdateEvent inquiryEvent){ + InquiryDocument inquiryDocument= + elasticInquiryRepository.findById(inquiryEvent.getId()).get(); + InquiryDocument toUpdateDoc=inquiryEvent.updateDoc(inquiryDocument); + elasticInquiryRepository.save(toUpdateDoc); + } + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) + public void updateImproveAfterCommit(ElasticImproveUpdateEvent improveEvent){ + ImprovementDocument improvementDocument= + elasticImproverRepository.findById(improveEvent.getId()).get(); + ImprovementDocument toUpdateDoc=improveEvent.updateDoc(improvementDocument); + elasticImproverRepository.save(toUpdateDoc); + } + + +} diff --git a/src/main/java/org/myteam/server/global/elastic/event/ElasticContentEvent.java b/src/main/java/org/myteam/server/global/elastic/event/ElasticContentEvent.java new file mode 100644 index 00000000..d50a6d5d --- /dev/null +++ b/src/main/java/org/myteam/server/global/elastic/event/ElasticContentEvent.java @@ -0,0 +1,60 @@ +package org.myteam.server.global.elastic.event; + + +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.myteam.server.admin.document.ContentDocument; +import org.myteam.server.admin.utill.enums.AdminControlType; +import org.myteam.server.admin.utill.enums.StaticDataType; +import org.springframework.data.annotation.Id; +import org.springframework.data.elasticsearch.annotations.DateFormat; +import org.springframework.data.elasticsearch.annotations.Field; +import org.springframework.data.elasticsearch.annotations.FieldType; + +import java.time.LocalDateTime; + +@Getter +@NoArgsConstructor +public class ElasticContentEvent { + + private String email; + private String nickName; + private StaticDataType staticDataType; + private AdminControlType adminControlType; + private Boolean isReported; + private Long contentId; + private String title; + private String content; + private LocalDateTime createDate; + + + @Builder + public ElasticContentEvent(String email, String nickName, StaticDataType staticDataType, + AdminControlType adminControlType, Boolean isReported, Long contentId, + String title, String content, LocalDateTime createDate) { + this.email = email; + this.nickName = nickName; + this.staticDataType = staticDataType; + this.adminControlType = adminControlType; + this.isReported=isReported; + this.contentId = contentId; + this.title = title; + this.content = content; + this.createDate = createDate; + } + + public ContentDocument getDoc(){ + return ContentDocument.builder() + .email(this.email) + .nickName(this.nickName) + .staticDataType(this.staticDataType) + .adminControlType(this.adminControlType) + .isReported(this.isReported) + .contentId(this.contentId) + .title(this.title) + .content(this.content) + .createDate(this.createDate) + .build(); + } +} diff --git a/src/main/java/org/myteam/server/global/elastic/event/ElasticContentUpdateEvent.java b/src/main/java/org/myteam/server/global/elastic/event/ElasticContentUpdateEvent.java new file mode 100644 index 00000000..1706e681 --- /dev/null +++ b/src/main/java/org/myteam/server/global/elastic/event/ElasticContentUpdateEvent.java @@ -0,0 +1,54 @@ +package org.myteam.server.global.elastic.event; + +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.myteam.server.admin.document.ContentDocument; +import org.myteam.server.admin.utill.enums.AdminControlType; +import org.myteam.server.admin.utill.enums.StaticDataType; + + +@Getter +@NoArgsConstructor +public class ElasticContentUpdateEvent { + + private StaticDataType staticDataType; + private String nickName; + private AdminControlType adminControlType; + private Boolean isReported; + private Long contentId; + private String title; + private String content; + + + public ElasticContentUpdateEvent(StaticDataType staticDataType, String nickName, AdminControlType adminControlType, + Boolean isReported, Long contentId, String title, String content) { + this.staticDataType = staticDataType; + this.nickName = nickName; + this.adminControlType = adminControlType; + this.isReported = isReported; + this.contentId = contentId; + this.title = title; + this.content = content; + } + + @Builder + + + public ContentDocument updateDoc(ContentDocument doc){ + return ContentDocument.builder() + .id(doc.getId()) + .email(doc.getEmail()) + .nickName(this.nickName!=null ? this.nickName:doc.getNickName()) + .staticDataType(doc.getStaticDataType()) + .adminControlType(this.adminControlType!=null ? this.adminControlType:doc.getAdminControlType()) + .isReported(this.isReported!=null ? this.isReported :doc.getIsReported()) + .contentId(doc.getContentId()) + .title(this.title!=null? this.title:doc.getTitle()) + .content(this.content!=null? this.content:doc.getContent()) + .createDate(doc.getCreateDate()) + .build(); + } + + +} diff --git a/src/main/java/org/myteam/server/global/elastic/event/ElasticImproveEvent.java b/src/main/java/org/myteam/server/global/elastic/event/ElasticImproveEvent.java new file mode 100644 index 00000000..8f335279 --- /dev/null +++ b/src/main/java/org/myteam/server/global/elastic/event/ElasticImproveEvent.java @@ -0,0 +1,55 @@ +package org.myteam.server.global.elastic.event; + +import jakarta.persistence.Id; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.myteam.server.admin.document.ImprovementDocument; +import org.myteam.server.improvement.domain.ImportantStatus; +import org.myteam.server.improvement.domain.ImprovementStatus; +import org.springframework.data.elasticsearch.annotations.DateFormat; +import org.springframework.data.elasticsearch.annotations.Field; +import org.springframework.data.elasticsearch.annotations.FieldType; + +import java.time.LocalDateTime; +import java.util.UUID; + +@Getter +@NoArgsConstructor +public class ElasticImproveEvent { + + + public Long id; + private UUID memberId; + private String title; + private String content; + private String nickName; + private ImprovementStatus improvementStatus; + private ImportantStatus importantStatus; + private LocalDateTime createDate; + @Builder + public ElasticImproveEvent(Long id, UUID memberId, String title, String content, String nickName, + ImprovementStatus improvementStatus, ImportantStatus importantStatus, + LocalDateTime createDate) { + this.id = id; + this.memberId = memberId; + this.title = title; + this.content = content; + this.nickName = nickName; + this.improvementStatus = improvementStatus; + this.importantStatus = importantStatus; + this.createDate = createDate; + } + public ImprovementDocument getDoc(){ + return ImprovementDocument.builder() + .id(this.id) + .content(this.content) + .nickName(this.nickName) + .title(this.title) + .improvementStatus(this.improvementStatus) + .importantStatus(this.importantStatus) + .memberId(this.memberId) + .createDate(this.createDate) + .build(); + } +} diff --git a/src/main/java/org/myteam/server/global/elastic/event/ElasticImproveUpdateEvent.java b/src/main/java/org/myteam/server/global/elastic/event/ElasticImproveUpdateEvent.java new file mode 100644 index 00000000..1bdb0748 --- /dev/null +++ b/src/main/java/org/myteam/server/global/elastic/event/ElasticImproveUpdateEvent.java @@ -0,0 +1,48 @@ +package org.myteam.server.global.elastic.event; + +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.myteam.server.admin.document.ImprovementDocument; +import org.myteam.server.improvement.domain.ImportantStatus; +import org.myteam.server.improvement.domain.ImprovementStatus; + +import java.time.LocalDateTime; +import java.util.UUID; + +@Getter +@NoArgsConstructor +public class ElasticImproveUpdateEvent { + + private Long id; + private String title; + private String content; + private String nickName; + private ImprovementStatus improvementStatus; + private ImportantStatus importantStatus; + + public ElasticImproveUpdateEvent(Long id, String title, String content, String nickName, + ImprovementStatus improvementStatus, ImportantStatus importantStatus) { + this.id = id; + this.title = title; + this.content = content; + this.nickName = nickName; + this.improvementStatus = improvementStatus; + this.importantStatus = importantStatus; + } + + @Builder + + public ImprovementDocument updateDoc(ImprovementDocument doc){ + return ImprovementDocument.builder() + .id(doc.getId()) + .content(this.content!=null ? this.content:doc.getContent()) + .nickName(this.nickName!=null? this.nickName: doc.getNickName()) + .title(this.title!=null? this.title:doc.getTitle()) + .improvementStatus(this.improvementStatus!=null? this.improvementStatus:doc.getImprovementStatus()) + .importantStatus(this.importantStatus!=null? this.importantStatus:doc.getImportantStatus()) + .memberId(doc.getMemberId()) + .createDate(doc.getCreateDate()) + .build(); + } +} diff --git a/src/main/java/org/myteam/server/global/elastic/event/ElasticInquiryEvent.java b/src/main/java/org/myteam/server/global/elastic/event/ElasticInquiryEvent.java new file mode 100644 index 00000000..ae8c4d9f --- /dev/null +++ b/src/main/java/org/myteam/server/global/elastic/event/ElasticInquiryEvent.java @@ -0,0 +1,45 @@ +package org.myteam.server.global.elastic.event; + +import jakarta.persistence.Id; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.myteam.server.admin.document.InquiryDocument; + +import java.time.LocalDateTime; + +@Getter +@NoArgsConstructor +public class ElasticInquiryEvent { + + private Long id; + private String content; + private String nickName; + private LocalDateTime createDate; + private String email; + private Boolean isAdminAnswered; + private Boolean isMember; + + @Builder + public ElasticInquiryEvent(Long id, String content, String nickName, LocalDateTime createDate, + String email, Boolean isAdminAnswered, Boolean isMember) { + this.id = id; + this.content = content; + this.nickName = nickName; + this.createDate = createDate; + this.email = email; + this.isAdminAnswered = isAdminAnswered; + this.isMember = isMember; + } + public InquiryDocument getDoc(){ + return InquiryDocument.builder() + .id(this.id) + .email(this.email) + .createDate(this.createDate) + .isAdminAnswered(this.isAdminAnswered) + .content(this.content) + .nickName(this.nickName) + .isMember(this.isMember) + .build(); + } +} diff --git a/src/main/java/org/myteam/server/global/elastic/event/ElasticInquiryUpdateEvent.java b/src/main/java/org/myteam/server/global/elastic/event/ElasticInquiryUpdateEvent.java new file mode 100644 index 00000000..b4386fb0 --- /dev/null +++ b/src/main/java/org/myteam/server/global/elastic/event/ElasticInquiryUpdateEvent.java @@ -0,0 +1,45 @@ +package org.myteam.server.global.elastic.event; + +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.RequiredArgsConstructor; +import org.myteam.server.admin.document.InquiryDocument; + +import java.time.LocalDateTime; + +@NoArgsConstructor +@Getter +public class ElasticInquiryUpdateEvent { + + private Long id; + private String content; + private String nickName; + private Boolean isAdminAnswered; + private Boolean isMember; + + + @Builder + public ElasticInquiryUpdateEvent(Long id, String content, String nickName, + Boolean isAdminAnswered, Boolean isMember) { + this.id = id; + this.content = content; + this.nickName = nickName; + this.isAdminAnswered = isAdminAnswered; + this.isMember = isMember; + } + + + + public InquiryDocument updateDoc(InquiryDocument doc){ + return InquiryDocument.builder() + .id(doc.getId()) + .email(doc.getEmail()) + .createDate(doc.getCreateDate()) + .isAdminAnswered(this.isAdminAnswered!=null ? this.isAdminAnswered :doc.getIsAdminAnswered()) + .content(this.content!=null ? this.content : doc.getContent()) + .nickName(this.nickName!=null ? this.nickName : doc.getNickName()) + .isMember(this.isMember!=null? this.isMember:doc.getIsMember()) + .build(); + } +} diff --git a/src/main/java/org/myteam/server/global/util/date/DateFormatUtil.java b/src/main/java/org/myteam/server/global/util/date/DateFormatUtil.java index 9f2b4eb8..0e1c1ba4 100644 --- a/src/main/java/org/myteam/server/global/util/date/DateFormatUtil.java +++ b/src/main/java/org/myteam/server/global/util/date/DateFormatUtil.java @@ -45,5 +45,16 @@ public static void makeTimeByFormatter(List data, , DateFormatUtil.FLEXIBLE_NANO_FORMATTER))); }); } + public static void makeElasticTimeByFormatter(List data, DateFormatEnum dateFormatEnum){ + data.stream() + .forEach(x -> { + x.updateCreateDate( + dateFormatEnum.equals(DateFormatEnum.formatByDotReq) ? + DateFormatUtil.formatByDot + .format(LocalDateTime.parse(x.getCreateDate())) + : DateFormatUtil.formatByDotAndSlash + .format(LocalDateTime.parse(x.getCreateDate()))); + }); + } } diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 02f8e56f..986fcf15 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -1,7 +1,6 @@ spring: jackson: time-zone: Asia/Seoul - datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://${DB_ENDPOINT}:3306/${DB_NAME}?useSSL=false&serverTimezone=Asia/Seoul diff --git a/src/main/resources/application-test.yml b/src/main/resources/application-test.yml index 1e614a0b..1cb84464 100755 --- a/src/main/resources/application-test.yml +++ b/src/main/resources/application-test.yml @@ -1,4 +1,9 @@ spring: + elasticsearch: + username: elastic + password: NH9OIw*T7rCotS8vrpsj #로컬환경실행시에 랜덤으로 배정되는 비밀번혼대 사용자마다 다르니까 알아서 바꿔야될거같습니다. + uris: + localhost:9200 datasource: url: jdbc:h2:mem:~/playhive;MODE=MySQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE driver-class-name: org.h2.Driver @@ -136,3 +141,7 @@ google: name: googleApi url: https://www.googleapis.com apiKey: test +logging: + level.org: + springframework.data.elasticsearch: DEBUG + #elasticsearch.client: DEBUG \ No newline at end of file diff --git a/src/test/java/org/myteam/server/admin/repository/ElasticSearchTest.java b/src/test/java/org/myteam/server/admin/repository/ElasticSearchTest.java new file mode 100644 index 00000000..69123ef6 --- /dev/null +++ b/src/test/java/org/myteam/server/admin/repository/ElasticSearchTest.java @@ -0,0 +1,216 @@ +package org.myteam.server.admin.repository; + + +import co.elastic.clients.json.JsonpUtils; +import com.querydsl.jpa.impl.JPAQueryFactory; +import io.lettuce.core.ScriptOutputType; +import org.assertj.core.api.Assertions; +import org.junit.Before; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.myteam.server.admin.document.ContentDocument; +import org.myteam.server.admin.document.ImprovementDocument; +import org.myteam.server.admin.document.InquiryDocument; +import org.myteam.server.admin.dto.request.ContentRequestDto; +import org.myteam.server.admin.dto.request.ImproveRequestDto; +import org.myteam.server.admin.dto.request.InquiryRequestDto; +import org.myteam.server.admin.dto.response.ImprovementResponseDto; +import org.myteam.server.admin.dto.response.InquiryResponseDto; +import org.myteam.server.admin.dto.response.ResponseContentDto; +import org.myteam.server.admin.repository.simpleRepo.ElasticContentRepository; +import org.myteam.server.admin.repository.simpleRepo.ElasticImproverRepository; +import org.myteam.server.admin.repository.simpleRepo.ElasticInquiryRepository; +import org.myteam.server.admin.utill.enums.AdminControlType; +import org.myteam.server.admin.utill.enums.StaticDataType; +import org.myteam.server.board.domain.Board; +import org.myteam.server.board.domain.BoardSearchType; +import org.myteam.server.board.domain.CategoryType; +import org.myteam.server.global.domain.Category; +import org.myteam.server.global.util.date.DateFormatUtil; +import org.myteam.server.improvement.domain.ImportantStatus; +import org.myteam.server.improvement.domain.Improvement; +import org.myteam.server.improvement.domain.ImprovementStatus; +import org.myteam.server.improvement.repository.ImprovementQueryRepository; +import org.myteam.server.inquiry.domain.Inquiry; +import org.myteam.server.member.entity.Member; +import org.myteam.server.support.IntegrationTestSupport; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.domain.Page; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.ArrayList; +import java.util.List; + +import static org.myteam.server.admin.dto.request.ContentRequestDto.*; +import static org.myteam.server.admin.dto.response.ImprovementResponseDto.*; +import static org.myteam.server.admin.dto.response.InquiryResponseDto.*; +import static org.myteam.server.admin.dto.response.ResponseContentDto.*; +import static org.myteam.server.board.domain.QBoard.board; + +//@SpringBootTest +public class ElasticSearchTest extends IntegrationTestSupport { + static private DataSource testDataSource; + + @Autowired + ContentSearchRepository contentSearchRepository; + @Autowired + InquirySearchRepo inquirySearchRepo; + @Autowired + AdminImprovementSearchRepo adminImprovementSearchRepo; + + @Autowired + InquiryElasticRepository inquiryElasticRepository; + + @Autowired + ImproveElasticRepository improveElasticRepository; + + @Autowired + ContentElasticRepository contentElasticRepository; + + Member m; + //@BeforeAll + static void setupH2CustomFunctions(@Autowired DataSource dataSource) { + testDataSource = dataSource; // + try (Connection conn = testDataSource.getConnection(); + Statement stmt = conn.createStatement()) { + + stmt.execute("CREATE ALIAS IF NOT EXISTS DATE_FORMAT FOR 'org.myteam.server.admin.utill.StaticUtil.dateFormat'"); + System.out.println("H2에 DATE_FORMAT 함수 별칭이 성공적으로 등록되었습니다."); + } catch (SQLException e) { + System.err.println("H2 함수 별칭 등록 중 오류 발생: " + e.getMessage()); + e.printStackTrace(); + + throw new RuntimeException("Failed to register H2 DATE_FORMAT alias", e); + } + } + + + //@BeforeEach + void setBeforeTest(){ + m=createMember(0); + + for(int i=0;10>i;i++){ + if(i%2==0){ + Board b=createBoard(m, Category.ESPORTS, CategoryType.FREE,"hello","오늘은 날씨가 매우 맑아서 공원에 나가 산책을 하기로 했다. 새들이 지저귀는 소리를 들으며 길을 걷다 보면 마음이 편안해진다. hello 친구들과 함께 커피를 마시며 이야기를 나누는 시간도 즐겁다. 점심으로는 간단하게 김밥과 떡볶이를 먹고," + + " 오후에는 책을 읽으며 휴식을 취했다. 저녁이 되자 노을이 아름답게 물들어 하루를 마무리했다."); + Inquiry inquiry=createInquiry(m); + Improvement improvement=createImprovement(m,false); + ContentDocument c + =ContentDocument + .builder() + .staticDataType(StaticDataType.BOARD) + .adminControlType(AdminControlType.HIDDEN) + .title("hello") + .contentId(b.getId()) + .content(b.getContent()) + .createDate(b.getCreateDate()) + .isReported(false) + .email(m.getEmail()) + .build(); + InquiryDocument inquiryDoc=InquiryDocument.builder() + .id(inquiry.getId()) + .content(inquiry.getContent()) + .isAdminAnswered(inquiry.isAdminAnswered()) + .isMember(true) + .createDate(inquiry.getCreatedAt()) + .email(inquiry.getEmail()) + .build(); + ImprovementDocument improvementDocument=ImprovementDocument.builder() + .id(improvement.getId()) + .improvementStatus(ImprovementStatus.PENDING) + .nickName(m.getNickname()) + .content(improvement.getContent()) + .title(improvement.getTitle()) + .importantStatus(ImportantStatus.NORMAL) + .createDate(improvement.getCreateDate()) + .title(improvement.getTitle()) + .build(); + elasticInquiryRepository.save(inquiryDoc); + elasticContentRepository.save(c); + elasticImproverRepository.save(improvementDocument); + } + else{ + Board b=createBoard(m, Category.ESPORTS, CategoryType.FREE,"hello", + "오늘은 날씨가 매우 맑아서 공원에 나가 산책을 하기로 했다. 새들이 지저귀는 소리를 들으며 길을 걷다 보면 마음이 편안해진다. 친구들과 함께 커피를 마시며 이야기를 나누는 시간도 즐겁다. " + + "점심으로는 간단하게 김밥과 떡볶이를 먹고, 오후에는 책을 읽으며 휴식을 취했다. 저녁이 되자 노을이 아름답게 물들어 하루를 마무리했다."); + ContentDocument c + =ContentDocument + .builder() + .staticDataType(StaticDataType.BOARD) + .adminControlType(AdminControlType.HIDDEN) + .title("hello") + .contentId(b.getId()) + .content(b.getContent()) + .createDate(b.getCreateDate()) + .isReported(false) + .email(m.getEmail()) + .build(); + elasticContentRepository.save(c); + } + } + + } + + //@Test + void contentTest(){ + + LocalDateTime now=LocalDateTime.now(); + LocalDateTime end=now.plusDays(1L); + String startTime= DateFormatUtil.formatByDot.format(now); + String endTime=DateFormatUtil.formatByDot.format(end); + + ContentRequestDto.RequestContentData requestContentData= + RequestContentData + .builder() + .staticDataType(StaticDataType.BOARD) + .boardSearchType(BoardSearchType.CONTENT) + .searchKeyWord("hello") + .startTime(startTime) + .endTime(endTime) + .reported(false) + .offset(1) + .build(); + Page responseContentSearches + =contentElasticRepository.useElasticSearchForUnionQuery(requestContentData); + Assertions.assertThat(responseContentSearches.getSize()).isEqualTo(5); + + + + ImproveRequestDto.RequestImprovementList requestImprovementList + = ImproveRequestDto.RequestImprovementList.builder() + .improvementStatus(ImprovementStatus.PENDING) + .importantStatus(ImportantStatus.NORMAL) + .endTime(endTime) + .nickName(m.getNickname()) + .startTime(startTime) + .offset(1) + .build(); + + Page responseImprovements + =improveElasticRepository.getImprovementByElasticSearch(requestImprovementList); + + Assertions.assertThat(responseImprovements.getSize()).isEqualTo(5); + + InquiryRequestDto.RequestInquiryListCond requestInquiryListCond= + InquiryRequestDto.RequestInquiryListCond.builder() + .isMember(true) + .offset(1) + .startTime(startTime) + .endTime(endTime) + .email(m.getEmail()) + .build(); + + Page responseInquiryLists + =inquiryElasticRepository.getInquiryListByContElasticSearch(requestInquiryListCond); + Assertions.assertThat(responseInquiryLists.getSize()).isEqualTo(5); + + + } +} diff --git a/src/test/java/org/myteam/server/support/IntegrationTestSupport.java b/src/test/java/org/myteam/server/support/IntegrationTestSupport.java index afa3d42f..cd37e856 100644 --- a/src/test/java/org/myteam/server/support/IntegrationTestSupport.java +++ b/src/test/java/org/myteam/server/support/IntegrationTestSupport.java @@ -108,6 +108,15 @@ public abstract class IntegrationTestSupport extends TestDriverSupport { protected AdminInquiryChangeLogRepo adminInquiryChangeLogRepo; @Autowired protected AdminMemberMemoRepo adminMemberMemoRepo; + + @Autowired + protected ElasticInquiryRepository elasticInquiryRepository; + @Autowired + protected ElasticImproverRepository elasticImproverRepository; + + @Autowired + protected ElasticContentRepository elasticContentRepository; + @AfterEach void tearDown() { adminMemberMemoRepo.deleteAllInBatch();; @@ -139,6 +148,9 @@ void tearDown() { memberActivityRepository.deleteAllInBatch(); memberAccessRepository.deleteAllInBatch(); memberJpaRepository.deleteAllInBatch(); + elasticImproverRepository.deleteAll(); + elasticInquiryRepository.deleteAll(); + elasticContentRepository.deleteAll(); } @Transactional