Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat/#72] OCR 인증 기능 추가 #74

Merged
merged 2 commits into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ dependencies {
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"

//s3
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'

implementation 'org.json:json:20210307'


}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package carbonneutral.academy.api.controller.ocr;


import carbonneutral.academy.api.controller.ocr.dto.response.PostOcrRes;
import carbonneutral.academy.api.service.ocr.OcrService.OcrService;
import carbonneutral.academy.common.BaseResponse;
import carbonneutral.academy.domain.user.User;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import static carbonneutral.academy.common.code.status.SuccessStatus.OCR_OK;

@Slf4j
@Tag(name = "ocr controller", description = "ocr 관련 API")
@RequiredArgsConstructor
@RestController
@RequestMapping("/api/v1/ocr")
public class OcrController {

private final OcrService ocrService;

@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation(summary = "OCR 이미지 인식 API",description = "OCR 이미지를 인식합니다.")
public BaseResponse<PostOcrRes> ocrImage(@AuthenticationPrincipal User user
, @RequestParam("receipt") MultipartFile receipt) {
return BaseResponse.of(OCR_OK, ocrService.ocrImage(user, receipt));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package carbonneutral.academy.api.controller.ocr.dto.response;

import carbonneutral.academy.domain.use.enums.UseStatus;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
@Builder
@AllArgsConstructor
public class PostOcrRes {

private int useLocationId;
private String useLocationName;
private String useLocationAddress;
private String useLocationImageUrl;
private String useTime;
private int currentPoint;
private int acquiredPoint;
private int userId;
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,5 @@ BaseResponse<PatchReturnRes> returnMultipleTimeContainers(@AuthenticationPrincip
@Validated @RequestBody PatchReturnReq patchReturnReq, @PathVariable("useAt") String useAt) {
return BaseResponse.of(RETURN_SAVE_OK, useService.returnMultipleTimeContainers(user, patchReturnReq, useAt));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public class AuthServiceImpl implements AuthService {
public PostSocialRes socialLogin(String code, SocialType socialType) {
switch (socialType){
case KAKAO: {
GetKakaoRes getKakaoRes = kakaoLoginService.getUserInfo(code);
GetKakaoRes getKakaoRes = kakaoLoginService.getUserInfo(kakaoLoginService.getAccessToken(code));

boolean isRegistered = userJpaRepository.existsByUsernameAndSocialTypeAndState(getKakaoRes.getId(), SocialType.KAKAO, ACTIVE);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
public class KakaoLoginServiceImpl implements KakaoLoginService {



@Value("${spring.security.oauth2.client.provider.kakao.token-uri}")
private String KAKAO_TOKEN_URL;

Expand Down Expand Up @@ -68,6 +69,7 @@ public String getAccessToken(String authorizationCode) {
}
}


@Override
public GetKakaoRes getUserInfo(String accessToken){
RestTemplate restTemplate = new RestTemplate();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package carbonneutral.academy.api.service.ocr.OcrService;

import carbonneutral.academy.api.controller.ocr.dto.response.PostOcrRes;
import carbonneutral.academy.domain.user.User;
import org.springframework.web.multipart.MultipartFile;

public interface OcrService {

PostOcrRes ocrImage(User user, MultipartFile receipt);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package carbonneutral.academy.api.service.ocr.OcrService;

import carbonneutral.academy.api.controller.ocr.dto.response.PostOcrRes;
import carbonneutral.academy.api.converter.time.TimeConverter;
import carbonneutral.academy.api.converter.use.UseConverter;
import carbonneutral.academy.api.service.use.UseService;
import carbonneutral.academy.api.service.user.UserService;
import carbonneutral.academy.common.exceptions.BaseException;
import carbonneutral.academy.domain.location.Location;
import carbonneutral.academy.domain.location.repository.LocationJpaRepository;
import carbonneutral.academy.domain.point.Point;
import carbonneutral.academy.domain.point.repository.PointJpaRepository;
import carbonneutral.academy.domain.use.Use;
import carbonneutral.academy.domain.use.enums.UseStatus;
import carbonneutral.academy.domain.use.repository.UseJpaRepository;
import carbonneutral.academy.domain.use_statistics.repository.UseStatisticsJpaRepository;
import carbonneutral.academy.domain.user.User;
import carbonneutral.academy.utils.clova.ClovaOCR;
import carbonneutral.academy.utils.s3.S3Provider;
import carbonneutral.academy.utils.s3.dto.S3UploadRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDateTime;

import static carbonneutral.academy.common.BaseEntity.State.ACTIVE;
import static carbonneutral.academy.common.code.status.ErrorStatus.*;

@Service
@RequiredArgsConstructor
@Slf4j
@Transactional
public class OcrServiceImpl implements OcrService {

private final S3Provider s3Provider;
private final ClovaOCR clovaOCR;
private final UseJpaRepository useJpaRepository;
private final LocationJpaRepository locationJpaRepository;
private final UseStatisticsJpaRepository useStatisticsJpaRepository;
private final PointJpaRepository pointJpaRepository;

@Override
public PostOcrRes ocrImage(User user, MultipartFile receipt) {
String receiptUrl = s3Provider.multipartFileUpload(receipt, S3UploadRequest.builder().userId(user.getId()).dirName("receipt").build());
log.info("receiptUrl : {}", receiptUrl);
String result = clovaOCR.OCRParse(receiptUrl);
//결과에 컵 할인 없으면 예외 던지기
if (!result.contains("컵 할인")) {
throw new BaseException(NOT_FIND_CUP_DISCOUNT);
}
Location location = locationJpaRepository.findByIdAndState(3, ACTIVE)
.orElseThrow(() -> new BaseException(NOT_FIND_LOCATION));
Use use = Use.builder()
.useAt(LocalDateTime.now())
.point(100)
.returnTime(LocalDateTime.now())
.multiUseContainerId(5)
.rentalLocation(location)
.returnLocation(location)
.status(UseStatus.RETURNED)
.user(user)
.build();
useJpaRepository.save(use);
Point point = pointJpaRepository.findByUserId(user.getId()).orElseThrow(() -> new BaseException(NOT_FIND_POINT));
point.addPoint(100);
useStatisticsJpaRepository.findById(user.getId()).orElseThrow(() -> new BaseException(NOT_FIND_USE_STATISTICS)).addTotalUseCount();
useStatisticsJpaRepository.findById(user.getId()).orElseThrow(() -> new BaseException(NOT_FIND_USE_STATISTICS)).addTotalReturnCount();
return PostOcrRes.builder()
.useTime(TimeConverter.toFormattedDate(use.getUseAt()))
.currentPoint(point.getAccumulatedPoint() - point.getUtilizedPoint())
.acquiredPoint(use.getPoint())
.useLocationId(location.getId())
.useLocationName(location.getName())
.useLocationAddress(location.getAddress())
.useLocationImageUrl(location.getImageUrl())
.userId(user.getId())
.build();
}



}
Original file line number Diff line number Diff line change
Expand Up @@ -124,12 +124,9 @@ public PatchReturnRes returnMultipleTimeContainers(User user, PatchReturnReq pat
log.info("use : {}", use.getUseAt());
Location returnLocation = locationJpaRepository.findByIdAndState(patchReturnReq.getReturnLocationId(), ACTIVE)
.orElseThrow(() -> new BaseException(NOT_FIND_LOCATION));
log.info("returnLocation : {}", returnLocation.getId());
log.info("returnLocation : {} {}", returnLocation.getLocationType(), returnLocation.isReturned());
if(!(returnLocation.isReturned()) && !(returnLocation.getLocationType().equals(LocationType.RETURN))) {
throw new BaseException(NOT_RETURN_LOCATION);
}
log.info("returnLocation여기니? : {}", returnLocation.getId());
if(!locationContainerJpaRepository.findByLocation_Id(returnLocation.getId())
.stream()
.map(locationContainer -> locationContainer.getMultiUseContainer().getId())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ public enum ErrorStatus implements BaseErrorCode {
INVALID_PAGE(HttpStatus.BAD_REQUEST, "PAGE4000", "페이지는 0 이상이어야 합니다."),
//사이즈는 무조건 10
INVALID_SIZE_10(HttpStatus.BAD_REQUEST, "PAGE4001", "사이즈는 10이어야 합니다."),
FILE_CONVERT(HttpStatus.BAD_REQUEST, "FILE4001", "파일 변환 실패."),
S3_UPLOAD(HttpStatus.BAD_REQUEST, "S3UPLOAD4001", "S3 파일 업로드 실패."),
NOT_FIND_CUP_DISCOUNT(HttpStatus.BAD_REQUEST, "CUP_DISCOUNT4000", "텀블러를 사용한 할인 내역이 없습니다."),


/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ public enum SuccessStatus implements BaseCode {
IN_USE_OK(HttpStatus.OK, "USE2002", "이용중인 다회용기 단일 조회 성공" ),
RETURN_SAVE_OK(HttpStatus.CREATED, "USE2003", "반납 성공"),
GET_POINT_OK(HttpStatus.OK, "POINT2000", "포인트 조회 성공"),
LOCATION_OK(HttpStatus.OK, "LOCATION2000", "장소 조회 성공");
LOCATION_OK(HttpStatus.OK, "LOCATION2000", "장소 조회 성공"),
OCR_OK(HttpStatus.OK, "OCR2000", "텀블러 이용 성공");

private final HttpStatus httpStatus;
private final String code;
Expand Down
32 changes: 32 additions & 0 deletions src/main/java/carbonneutral/academy/common/config/S3Config.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package carbonneutral.academy.common.config;

import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class S3Config {
@Value("${cloud.aws.region.static}")
private String region;

@Value("${cloud.aws.credentials.access-key}")
private String accessKey;

@Value("${cloud.aws.credentials.secret-key}")
private String secretKey;

@Bean
public AmazonS3Client amazonS3Client() {
BasicAWSCredentials awsCredentials = new BasicAWSCredentials(accessKey, secretKey);
return (AmazonS3Client) AmazonS3ClientBuilder
.standard()
.withCredentials(new AWSStaticCredentialsProvider(awsCredentials))
.withRegion(region)
.build();
}
}
Loading
Loading