Skip to content

Commit

Permalink
Merge pull request #50 from YAPP-Github/feature/MAFOO-26
Browse files Browse the repository at this point in the history
[MAFOO-26] feat: 포토그레이 벤더 추가 및 벤더별 Parser 함수 나누기
  • Loading branch information
gmkim20713 authored Jul 24, 2024
2 parents 5ea42f5 + 55134f4 commit 0508c7f
Show file tree
Hide file tree
Showing 10 changed files with 273 additions and 128 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public enum BrandType {
HARU_FILM(Pattern.compile("http://haru\\d+\\.mx\\d+\\.co\\.kr/.*")),
DONT_LOOK_UP(Pattern.compile("https://x\\.dontlxxkup\\.kr/.*")),
MY_FOUR_CUT(Pattern.compile("https://firebasestorage\\.googleapis\\.com:443/v0/b/my4ccu\\.appspot\\.com/.*")),
PHOTOGRAY(Pattern.compile("https://pgshort\\.aprd\\.io/.*")),
;

private final Pattern urlPattern;
Expand Down
145 changes: 17 additions & 128 deletions photo-service/src/main/java/kr/mafoo/photo/service/QrService.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,155 +2,44 @@

import kr.mafoo.photo.domain.BrandType;
import kr.mafoo.photo.exception.PhotoBrandNotExistsException;
import kr.mafoo.photo.exception.PhotoQrUrlExpiredException;
import kr.mafoo.photo.exception.RedirectUriNotFoundException;
import kr.mafoo.photo.service.dto.FileDto;
import kr.mafoo.photo.service.vendors.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;

@Slf4j
@RequiredArgsConstructor
@Service
public class QrService {

private final WebClient externalWebClient;
private final LifeFourCutsQrVendor lifeFourCutsQrVendor;
private final PhotoismQrVendor photoismQrVendor;
private final DontLookUpQrVendor dontLookUpQrVendor;
private final HaruFilmQrVendor haruFilmQrVendor;
private final MyFourCutQrVendor myFourCutQrVendor;
private final PhotoGrayQrVendor photoGrayQrVendor;


public Mono<FileDto> getFileFromQrUrl(String qrUrl) {
BrandType brandType = Optional.ofNullable(BrandType.matchBrandType(qrUrl))
.orElseThrow(PhotoBrandNotExistsException::new);

return switch (brandType) {
case LIFE_FOUR_CUTS -> createFileDto(brandType, getLifeFourCutsFiles(qrUrl));
case PHOTOISM -> createFileDto(brandType, getPhotoismFiles(qrUrl));
case HARU_FILM -> createFileDto(brandType, getHaruFilmFiles(qrUrl));
case DONT_LOOK_UP -> createFileDto(brandType, getDontLookUpFiles(qrUrl));
case MY_FOUR_CUT -> createFileDto(brandType, getMyFourCutFiles(qrUrl));
QrVendor qrVendor = switch (brandType) {
case LIFE_FOUR_CUTS -> lifeFourCutsQrVendor;
case PHOTOISM -> photoismQrVendor;
case HARU_FILM -> haruFilmQrVendor;
case DONT_LOOK_UP -> dontLookUpQrVendor;
case MY_FOUR_CUT -> myFourCutQrVendor;
case PHOTOGRAY -> photoGrayQrVendor;
};

return createFileDto(brandType, qrVendor.extractImageFromQrUrl(qrUrl));
}

private Mono<FileDto> createFileDto(BrandType brandType, Mono<byte[]> fileMono) {
return fileMono.map(file -> new FileDto(brandType, file));
}

private Mono<byte[]> getLifeFourCutsFiles(String qrUrl) {

return getRedirectUri(qrUrl)
.flatMap(redirectUri -> {
String imageUrl = extractValueFromUrl(redirectUri, "path=")[1].replace("index.html", "image.jpg");

// TODO : 추후 비디오 URL 추가 예정
// String videoUrl = redirectUri.toString().replace("index.html", "video.mp4");

return getFileAsByte(imageUrl);
})
.onErrorMap(e -> new PhotoQrUrlExpiredException());
}

private Mono<byte[]> getPhotoismFiles(String qrUrl) {
return getRedirectUri(qrUrl)
.flatMap(redirectUri -> {
String uid = extractValueFromUrl(redirectUri, "u=")[1];

return externalWebClient
.post()
.uri("https://cmsapi.seobuk.kr/v1/etc/seq/resource")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(Map.of("uid", uid))
.retrieve()
.bodyToMono(LinkedHashMap.class)
.flatMap(responseBody -> {
LinkedHashMap<String, Object> content = (LinkedHashMap<String, Object>) responseBody.get("content");
LinkedHashMap<String, Object> fileInfo = (LinkedHashMap<String, Object>) content.get("fileInfo");
LinkedHashMap<String, Object> picFile = (LinkedHashMap<String, Object>) fileInfo.get("picFile");
String imageUrl = (String) picFile.get("path");

return getFileAsByte(imageUrl);
});
})
.onErrorMap(e -> {
e.printStackTrace();
return new PhotoQrUrlExpiredException();
});
}

private Mono<byte[]> getHaruFilmFiles(String qrUrl) {
String[] urlValueList = extractValueFromUrl(qrUrl, "/@");
String albumCode = urlValueList[1];

String baseUrl = urlValueList[0] + "/base_api?command=albumdn&albumCode=";
String imageUrl = baseUrl + albumCode + "&type=photo&file_name=output.jpg&max=10&limit=+24 hours";

// TODO : 추후 비디오 URL 추가 예정
// String videoUrl = baseUrl + albumCode + "&type=video&max=10&limit=+24 hours";

return getFileAsByte(imageUrl)
.onErrorMap(e -> new PhotoQrUrlExpiredException());
}

private Mono<byte[]> getDontLookUpFiles(String qrUrl) {
String imageName = extractValueFromUrl(qrUrl, ".kr/image/")[1];

String baseUrl = "https://x.dontlxxkup.kr/uploads/";
String imageUrl = baseUrl + imageName;

// TODO : 추후 비디오 URL 추가 예정
// String videoName = imageName.replace("image", "video").replace(".jpg", ".mp4");
// String videoUrl = baseUrl + videoName;

return getRedirectUri(qrUrl)
.flatMap(redirectUri -> {
if (redirectUri.endsWith("/delete")) {
return Mono.error(new PhotoQrUrlExpiredException());
} else {
return getFileAsByte(imageUrl);
}
})
.onErrorResume(
RedirectUriNotFoundException.class, e -> getFileAsByte(imageUrl)
);
}

private Mono<byte[]> getMyFourCutFiles(String qrUrl) {
return getFileAsByte(qrUrl);
}

private Mono<String> getRedirectUri(String url) {
return externalWebClient
.get()
.uri(url)
.retrieve()
.toBodilessEntity()
.flatMap(response -> {
URI redirectUri = response.getHeaders().getLocation();
if (redirectUri == null) {
return Mono.error(new RedirectUriNotFoundException());
} else {
return Mono.just(redirectUri.toString());
}
});
}

private String[] extractValueFromUrl(String url, String delimiter) {
return url.split(delimiter);
}

private Mono<byte[]> getFileAsByte(String url) {
return externalWebClient
.get()
.uri(url)
.accept(MediaType.APPLICATION_OCTET_STREAM)
.retrieve()
.bodyToMono(byte[].class);
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package kr.mafoo.photo.service.vendors;

import kr.mafoo.photo.exception.PhotoQrUrlExpiredException;
import kr.mafoo.photo.exception.RedirectUriNotFoundException;
import kr.mafoo.photo.util.WebClientUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

@RequiredArgsConstructor
@Component
public class DontLookUpQrVendor implements QrVendor {
private final WebClient webClient;

@Override
public Mono<byte[]> extractImageFromQrUrl(String qrUrl) {
String imageName = qrUrl.split(".kr/image/")[1];

String baseUrl = "https://x.dontlxxkup.kr/uploads/";
String imageUrl = baseUrl + imageName;

// TODO : 추후 비디오 URL 추가 예정
// String videoName = imageName.replace("image", "video").replace(".jpg", ".mp4");
// String videoUrl = baseUrl + videoName;

return WebClientUtil.getRedirectUri(webClient, qrUrl)
.flatMap(redirectUri -> {
if (redirectUri.endsWith("/delete")) {
return Mono.error(new PhotoQrUrlExpiredException());
} else {
return WebClientUtil.getBlob(webClient, imageUrl);
}
})
.onErrorResume(
RedirectUriNotFoundException.class, e -> WebClientUtil.getBlob(webClient, imageUrl)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package kr.mafoo.photo.service.vendors;

import kr.mafoo.photo.exception.PhotoQrUrlExpiredException;
import kr.mafoo.photo.util.WebClientUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

@RequiredArgsConstructor
@Component
public class HaruFilmQrVendor implements QrVendor {
private final WebClient webClient;

@Override
public Mono<byte[]> extractImageFromQrUrl(String qrUrl) {
String[] urlValueList = qrUrl.split("/@");
String albumCode = urlValueList[1];

String baseUrl = urlValueList[0] + "/base_api?command=albumdn&albumCode=";
String imageUrl = baseUrl + albumCode + "&type=photo&file_name=output.jpg&max=10&limit=+24 hours";

// TODO : 추후 비디오 URL 추가 예정
// String videoUrl = baseUrl + albumCode + "&type=video&max=10&limit=+24 hours";

return WebClientUtil.getBlob(webClient, imageUrl)
.onErrorMap(e -> new PhotoQrUrlExpiredException());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package kr.mafoo.photo.service.vendors;

import kr.mafoo.photo.exception.PhotoQrUrlExpiredException;
import kr.mafoo.photo.util.WebClientUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

@RequiredArgsConstructor
@Component
public class LifeFourCutsQrVendor implements QrVendor {
private final WebClient webClient;

@Override
public Mono<byte[]> extractImageFromQrUrl(String qrUrl) {
return WebClientUtil.getRedirectUri(webClient, qrUrl)
.flatMap(redirectUri -> {
String imageUrl = redirectUri.split("path=")[1].replace("index.html", "image.jpg");

// TODO : 추후 비디오 URL 추가 예정
// String videoUrl = redirectUri.toString().replace("index.html", "video.mp4");

return WebClientUtil.getBlob(webClient, imageUrl);
})
.onErrorMap(e -> new PhotoQrUrlExpiredException());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package kr.mafoo.photo.service.vendors;

import kr.mafoo.photo.exception.PhotoQrUrlExpiredException;
import kr.mafoo.photo.util.WebClientUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

@RequiredArgsConstructor
@Component
public class MyFourCutQrVendor implements QrVendor {
private final WebClient webClient;

@Override
public Mono<byte[]> extractImageFromQrUrl(String qrUrl) {
return WebClientUtil
.getBlob(webClient, qrUrl) //just image url
.onErrorMap(e -> new PhotoQrUrlExpiredException());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package kr.mafoo.photo.service.vendors;

import kr.mafoo.photo.exception.PhotoQrUrlExpiredException;
import kr.mafoo.photo.util.WebClientUtil;
import lombok.RequiredArgsConstructor;
import org.apache.http.client.utils.URLEncodedUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.Base64;

@RequiredArgsConstructor
@Component
public class PhotoGrayQrVendor implements QrVendor {
private final WebClient webClient;

@Override
public Mono<byte[]> extractImageFromQrUrl(String qrUrl) {
return WebClientUtil // https://pgshort.aprd.io/{qr}
.getRedirectUri(webClient, qrUrl) //https://photogray-download.aprd.io?id={base64}
.flatMap((currentUrl) -> {
String encodedStr = extractIdFromUrl(currentUrl);
String decodedStr = new String(Base64.getDecoder().decode(encodedStr));
String sessionId = extractSessionIdFromQueryString(decodedStr);
String imageUrl = String.format("https://pg-qr-resource.aprd.io/%s/image.jpg", sessionId);
return WebClientUtil.getBlob(webClient, imageUrl);
}) //https://pg-qr-resource.aprd.io/{sessionId}/image.jpg
.onErrorMap(e -> new PhotoQrUrlExpiredException());
}

private String extractIdFromUrl(String url) {
return UriComponentsBuilder
.fromUriString(url)
.build()
.getQueryParams()
.getFirst("id");
}

private String extractSessionIdFromQueryString(String queryString) {
return URLEncodedUtils.parse(queryString, StandardCharsets.UTF_8)
.stream()
.filter(e -> e.getName().equals("sessionId"))
.findFirst()
.orElseThrow()
.getValue();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package kr.mafoo.photo.service.vendors;

import kr.mafoo.photo.exception.PhotoQrUrlExpiredException;
import kr.mafoo.photo.util.WebClientUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import java.util.LinkedHashMap;
import java.util.Map;

@RequiredArgsConstructor
@Component
public class PhotoismQrVendor implements QrVendor {
private final WebClient webClient;

@Override
public Mono<byte[]> extractImageFromQrUrl(String qrUrl) {
return WebClientUtil.getRedirectUri(webClient, qrUrl)
.flatMap(redirectUri -> {
String uid = redirectUri.split("u=")[1];

return webClient
.post()
.uri("https://cmsapi.seobuk.kr/v1/etc/seq/resource")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(Map.of("uid", uid))
.retrieve()
.bodyToMono(LinkedHashMap.class)
.flatMap(responseBody -> {
LinkedHashMap<String, Object> content = (LinkedHashMap<String, Object>) responseBody.get("content");
LinkedHashMap<String, Object> fileInfo = (LinkedHashMap<String, Object>) content.get("fileInfo");
LinkedHashMap<String, Object> picFile = (LinkedHashMap<String, Object>) fileInfo.get("picFile");
String imageUrl = (String) picFile.get("path");

return WebClientUtil.getBlob(webClient, imageUrl);
});
})
.onErrorMap(e -> {
e.printStackTrace();
return new PhotoQrUrlExpiredException();
});
}
}
Loading

0 comments on commit 0508c7f

Please sign in to comment.