diff --git a/.github/workflows/cicd-ec2.yml b/.github/workflows/cicd-ec2.yml index 4dadd61f..b5fd574b 100644 --- a/.github/workflows/cicd-ec2.yml +++ b/.github/workflows/cicd-ec2.yml @@ -11,6 +11,7 @@ env: AWS_REGION: ap-northeast-2 S3_BUCKET_NAME: genti-deploy CODE_DEPLOY_APPLICATION_NAME: genti + CODE_DEPLOY_APPLICATION_NAME_STAGING: genti-dev permissions: contents: read @@ -105,13 +106,13 @@ jobs: run: | chmod +x ./gradlew ./gradlew clean build -x test + - - - name: Get Github action IP - if: contains(github.ref, 'staging') - id: ip - uses: haythem/public-ip@v1.2 - + # - name: Get Github action IP + # if: contains(github.ref, 'staging') + # id: ip + # uses: haythem/public-ip@v1.2 + # - name: Setting environment variables run: | echo "AWS_DEFAULT_REGION=ap-northeast-2" >> $GITHUB_ENV @@ -123,18 +124,18 @@ jobs: aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ap-northeast-2 - - name: Add Github Actions IP to Security group - if: contains(github.ref, 'staging') - run: | - aws ec2 authorize-security-group-ingress --group-id ${{ secrets.AWS_SG_ID }} --protocol tcp --port 22 --cidr ${{ steps.ip.outputs.ipv4 }}/32 - env: - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - AWS_DEFAULT_REGION: ap-northeast-2 + # - name: Add Github Actions IP to Security group + # if: contains(github.ref, 'staging') + # run: | + # aws ec2 authorize-security-group-ingress --group-id ${{ secrets.AWS_SG_ID }} --protocol tcp --port 22 --cidr ${{ steps.ip.outputs.ipv4 }}/32 + # env: + # AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + # AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + # AWS_DEFAULT_REGION: ap-northeast-2 - name: Login to aws ECR - if: contains(github.ref, 'main') + if: contains(github.ref, 'staging') || contains(github.ref, 'main') id: login-ecr uses: aws-actions/amazon-ecr-login@v1 @@ -148,6 +149,15 @@ jobs: docker build -f ./Dockerfile_deploy -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG + - name: Build, tag, and push image to aws ECR + if: contains(github.ref, 'staging') + env: + ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} + ECR_REPOSITORY: genti-staging + IMAGE_TAG: latest + run: | + docker build -f ./Dockerfile_staging -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . + docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG - name: Upload docker-compose, appspec, afterInstall file to S3 if: contains(github.ref, 'main') @@ -169,16 +179,36 @@ jobs: # Clean up the temporary directory rm -rf temp_dir - - name: Upload docker compose file to staging server + - name: Upload docker-compose, appspec, afterInstall file to S3 if: contains(github.ref, 'staging') - uses: appleboy/scp-action@master - with: - host: ${{ secrets.HOST_STAGING }} - username: ubuntu - key: ${{ secrets.EC2_KEY }} - port: 22 - source: "./docker/staging/*" - target: "/home/ubuntu/workspace/" + run: | + # Create a temporary directory for the zip contents + mkdir -p temp_dir/scripts + cp -r ./scripts/* temp_dir/scripts/ + cp appspec.yml temp_dir/ + cp ./docker/staging/docker-compose.yml temp_dir/docker-compose.yml + + # Navigate to the temporary directory and create the zip file + cd temp_dir + zip -r ../$GITHUB_SHA.zip ./* + + # Move back to the initial directory and upload the zip file to S3 + cd .. + aws s3 cp --region ap-northeast-2 ./$GITHUB_SHA.zip s3://$S3_BUCKET_NAME + + # Clean up the temporary directory + rm -rf temp_dir + + # - name: Upload docker compose file to staging server + # if: contains(github.ref, 'staging') + # uses: appleboy/scp-action@master + # with: + # host: ${{ secrets.HOST_STAGING }} + # username: ubuntu + # key: ${{ secrets.EC2_KEY }} + # port: 22 + # source: "./docker/staging/*" + # target: "/home/ubuntu/workspace/" # docker build & push to deploy server - name: Deploy to EC2 with CodeDeploy @@ -189,38 +219,45 @@ jobs: --deployment-group-name ${{ secrets.CODE_DEPLOY_DEPLOYMENT_GROUP_NAME }} \ --s3-location bucket=$S3_BUCKET_NAME,key=$GITHUB_SHA.zip,bundleType=zip - # docker build & push to staging - - name: Docker build & push to staging + - name: Staging Deploy to EC2 with CodeDeploy if: contains(github.ref, 'staging') run: | - docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} - docker build -f Dockerfile_staging -t ${{ secrets.DOCKER_USERNAME }}/genti-staging . - docker push ${{ secrets.DOCKER_USERNAME }}/genti-staging + aws deploy create-deployment \ + --application-name ${{ env.CODE_DEPLOY_APPLICATION_NAME_STAGING }} \ + --deployment-group-name genti-tg-staging \ + --s3-location bucket=$S3_BUCKET_NAME,key=$GITHUB_SHA.zip,bundleType=zip + # docker build & push to staging + # - name: Docker build & push to staging + # if: contains(github.ref, 'staging') + # run: | + # docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} + # docker build -f Dockerfile_staging -t ${{ secrets.DOCKER_USERNAME }}/genti-staging . + # docker push ${{ secrets.DOCKER_USERNAME }}/genti-staging ## deploy to staging server - - name: Deploy to staging server - uses: appleboy/ssh-action@master - id: deploy-staging - if: contains(github.ref, 'staging') - with: - host: ${{ secrets.HOST_STAGING }} # EC2 퍼블릭 IPv4 DNS - username: ubuntu - password: ${{ secrets.PASSWORD }} - port: 22 - key: ${{ secrets.EC2_KEY }} - script: | - sudo docker ps - cd /home/ubuntu/workspace/docker/staging - docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} - sudo docker pull ${{ secrets.DOCKER_USERNAME }}/genti-staging - sudo docker-compose up -d - sudo docker image prune -f - - - name: delete github actions ip from aws security group - if: contains(github.ref, 'staging') - run: | - aws ec2 revoke-security-group-ingress --group-id ${{ secrets.AWS_SG_ID }} --protocol tcp --port 22 --cidr ${{ steps.ip.outputs.ipv4 }}/32 - env: - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - AWS_DEFAULT_REGION: ap-northeast-2 + # - name: Deploy to staging server + # uses: appleboy/ssh-action@master + # id: deploy-staging + # if: contains(github.ref, 'staging') + # with: + # host: ${{ secrets.HOST_STAGING }} # EC2 퍼블릭 IPv4 DNS + # username: ubuntu + # password: ${{ secrets.PASSWORD }} + # port: 22 + # key: ${{ secrets.EC2_KEY }} + # script: | + # sudo docker ps + # cd /home/ubuntu/workspace/docker/staging + # docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} + # sudo docker pull ${{ secrets.DOCKER_USERNAME }}/genti-staging + # sudo docker-compose up -d + # sudo docker image prune -f + +# - name: delete github actions ip from aws security group +# if: contains(github.ref, 'staging') +# run: | +# aws ec2 revoke-security-group-ingress --group-id ${{ secrets.AWS_SG_ID }} --protocol tcp --port 22 --cidr ${{ steps.ip.outputs.ipv4 }}/32 +# env: +# AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} +# AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} +# AWS_DEFAULT_REGION: ap-northeast-2 diff --git a/.gitignore b/.gitignore index c065f734..dae41ced 100644 --- a/.gitignore +++ b/.gitignore @@ -66,3 +66,4 @@ firebase-genti.json AuthKey_ZRZMQQX883.p8 /genti-api/src/main/resources/static/swagger.json +update-github-secret.sh \ No newline at end of file diff --git a/genti-api/src/main/java/com/gt/genti/openchat/api/AdminOpenChatApi.java b/genti-api/src/main/java/com/gt/genti/openchat/api/AdminOpenChatApi.java new file mode 100644 index 00000000..3f7912de --- /dev/null +++ b/genti-api/src/main/java/com/gt/genti/openchat/api/AdminOpenChatApi.java @@ -0,0 +1,25 @@ +package com.gt.genti.openchat.api; + +import com.gt.genti.openchat.model.OpenChat; +import com.gt.genti.openchat.model.OpenChatType; +import com.gt.genti.response.GentiResponse.ApiResult; +import com.gt.genti.swagger.AuthorizedAdmin; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestParam; + +@AuthorizedAdmin +@Tag(name = "[AdminOpenChatController] 어드민의 오픈채팅방 정보 수정", description = "카카오톡 오픈채팅방 정보 수정") +public interface AdminOpenChatApi { + + @Operation(summary = "오픈채팅방 정보 수정", description = "오픈채팅방의 인원 수를 수정합니다.") + ResponseEntity> modifyOpenChatInfo( + @RequestHeader(value = "Admin-Secret-Key") String secretKey, + @PathVariable(value = "type") String type, + @RequestParam(value = "count") Long count + ); + +} diff --git a/genti-api/src/main/java/com/gt/genti/openchat/api/UserOpenChatApi.java b/genti-api/src/main/java/com/gt/genti/openchat/api/UserOpenChatApi.java new file mode 100644 index 00000000..d898c88d --- /dev/null +++ b/genti-api/src/main/java/com/gt/genti/openchat/api/UserOpenChatApi.java @@ -0,0 +1,20 @@ +package com.gt.genti.openchat.api; + +import com.gt.genti.openchat.dto.response.OpenChatInfoResponseDto; +import com.gt.genti.response.GentiResponse.ApiResult; +import com.gt.genti.swagger.AuthorizedUser; +import com.gt.genti.user.model.AuthUser; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.http.ResponseEntity; + +@AuthorizedUser +@Tag(name = "[UserOpenChatController] 유저의 오픈채팅방 요청", description = "카카오톡 오픈채팅방 정보 요청") +public interface UserOpenChatApi { + + @Operation(summary = "오픈채팅방 정보 조회", description = "오픈채팅방 url과 인원 수를 조회합니다.") + ResponseEntity> getOpenChatUrl( + @AuthUser Long userId + ); + +} diff --git a/genti-api/src/main/java/com/gt/genti/openchat/controller/AdminOpenChatController.java b/genti-api/src/main/java/com/gt/genti/openchat/controller/AdminOpenChatController.java new file mode 100644 index 00000000..2663e584 --- /dev/null +++ b/genti-api/src/main/java/com/gt/genti/openchat/controller/AdminOpenChatController.java @@ -0,0 +1,39 @@ +package com.gt.genti.openchat.controller; + +import com.gt.genti.error.ExpectedException; +import com.gt.genti.error.ResponseCode; +import com.gt.genti.openchat.api.AdminOpenChatApi; +import com.gt.genti.openchat.model.OpenChat; +import com.gt.genti.openchat.model.OpenChatType; +import com.gt.genti.openchat.service.OpenChatService; +import com.gt.genti.response.GentiResponse; +import com.gt.genti.response.GentiResponse.ApiResult; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/v1/open-chat") +@RequiredArgsConstructor +public class AdminOpenChatController implements AdminOpenChatApi { + + private final OpenChatService openChatService; + + @Value("${openchat.admin-secret-key}") + private String ADMIN_SECRET_KEY; + + @PatchMapping("/{type}") + public ResponseEntity> modifyOpenChatInfo( + @RequestHeader(value = "Admin-Secret-Key") String adminSecretKey, + @PathVariable(value = "type") String type, + @RequestParam(value = "count") Long count + ){ + OpenChatType openChatType = OpenChatType.fromString(type); + if (!ADMIN_SECRET_KEY.equals(adminSecretKey)) { + throw ExpectedException.withLogging(ResponseCode.InvalidOpenChatSecretKey); + } else{ + return GentiResponse.success(openChatService.modifyOpenChatInfo(openChatType, count)); + } + } +} diff --git a/genti-api/src/main/java/com/gt/genti/openchat/controller/UserOpenChatController.java b/genti-api/src/main/java/com/gt/genti/openchat/controller/UserOpenChatController.java new file mode 100644 index 00000000..df1c34f6 --- /dev/null +++ b/genti-api/src/main/java/com/gt/genti/openchat/controller/UserOpenChatController.java @@ -0,0 +1,29 @@ +package com.gt.genti.openchat.controller; + +import com.gt.genti.openchat.api.UserOpenChatApi; +import com.gt.genti.openchat.dto.response.OpenChatInfoResponseDto; +import com.gt.genti.openchat.service.OpenChatService; +import com.gt.genti.response.GentiResponse; +import com.gt.genti.response.GentiResponse.ApiResult; +import com.gt.genti.user.model.AuthUser; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/v1/open-chat") +@RequiredArgsConstructor +public class UserOpenChatController implements UserOpenChatApi { + + private final OpenChatService openChatService; + + @GetMapping + public ResponseEntity> getOpenChatUrl( + @AuthUser Long userId + ) { + return GentiResponse.success(openChatService.getOpenChatUrl(userId)); + } + +} diff --git a/genti-api/src/main/java/com/gt/genti/openchat/dto/response/OpenChatInfoResponseDto.java b/genti-api/src/main/java/com/gt/genti/openchat/dto/response/OpenChatInfoResponseDto.java new file mode 100644 index 00000000..5243f4ae --- /dev/null +++ b/genti-api/src/main/java/com/gt/genti/openchat/dto/response/OpenChatInfoResponseDto.java @@ -0,0 +1,29 @@ +package com.gt.genti.openchat.dto.response; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Schema(name = "[OpenChat][User] 오픈채팅방 정보 응답 dto") +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class OpenChatInfoResponseDto { + + @Schema(description = "오픈 채팅방 대상 여부", example = "true") + Boolean accessible; + + @Schema(description = "오픈 채팅방 사람 수") + Long count; + + @Schema(description = "오픈 채팅방 url") + String url; + + @Builder + public OpenChatInfoResponseDto(Boolean accessible, Long count, String url) { + this.accessible = accessible; + this.count = count; + this.url = url; + } +} diff --git a/genti-api/src/main/java/com/gt/genti/openchat/service/OpenChatService.java b/genti-api/src/main/java/com/gt/genti/openchat/service/OpenChatService.java new file mode 100644 index 00000000..9dfb57ab --- /dev/null +++ b/genti-api/src/main/java/com/gt/genti/openchat/service/OpenChatService.java @@ -0,0 +1,80 @@ +package com.gt.genti.openchat.service; + +import com.gt.genti.error.ExpectedException; +import com.gt.genti.error.ResponseCode; +import com.gt.genti.openchat.dto.response.OpenChatInfoResponseDto; +import com.gt.genti.openchat.model.OpenChat; +import com.gt.genti.openchat.model.OpenChatType; +import com.gt.genti.openchat.repository.OpenChatRepository; +import com.gt.genti.user.model.User; +import com.gt.genti.user.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import static com.gt.genti.openchat.model.OpenChatType.*; + +@Service +@Transactional +@RequiredArgsConstructor +@Slf4j +public class OpenChatService { + private final OpenChatRepository openChatRepository; + private final UserRepository userRepository; + + public OpenChatInfoResponseDto getOpenChatUrl(Long userId){ + User foundUser = getUserById(userId); + OpenChatType openChatType = getOpenChatType(foundUser); + + if(openChatType == YB || openChatType == OB) { + OpenChat openChat = openChatRepository.findByType(openChatType); + return OpenChatInfoResponseDto.builder() + .accessible(true) + .url(openChat.getUrl()) + .count(openChat.getCount()) + .build(); + } else { + return OpenChatInfoResponseDto.builder() + .accessible(false) + .url(null) + .count(null) + .build(); + } + } + + public OpenChat modifyOpenChatInfo(OpenChatType type, Long count){ + OpenChat openChat = getOpenChatInfoByType(type); + openChat.updateCount(count); + return openChatRepository.save(openChat); + } + + private OpenChat getOpenChatInfoByType(OpenChatType type){ + OpenChat openChat = openChatRepository.findByType(type); + if (openChat == null) { + throw ExpectedException.withLogging(ResponseCode.OpenChatNotFound, type); + } + return openChat; + } + + private User getUserById(Long userId) { + return userRepository.findById(userId) + .orElseThrow(() -> ExpectedException.withLogging(ResponseCode.UserNotFound, userId)); + } + + private OpenChatType getOpenChatType(User user) { + try{ + int userBirthYear = Integer.parseInt(user.getBirthYear()); + if (userBirthYear >= 2001) { + return YB; + } else if (userBirthYear <= 1974) { + return OB; + } else { + return null; + } + } catch (NumberFormatException | NullPointerException e) { + log.info("사용자 생년의 숫자 변환 시 문제가 발생 : {}", e.getMessage(), e); + return null; + } + } +} diff --git a/genti-auth/src/main/java/com/gt/genti/config/SecurityConfig.java b/genti-auth/src/main/java/com/gt/genti/config/SecurityConfig.java index d0b881c0..c5b9b17d 100644 --- a/genti-auth/src/main/java/com/gt/genti/config/SecurityConfig.java +++ b/genti-auth/src/main/java/com/gt/genti/config/SecurityConfig.java @@ -47,10 +47,10 @@ public CorsConfigurationSource corsConfigurationSource() { config.setAllowedOriginPatterns(List.of("*")); - config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS")); + config.setAllowedMethods(List.of("GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS")); config.addExposedHeader("Access-Token"); config.addExposedHeader("Refresh-Token"); - config.setAllowedHeaders(List.of("Authorization", "Cache-Control", "Content-Type")); + config.setAllowedHeaders(List.of("Authorization", "Cache-Control", "Content-Type", "Admin-Secret-Key")); config.setAllowCredentials(true); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); diff --git a/genti-common/src/main/java/com/gt/genti/constants/ErrorConstants.java b/genti-common/src/main/java/com/gt/genti/constants/ErrorConstants.java index fc8074e0..c62ac4ac 100644 --- a/genti-common/src/main/java/com/gt/genti/constants/ErrorConstants.java +++ b/genti-common/src/main/java/com/gt/genti/constants/ErrorConstants.java @@ -20,6 +20,7 @@ public class ErrorConstants { private static final String OAUTH = "OAUTH"; private static final String SERVER = "SERVER"; private static final String FCM = "FCM"; + private static final String OPENCHAT = "OPENCHAT"; private static String CODE(String type, int seq) { return type + String.format("-%05d", seq); @@ -68,6 +69,7 @@ private static String CODE(String type, int seq) { public static final String WithDrawnUser = CODE(USER, 5); public static final String UserNotLoggedIn = CODE(USER, 6); public static final String UserAlreadySignedUp = CODE(USER, 7); + public static final String InvalidBirthYearFormat = CODE(USER, 8); public static final String CreatorNotFound = CODE(CREATOR, 1); @@ -114,4 +116,7 @@ private static String CODE(String type, int seq) { public static final String FCM_GOOGLE_REQUEST_TOKEN_ERROR = CODE(FCM, 3); public static final String FCM_CONNECT_ERROR = CODE(FCM, 4); + public static final String InvalidOpenChatSecretKey = CODE(OPENCHAT, 1); + public static final String OpenChatNotFound = CODE(OPENCHAT, 2); + } diff --git a/genti-common/src/main/java/com/gt/genti/constants/WhiteListConstants.java b/genti-common/src/main/java/com/gt/genti/constants/WhiteListConstants.java index 16613727..54a4af64 100644 --- a/genti-common/src/main/java/com/gt/genti/constants/WhiteListConstants.java +++ b/genti-common/src/main/java/com/gt/genti/constants/WhiteListConstants.java @@ -40,7 +40,8 @@ void postConstruct() { "/api-docs/**", "/v3/api-docs/**", "/h2-console/**", - "/h2-console" + "/h2-console", + "/api/v1/open-chat/*" )); private List securityWhiteList = new ArrayList<>(List.of( @@ -56,6 +57,7 @@ void postConstruct() { "/api-docs/**", "/v3/api-docs/**", "/h2-console/**", - "/h2-console" + "/h2-console", + "/api/v1/open-chat/*" )); } diff --git a/genti-common/src/main/java/com/gt/genti/error/ResponseCode.java b/genti-common/src/main/java/com/gt/genti/error/ResponseCode.java index b7c2ac24..e46d555c 100644 --- a/genti-common/src/main/java/com/gt/genti/error/ResponseCode.java +++ b/genti-common/src/main/java/com/gt/genti/error/ResponseCode.java @@ -131,6 +131,7 @@ public enum ResponseCode { UserNotFoundByEmail(ErrorConstants.UserNotFound, HttpStatus.NOT_FOUND, false, "존재하지 않는 사용자입니다. 찾은 email: [%s]"), UserDeactivated(ErrorConstants.UserDeactivated, HttpStatus.BAD_REQUEST, false, "비활성화된 계정입니다."), UserAlreadySignedUp(ErrorConstants.UserAlreadySignedUp, HttpStatus.BAD_REQUEST, false, "이미 회원가입 처리가 완료된 유저입니다."), + InvalidBirthYearFormat(ErrorConstants.InvalidBirthYearFormat, BAD_REQUEST, false, "올바르지 않은 생년 형식입니다."), /** * Creator @@ -207,7 +208,15 @@ public enum ResponseCode { FCM_GOOGLE_REQUEST_TOKEN_ERROR(ErrorConstants.FCM_GOOGLE_REQUEST_TOKEN_ERROR, INTERNAL_SERVER_ERROR, false, "파이어베이스 서버 접속 전 구글 통신 오류"), FCM_CONNECT_ERROR(ErrorConstants.FCM_CONNECT_ERROR, INTERNAL_SERVER_ERROR, false, - "파이어베이스 서버 통신 오류 : %s"); + "파이어베이스 서버 통신 오류 : %s"), + + /** + * OpenChat + */ + + InvalidOpenChatSecretKey(ErrorConstants.InvalidOpenChatSecretKey, BAD_REQUEST, false, "유효하지 않은 시크릿 키입니다."), + OpenChatNotFound(ErrorConstants.OpenChatNotFound, HttpStatus.NOT_FOUND, false, "존재하지 않는 오픈채팅방입니다. 찾은 오픈채팅방 type : [%s]"); + private final String errorCode; private final HttpStatusCode httpStatusCode; diff --git a/genti-domain/src/main/java/com/gt/genti/common/converter/OpenChatTypeConverter.java b/genti-domain/src/main/java/com/gt/genti/common/converter/OpenChatTypeConverter.java new file mode 100644 index 00000000..507835bc --- /dev/null +++ b/genti-domain/src/main/java/com/gt/genti/common/converter/OpenChatTypeConverter.java @@ -0,0 +1,13 @@ +package com.gt.genti.common.converter; + +import com.gt.genti.openchat.model.OpenChatType; +import jakarta.persistence.Converter; + +@Converter +public class OpenChatTypeConverter extends DefaultEnumDBConverter { + + public OpenChatTypeConverter() { + super(OpenChatType.class); + } + +} \ No newline at end of file diff --git a/genti-domain/src/main/java/com/gt/genti/openchat/model/OpenChat.java b/genti-domain/src/main/java/com/gt/genti/openchat/model/OpenChat.java new file mode 100644 index 00000000..8b1cb927 --- /dev/null +++ b/genti-domain/src/main/java/com/gt/genti/openchat/model/OpenChat.java @@ -0,0 +1,38 @@ +package com.gt.genti.openchat.model; + +import com.gt.genti.common.converter.OpenChatTypeConverter; +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Table(name = "openchat") +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class OpenChat { + + @Id + @Column(name = "type", length = 2) + @Enumerated(EnumType.STRING) + private OpenChatType type; + + @Column(name = "count") + private Long count; + + @Column(name = "url") + private String url; + + @Builder + public OpenChat(OpenChatType type, Long count, String url) { + this.type = type; + this.count = count; + this.url = url; + } + + public void updateCount(Long count) { + this.count = count; + } + +} diff --git a/genti-domain/src/main/java/com/gt/genti/openchat/model/OpenChatType.java b/genti-domain/src/main/java/com/gt/genti/openchat/model/OpenChatType.java new file mode 100644 index 00000000..7007f2d2 --- /dev/null +++ b/genti-domain/src/main/java/com/gt/genti/openchat/model/OpenChatType.java @@ -0,0 +1,27 @@ +package com.gt.genti.openchat.model; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.gt.genti.common.ConvertableEnum; +import com.gt.genti.common.EnumUtil; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum OpenChatType implements ConvertableEnum { + OB("OB", "어린층"), + YB("YB", "중년층"); + + private final String stringValue; + private final String response; + + @JsonCreator + public static OpenChatType fromString(String value) { + return EnumUtil.stringToEnum(OpenChatType.class, value); + } + + @Override + public & ConvertableEnum> E getNullValue() { + return null; + } +} diff --git a/genti-domain/src/main/java/com/gt/genti/openchat/repository/OpenChatRepository.java b/genti-domain/src/main/java/com/gt/genti/openchat/repository/OpenChatRepository.java new file mode 100644 index 00000000..582b0892 --- /dev/null +++ b/genti-domain/src/main/java/com/gt/genti/openchat/repository/OpenChatRepository.java @@ -0,0 +1,9 @@ +package com.gt.genti.openchat.repository; + +import com.gt.genti.openchat.model.OpenChat; +import com.gt.genti.openchat.model.OpenChatType; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface OpenChatRepository extends JpaRepository { + OpenChat findByType(OpenChatType type); +}