Skip to content

Commit 4a1e521

Browse files
authored
Merge pull request #143 from SEMOSAN/feat/#134-demo-tracking-complete
[Feat] 데모 시연용 트래킹 사진 조회 API 구현
2 parents d21b310 + 70c5194 commit 4a1e521

6 files changed

Lines changed: 154 additions & 0 deletions

File tree

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.semosan.api.common.config;
2+
3+
import org.springframework.boot.context.properties.ConfigurationProperties;
4+
5+
import java.util.List;
6+
7+
@ConfigurationProperties(prefix = "demo")
8+
public record DemoProperties(
9+
List<String> photoFilenames
10+
) {
11+
public DemoProperties {
12+
photoFilenames = photoFilenames == null ? List.of() : photoFilenames;
13+
}
14+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.semosan.api.domain.demo.controller;
2+
3+
import com.semosan.api.common.config.DemoProperties;
4+
import com.semosan.api.common.response.ApiResponse;
5+
import com.semosan.api.common.status.SuccessStatus;
6+
import com.semosan.api.domain.demo.controller.docs.DemoControllerDocs;
7+
import com.semosan.api.domain.demo.service.DemoService;
8+
import lombok.RequiredArgsConstructor;
9+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
10+
import org.springframework.http.ResponseEntity;
11+
import org.springframework.web.bind.annotation.*;
12+
13+
import java.util.List;
14+
15+
@RestController
16+
@RequestMapping("/api/demo")
17+
@RequiredArgsConstructor
18+
@EnableConfigurationProperties(DemoProperties.class)
19+
public class DemoController implements DemoControllerDocs {
20+
21+
private final DemoService demoService;
22+
23+
@GetMapping("/tracking/sessions/{sessionId}/photos")
24+
@Override
25+
public ResponseEntity<ApiResponse<List<String>>> getDemoPhotos(
26+
@PathVariable Long sessionId,
27+
@RequestParam(defaultValue = "3") int count
28+
) {
29+
List<String> combined = demoService.getDemoPhotos(sessionId, count);
30+
return ApiResponse.success(SuccessStatus.TRACKING_PHOTO_LIST_SUCCESS, combined);
31+
}
32+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.semosan.api.domain.demo.controller.docs;
2+
3+
import com.semosan.api.common.response.ApiResponse;
4+
import io.swagger.v3.oas.annotations.Operation;
5+
import io.swagger.v3.oas.annotations.Parameter;
6+
import io.swagger.v3.oas.annotations.responses.ApiResponses;
7+
import io.swagger.v3.oas.annotations.tags.Tag;
8+
import org.springframework.http.ResponseEntity;
9+
import org.springframework.web.bind.annotation.PathVariable;
10+
import org.springframework.web.bind.annotation.RequestParam;
11+
12+
import java.util.List;
13+
14+
@Tag(name = "Demo", description = "시연용 API")
15+
public interface DemoControllerDocs {
16+
17+
@Operation(
18+
summary = "시연용 트래킹 사진 조회",
19+
description = "MinIO에 미리 올려둔 사진 중 랜덤 N개 + 해당 세션에서 직접 촬영한 사진 URL을 합쳐서 반환합니다."
20+
)
21+
@ApiResponses({
22+
@io.swagger.v3.oas.annotations.responses.ApiResponse(
23+
responseCode = "200",
24+
description = "시연용 사진 목록 조회 성공"
25+
)
26+
})
27+
ResponseEntity<ApiResponse<List<String>>> getDemoPhotos(
28+
@Parameter(description = "트래킹 세션 ID", required = true)
29+
@PathVariable Long sessionId,
30+
@Parameter(description = "랜덤 사진 개수 (기본값 3)", required = false)
31+
@RequestParam(defaultValue = "3") int count
32+
);
33+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
2+
package com.semosan.api.domain.demo.service;
3+
4+
import com.semosan.api.common.config.DemoProperties;
5+
import com.semosan.api.common.config.MinioProperties;
6+
import com.semosan.api.domain.tracking.entity.TrackingPhoto;
7+
import com.semosan.api.domain.tracking.repository.TrackingPhotoRepository;
8+
import io.minio.GetPresignedObjectUrlArgs;
9+
import io.minio.MinioClient;
10+
import io.minio.http.Method;
11+
import lombok.RequiredArgsConstructor;
12+
import lombok.extern.slf4j.Slf4j;
13+
import org.springframework.stereotype.Service;
14+
import org.springframework.transaction.annotation.Transactional;
15+
16+
import java.util.ArrayList;
17+
import java.util.Collections;
18+
import java.util.List;
19+
import java.util.concurrent.TimeUnit;
20+
21+
@Slf4j
22+
@Service
23+
@RequiredArgsConstructor
24+
@Transactional(readOnly = true)
25+
public class DemoService {
26+
27+
private static final String BUCKET = "tracking-photos";
28+
29+
private final DemoProperties demoProperties;
30+
private final MinioClient minioClient;
31+
private final MinioProperties minioProperties;
32+
private final TrackingPhotoRepository trackingPhotoRepository;
33+
34+
public List<String> getDemoPhotos(Long sessionId, int count) {
35+
List<String> shuffled = new ArrayList<>(demoProperties.photoFilenames());
36+
Collections.shuffle(shuffled);
37+
List<String> randomUrls = shuffled.subList(0, Math.min(count, shuffled.size()))
38+
.stream()
39+
.map(this::presignedGetUrl)
40+
.toList();
41+
42+
List<String> uploadedUrls = trackingPhotoRepository
43+
.findByTrackingSession_IdOrderByMilestoneIndexAsc(sessionId)
44+
.stream()
45+
.map(TrackingPhoto::getImageUrl)
46+
.toList();
47+
48+
List<String> combined = new ArrayList<>(randomUrls);
49+
combined.addAll(uploadedUrls);
50+
return combined;
51+
}
52+
53+
private String presignedGetUrl(String objectKey) {
54+
try {
55+
String url = minioClient.getPresignedObjectUrl(
56+
GetPresignedObjectUrlArgs.builder()
57+
.method(Method.GET)
58+
.bucket(BUCKET)
59+
.object(objectKey)
60+
.expiry(1, TimeUnit.HOURS)
61+
.build()
62+
);
63+
return url.replace(minioProperties.endpoint(), minioProperties.publicUrl());
64+
} catch (Exception e) {
65+
log.warn("Failed to generate presigned URL for {}", objectKey, e);
66+
return minioProperties.publicUrl() + "/" + BUCKET + "/" + objectKey;
67+
}
68+
}
69+
}

src/main/resources/application-prod.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ tracking:
4242
test:
4343
secret-key: ${TEST_SECRET_KEY}
4444

45+
demo:
46+
photo-filenames: ${DEMO_PHOTO_FILENAMES}
47+
4548
discord:
4649
alert:
4750
enabled: ${DISCORD_ALERT_ENABLED}

src/main/resources/application.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ minio:
6060
secret-key: ${MINIO_SECRET_KEY}
6161
public-url: ${MINIO_PUBLIC_URL}
6262

63+
demo:
64+
photo-filenames: ${DEMO_PHOTO_FILENAMES}
65+
6366
discord:
6467
alert:
6568
enabled: ${DISCORD_ALERT_ENABLED}

0 commit comments

Comments
 (0)