diff --git a/.github/workflows/cd-for-no-es.yml b/.github/workflows/cd-for-no-es.yml new file mode 100644 index 00000000..0d1b8433 --- /dev/null +++ b/.github/workflows/cd-for-no-es.yml @@ -0,0 +1,99 @@ +name: CD - upload to EC2 with no elasticsearch + +on: + push: + branches: + - develop-no-es + +jobs: + upload-to-ec2 : + runs-on: ubuntu-latest + + services: + postgres: + image: postgres:16 + env: + POSTGRES_DB: ${{ secrets.DB_DATABASE_NAME }} + POSTGRES_USER: ${{ secrets.DB_USERNAME }} + POSTGRES_PASSWORD: ${{ secrets.DB_PASSWORD }} + ports: + - 5432:5432 + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + + steps: + - name: 소스 코드 Checkout + uses: actions/checkout@v4 + + - name: Redis 실행 + uses: supercharge/redis-github-action@1.6.0 + + - name: .env 설정 + run: | + echo "SPRING_PROFILES_ACTIVE=ci" >> $GITHUB_ENV + echo "DB_URL=${{ secrets.DB_URL }}" >> $GITHUB_ENV + echo "DB_USERNAME=${{ secrets.DB_USERNAME }}" >> $GITHUB_ENV + echo "DB_PASSWORD=${{ secrets.DB_PASSWORD }}" >> $GITHUB_ENV + echo "REDIS_HOST=${{ secrets.REDIS_HOST }}" >> $GITHUB_ENV + echo "REDIS_PORT=${{ secrets.REDIS_PORT }}" >> $GITHUB_ENV + echo "JWT_SECRET_KEY=${{ secrets.JWT_SECRET_KEY }}" >> $GITHUB_ENV + echo "JWT_VALIDATION_TIME=${{ secrets.JWT_VALIDATION_TIME }}" >> $GITHUB_ENV + echo "SOCIAL_CLIENT_ID=${{ secrets.SOCIAL_CLIENT_ID }}" >> $GITHUB_ENV + echo "SOCIAL_SECRET=${{ secrets.SOCIAL_SECRET }}" >> $GITHUB_ENV + echo "API_KEY=${{ secrets.API_KEY }}" >> $GITHUB_ENV + echo "API_SECRET=${{ secrets.API_SECRET }}" >> $GITHUB_ENV + echo "S3_ACCESS_KEY=${{ secrets.S3_ACCESS_KEY }}" >> $GITHUB_ENV + echo "S3_SECRET_KEY=${{ secrets.S3_SECRET_KEY }}" >> $GITHUB_ENV + echo "S3_BUCKET_NAME=${{ secrets.S3_BUCKET_NAME }}" >> $GITHUB_ENV + echo "S3_BUCKET_URL=${{ secrets.S3_BUCKET_URL }}" >> $GITHUB_ENV + echo "LOGSTASH_URL=${{ secrets.LOGSTASH_URL }}" >> $GITHUB_ENV + echo "OCR_JSON=${{ secrets.OCR_JSON }}" >> $GITHUB_ENV + echo "FIRE_BASE_JSON=${{ secrets.FIRE_BASE_JSON }}" >> $GITHUB_ENV + echo "GOOGLE_MAIL=${{ secrets.GOOGLE_MAIL }}" >> $GITHUB_ENV + echo "GOOGLE_MAIL_PASSWORD=${{ secrets.GOOGLE_MAIL_PASSWORD }}" >> $GITHUB_ENV + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + - name: 실행 권한 부여 + run: chmod +x ./gradlew + + - name: firebase.json 복원 + run: | + echo "${{ secrets.FIRE_BASE_BASE_64_JSON }}" | base64 -d > src/main/resources/firebase.json + + - name: 빌드 테스트 + run: ./gradlew clean build -x test + + - name: Docker Hub 로그인 + run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin + + - name: Build 후 Docker image push + run: | + docker build -t ${{ secrets.DOCKER_USERNAME }}/badata:latest . + docker push ${{ secrets.DOCKER_USERNAME }}/badata:latest + + - name: EC2 배포 + uses: appleboy/ssh-action@v1.0.0 + with: + host: ${{ secrets.EC2_HOST }} + username: ubuntu + key: ${{ secrets.EC2_SSH_KEY }} + script: | + cd /home/ubuntu + + echo "기존 컨테이너 중지 및 제거" + sudo docker stop badata_be || true + sudo docker rm badata_be || true + + echo "새로운 이미지 빌드" + sudo docker pull ${{ secrets.DOCKER_USERNAME }}/badata:latest + sudo docker run --add-host host.docker.internal:host-gateway --env-file ~/be_badata/.env -v ~/be_badata${{ secrets.OCR_JSON }}:/BaData/src${{ secrets.OCR_JSON }} -d -p 8080:8080 --name badata_be ${{ secrets.DOCKER_USERNAME }}/badata:latest + + echo "배포 완료" diff --git a/src/main/java/com/TwoSeaU/BaData/domain/trade/entity/PostDocument.java b/src/main/java/com/TwoSeaU/BaData/domain/trade/entity/PostDocument.java deleted file mode 100644 index 145ef1c5..00000000 --- a/src/main/java/com/TwoSeaU/BaData/domain/trade/entity/PostDocument.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.TwoSeaU.BaData.domain.trade.entity; - -import jakarta.persistence.Id; -import lombok.*; -import org.springframework.data.elasticsearch.annotations.DateFormat; -import org.springframework.data.elasticsearch.annotations.Document; -import org.springframework.data.elasticsearch.annotations.Field; -import org.springframework.data.elasticsearch.annotations.FieldType; - -import java.time.LocalDate; -import java.time.LocalDateTime; - -@Getter -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@AllArgsConstructor(access = AccessLevel.PROTECTED) -@Builder(access = AccessLevel.PROTECTED) -@Document(indexName = "post") -public class PostDocument { - @Id - @Field(type = FieldType.Long) - private Long id; - - @Field(name = "title", type = FieldType.Text) - private String title; - - @Field(name = "comment", type = FieldType.Text) - private String comment; - - @Field(name = "deadLine", type = FieldType.Date) - private LocalDate deadLine; - - @Field(name = "createdAt", type = FieldType.Date, format = {DateFormat.date_hour_minute_second_millis, DateFormat.epoch_millis}) - private LocalDateTime createdAt; - - @Field(name = "postType", type = FieldType.Keyword) - private String postType; - - public static PostDocument from(final Post post) { - return PostDocument.builder() - .id(post.getId()) - .title(post.getTitle()) - .comment(post.getComment()) - .deadLine(post.getDeadLine()) - .createdAt(post.getCreatedAt()) - .postType(post.getClass().getSimpleName()) - .build(); - } -} \ No newline at end of file diff --git a/src/main/java/com/TwoSeaU/BaData/domain/trade/entity/SearchHistoryDocument.java b/src/main/java/com/TwoSeaU/BaData/domain/trade/entity/SearchHistoryDocument.java deleted file mode 100644 index d8ecd5b0..00000000 --- a/src/main/java/com/TwoSeaU/BaData/domain/trade/entity/SearchHistoryDocument.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.TwoSeaU.BaData.domain.trade.entity; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NoArgsConstructor; -import org.springframework.data.annotation.Id; -import org.springframework.data.elasticsearch.annotations.Document; -import org.springframework.data.elasticsearch.annotations.Field; -import org.springframework.data.elasticsearch.annotations.FieldType; -import java.time.Instant; - -@Getter -@Document(indexName = "keyword-index-v2") -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class SearchHistoryDocument { - @Id - @Field(type = FieldType.Keyword) - private String id; - - @Field(name = "@timestamp", type = FieldType.Date) - private Instant timestamp; - - @Field(name = "thread_name", type = FieldType.Keyword) - private String threadName; - - @Field(name = "@version", type = FieldType.Keyword) - private String version; - - @Field(name = "logger_name", type = FieldType.Keyword) - private String loggerName; - - @Field(type = FieldType.Keyword) - private String level; - - @Field(name = "level_value", type = FieldType.Integer) - private int levelValue; - - @Field(type = FieldType.Text) - private String message; -} diff --git a/src/main/java/com/TwoSeaU/BaData/domain/trade/repository/PostDocumentRepository.java b/src/main/java/com/TwoSeaU/BaData/domain/trade/repository/PostDocumentRepository.java deleted file mode 100644 index 883ba03a..00000000 --- a/src/main/java/com/TwoSeaU/BaData/domain/trade/repository/PostDocumentRepository.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.TwoSeaU.BaData.domain.trade.repository; - -import com.TwoSeaU.BaData.domain.trade.entity.PostDocument; -import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; - -public interface PostDocumentRepository extends ElasticsearchRepository { - -} diff --git a/src/main/java/com/TwoSeaU/BaData/domain/trade/service/PaymentService.java b/src/main/java/com/TwoSeaU/BaData/domain/trade/service/PaymentService.java index c34875da..de49b4fa 100644 --- a/src/main/java/com/TwoSeaU/BaData/domain/trade/service/PaymentService.java +++ b/src/main/java/com/TwoSeaU/BaData/domain/trade/service/PaymentService.java @@ -6,12 +6,10 @@ import com.TwoSeaU.BaData.domain.trade.entity.Gifticon; import com.TwoSeaU.BaData.domain.trade.entity.Payment; import com.TwoSeaU.BaData.domain.trade.entity.Post; -import com.TwoSeaU.BaData.domain.trade.entity.PostDocument; import com.TwoSeaU.BaData.domain.trade.enums.PayMethod; import com.TwoSeaU.BaData.domain.trade.enums.PaymentStatus; import com.TwoSeaU.BaData.domain.trade.exception.TradeException; import com.TwoSeaU.BaData.domain.trade.repository.PaymentRepository; -import com.TwoSeaU.BaData.domain.trade.repository.PostDocumentRepository; import com.TwoSeaU.BaData.domain.trade.repository.PostRepository; import com.TwoSeaU.BaData.domain.user.entity.CoinHistory; import com.TwoSeaU.BaData.domain.user.entity.User; @@ -42,7 +40,6 @@ @Transactional public class PaymentService { private final PaymentRepository paymentRepository; - private final PostDocumentRepository postDocumentRepository; private final UserRepository userRepository; private final PostRepository postRepository; private final CoinHistoryRepository coinHistoryRepository; @@ -164,9 +161,6 @@ public GetValidatePaymentResponse validateIamport(final String impUid, final Lon payment.updatePaymentStatus(PaymentStatus.PAID); - final PostDocument postDocument = PostDocument.from(post); - postDocumentRepository.delete(postDocument); - user.updateUsedCoin(payment.getUseCoin().intValue()); if (payment.getUseCoin().intValue() > 0){ diff --git a/src/main/java/com/TwoSeaU/BaData/domain/trade/service/PostSearchService.java b/src/main/java/com/TwoSeaU/BaData/domain/trade/service/PostSearchService.java index 7246a167..cb9abd79 100644 --- a/src/main/java/com/TwoSeaU/BaData/domain/trade/service/PostSearchService.java +++ b/src/main/java/com/TwoSeaU/BaData/domain/trade/service/PostSearchService.java @@ -6,7 +6,6 @@ import co.elastic.clients.elasticsearch._types.query_dsl.TextQueryType; import com.TwoSeaU.BaData.domain.trade.dto.response.PostResponse; import com.TwoSeaU.BaData.domain.trade.entity.Post; -import com.TwoSeaU.BaData.domain.trade.entity.PostDocument; import com.TwoSeaU.BaData.domain.trade.exception.TradeException; import com.TwoSeaU.BaData.domain.trade.repository.PostLikesRepository; import com.TwoSeaU.BaData.domain.trade.repository.PostRepository; @@ -95,61 +94,13 @@ private Query getQuery(final String query) { ._toQuery(); } - public CursorPageResponse searchByTitleAndComment(final String userQuery, final String username, final Long cursor, final int size) { - final User user = username == null ? null : userRepository.findByUsername(username) - .orElseThrow(() -> new GeneralException(UserException.USER_NOT_FOUND)); - - final NativeQueryBuilder nativeQueryBuilder = new NativeQueryBuilder() - .withQuery(getQuery(userQuery)) - .withSort( - SortOptions.of( - s -> s.field(f -> f.field("id").order(Desc)) - ) - ) - .withMaxResults(size + 1); - - if (cursor != null) { - nativeQueryBuilder.withSearchAfter(List.of(cursor)); - } - - final SearchHits searchHits = elasticsearchOperations.search( - nativeQueryBuilder.build(), - PostDocument.class, - IndexCoordinates.of(INDEX_NAME) - ); - - final boolean hasNext = searchHits.getSearchHits().size() > size; - - final List> postDocuments = hasNext ? searchHits.getSearchHits().subList(0, size) : searchHits.getSearchHits(); - - final Long nextCursor = postDocuments.isEmpty() ? null : postDocuments.get(postDocuments.size() - 1).getContent().getId(); - - final List postIds = postDocuments.stream() - .map(hit -> hit.getContent().getId()) - .toList(); - - final Map postMap = postRepository.findAllById(postIds).stream() - .collect(Collectors.toMap(Post::getId, post -> post)); + public CursorPageResponse searchByTitleAndComment(final String query, final String username, final Long cursor, final int size) { - final Map postLikesCountMap = postLikesRepository.countByPostIds(postIds); - - final Set likedPostIds = username != null ? postLikesRepository.findLikedPostIdsByUserIdAndPostIds(user.getId(), postIds) : Set.of(); - - final List responseList = postDocuments.stream() - .map(hit -> { - final Long postId = hit.getContent().getId(); - final Post post = postMap.get(postId); - if (post == null) { - throw new GeneralException(TradeException.POST_NOT_FOUND); - } - return PostResponse.from( - post, - postLikesCountMap.getOrDefault(postId, 0), - likedPostIds.contains(postId) - ); - }) - .toList(); + if (query != null && !query.isEmpty()) { + log.info("event-keyword-search, {}", query); + saveSearchHistoryIfUserExists(username, query); + } - return CursorPageResponse.of(responseList, nextCursor, hasNext); + return postRepository.searchPostsByKeyword(query, username, cursor, size); } } diff --git a/src/main/java/com/TwoSeaU/BaData/domain/trade/service/PostService.java b/src/main/java/com/TwoSeaU/BaData/domain/trade/service/PostService.java index d22dd87d..5cdd3569 100644 --- a/src/main/java/com/TwoSeaU/BaData/domain/trade/service/PostService.java +++ b/src/main/java/com/TwoSeaU/BaData/domain/trade/service/PostService.java @@ -33,7 +33,6 @@ import java.time.LocalDate; import java.util.ArrayList; import java.util.List; -import java.util.Random; import java.util.stream.Stream; @Slf4j @@ -55,7 +54,6 @@ public class PostService { private final VectorUtilsPg vectorUtilsPg; private final S3ImageService s3ImageService; private final OCRService ocrService; - private final PostDocumentRepository postDocumentRepository; private final JdbcRepository jdbcRepository; private final PartnerRepository partnerRepository; @@ -130,10 +128,7 @@ public SavePostResponse createGifticonPost(final SaveGifticonPostRequest saveGif ); final Gifticon savedGifticon = gifticonRepository.save(gifticon); - final PostDocument postDocument = PostDocument.from(savedGifticon); - postDocumentRepository.save(postDocument); - //TODO: 여기 jdbcRepository.insertPostVector(savedGifticon.getId(), floatVector); return SavePostResponse.builder() @@ -160,8 +155,6 @@ public SavePostResponse createDataPost(final SaveDataPostRequest saveDataPostReq ); final Data savedData = dataRepository.save(data); - final PostDocument postDocument = PostDocument.from(savedData); - postDocumentRepository.save(postDocument); return SavePostResponse.builder() .postId(savedData.getId()) @@ -262,8 +255,6 @@ public DeletePostResponse deletePost(final Long postId, final String username) { } post.updateIsDeleted(); - final PostDocument postDocument = PostDocument.from(post); - postDocumentRepository.delete(postDocument); return DeletePostResponse.of(postId); } @@ -277,9 +268,6 @@ public SavePostResponse modifyPostData(final Long postId, final UpdateDataPostRe updateDataPostRequest.getTitle() ); - final PostDocument postDocument = PostDocument.from(post); - postDocumentRepository.save(postDocument); - return SavePostResponse.of(post.getId()); } @@ -313,14 +301,14 @@ public SavePostResponse modifyPostGifticon(final Long postId, final UpdateGiftic gifticon.updateFloatVector(floatVector); jdbcRepository.updatePostVector(gifticon.getId(), floatVector); - final PostDocument postDocument = PostDocument.from(gifticon); - postDocumentRepository.save(postDocument); - return SavePostResponse.of(post.getId()); } public String generateGifticons() { - List categories = gifticonCategoryRepository.findAll(); + GifticonCategory category = gifticonCategoryRepository.findById(1L) + .orElseThrow(() -> new GeneralException(TradeException.NOT_FOUND_GIFTICON_CATEGORY)); + + User user = userRepository.findById(2L) .orElseThrow(() -> new GeneralException(UserException.USER_NOT_FOUND)); @@ -335,7 +323,7 @@ public String generateGifticons() { int index = 1; - for (GifticonCategory category : categories) { + //for (GifticonCategory category : categories) { List partners = partnerRepository.findByCategoryId(category.getId()); for (Partner partner : partners) { @@ -377,8 +365,15 @@ public String generateGifticons() { index++; } } + + try { + Thread.sleep(5000); // 0.1초 대기 + } catch (InterruptedException e) { + throw new RuntimeException("Thread was interrupted", e); + } + } - } + //} return "success"; } diff --git a/src/main/java/com/TwoSeaU/BaData/domain/trade/service/TrendingKeywordService.java b/src/main/java/com/TwoSeaU/BaData/domain/trade/service/TrendingKeywordService.java index 96bb584e..e6f1f373 100644 --- a/src/main/java/com/TwoSeaU/BaData/domain/trade/service/TrendingKeywordService.java +++ b/src/main/java/com/TwoSeaU/BaData/domain/trade/service/TrendingKeywordService.java @@ -1,33 +1,13 @@ package com.TwoSeaU.BaData.domain.trade.service; -import co.elastic.clients.elasticsearch._types.Script; -import co.elastic.clients.elasticsearch._types.ScriptLanguage; -import co.elastic.clients.elasticsearch._types.aggregations.Aggregation; -import co.elastic.clients.elasticsearch._types.aggregations.AggregationBuilders; -import co.elastic.clients.elasticsearch._types.aggregations.StringTermsBucket; -import co.elastic.clients.elasticsearch._types.query_dsl.Query; -import co.elastic.clients.elasticsearch._types.query_dsl.QueryBuilders; -import co.elastic.clients.json.JsonData; import com.TwoSeaU.BaData.domain.trade.dto.response.GetTrendingResponse; -import com.TwoSeaU.BaData.domain.trade.entity.SearchHistoryDocument; import com.TwoSeaU.BaData.domain.trade.exception.TradeException; import com.TwoSeaU.BaData.global.response.GeneralException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.data.elasticsearch.client.elc.ElasticsearchAggregations; -import org.springframework.data.elasticsearch.client.elc.NativeQuery; -import org.springframework.data.elasticsearch.client.elc.NativeQueryBuilder; import org.springframework.data.elasticsearch.core.ElasticsearchOperations; -import org.springframework.data.elasticsearch.core.SearchHits; -import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates; import org.springframework.stereotype.Service; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; - @Slf4j @Service @RequiredArgsConstructor @@ -41,7 +21,7 @@ public class TrendingKeywordService { public GetTrendingResponse getTrendingKeyword() { try { - return GetTrendingResponse.of(getRecentTop10Keywords().toArray(String[]::new)); + return GetTrendingResponse.of(getRecentTop10KeywordsNotEs()); } catch (GeneralException e){ throw e; @@ -52,80 +32,11 @@ public GetTrendingResponse getTrendingKeyword() { } } - public List getRecentTop10Keywords() { - NativeQuery searchQuery = getRecentTop10KeywordsNativeQuery(); - - SearchHits searchHits = elasticsearchOperations.search( - searchQuery, - SearchHistoryDocument.class, - IndexCoordinates.of(INDEX_NAME)); - - ElasticsearchAggregations aggregations = (ElasticsearchAggregations)searchHits.getAggregations(); - - if (aggregations == null) { - throw new GeneralException(TradeException.REALTIME_SEARCH_CONTENT_NOT_FOUND); - } - - List topTenBuckets = aggregations.aggregationsAsMap() - .get("top_ten") - .aggregation() - .getAggregate() - .sterms() - .buckets() - .array(); - - List result = new LinkedList<>(); - - topTenBuckets.forEach(topTenBucket -> { - String keyword = topTenBucket.key().stringValue(); - if (keyword != null && !keyword.isEmpty()) { - result.add(keyword); - } - }); - - return result; - } - - private Query createTimeRangeQuery() { - LocalDateTime now = LocalDateTime.now(); - LocalDateTime oneHourAgo = now.minusHours(AGGREGATION_INTERVAL_HOURS); - - return QueryBuilders.range() - .field("@timestamp") - .gte(JsonData.of(oneHourAgo - .atZone(ZoneId.systemDefault()) - .toInstant() - .toEpochMilli())) - .lte(JsonData.of(now - .atZone(ZoneId.systemDefault()) - .toInstant() - .toEpochMilli())) - .build() - ._toQuery(); - } - - private NativeQuery getRecentTop10KeywordsNativeQuery() { - NativeQueryBuilder queryBuilder = new NativeQueryBuilder(); - - Script script = Script.of(scriptBuilder -> scriptBuilder.inline(inlineScriptBuilder -> - inlineScriptBuilder.lang(ScriptLanguage.Painless) - .source(KEYWORD_EXTRACTION_SCRIPT) - .params(Collections.emptyMap()) - )); - - Aggregation agg = AggregationBuilders.terms() - .script(script) - .size(DEFAULT_TOP_COUNT) - .build() - ._toAggregation(); - - Query boolQuery = QueryBuilders.bool() - .must(createTimeRangeQuery()) - .build() - ._toQuery(); - - return queryBuilder.withQuery(boolQuery) - .withAggregation("top_ten", agg) - .build(); + private String[] getRecentTop10KeywordsNotEs() { + return new String[]{ + "스타벅스", "이디야", "투썸플레이스", + "메가커피", "커피빈", "할리스", + "파스쿠찌", "탐앤탐스", "빽다방", + "폴바셋"}; } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 29ccafae..0ef03db1 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -49,10 +49,6 @@ jwt: secret: ${JWT_SECRET_KEY} validationTime: ${JWT_VALIDATION_TIME} -logging: - config: classpath:logback-spring.xml - - fcm: certification: firebase.json diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml deleted file mode 100644 index 4b5f3eb1..00000000 --- a/src/main/resources/logback-spring.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{10} - %msg%n - - - - - - ${LOGSTASH_URL} - - - - - - - - - \ No newline at end of file