Skip to content

Commit 999ab11

Browse files
authored
Merge pull request #210 from Central-MakeUs/dev
feat: μ•± 버전 μ •μ±… 반영
2 parents cc9d2d7 + a1f4c2d commit 999ab11

File tree

12 files changed

+390
-19
lines changed

12 files changed

+390
-19
lines changed

β€Žsrc/main/java/com/example/ForDay/domain/app/controller/AppController.javaβ€Ž

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
import com.example.ForDay.domain.app.dto.request.GeneratePresignedReqDto;
55
import com.example.ForDay.domain.app.dto.response.AppMetaDataResDto;
66
import com.example.ForDay.domain.app.dto.response.GeneratePresignedUrlResDto;
7+
import com.example.ForDay.domain.app.dto.response.VersionPolicyResDto;
78
import com.example.ForDay.domain.app.service.AppService;
9+
import com.example.ForDay.domain.app.type.Platform;
810
import com.example.ForDay.global.common.response.dto.MessageResDto;
911
import jakarta.validation.Valid;
1012
import lombok.RequiredArgsConstructor;
@@ -32,4 +34,13 @@ public List<GeneratePresignedUrlResDto> generatePresignedUrl(@RequestBody @Valid
3234
public MessageResDto deleteS3Image(@RequestBody @Valid DeleteS3ImageReqDto reqDto) {
3335
return appService.deleteS3Image(reqDto);
3436
}
37+
38+
@GetMapping("/version-policy")
39+
public VersionPolicyResDto getVersionPolicy(
40+
@RequestParam Platform platform,
41+
@RequestParam String appVersion,
42+
@RequestParam int build
43+
) {
44+
return appService.getPolicy(platform, appVersion, build);
45+
}
3546
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.example.ForDay.domain.app.dto.response;
2+
3+
import com.example.ForDay.domain.app.type.Platform;
4+
import com.example.ForDay.domain.app.type.UpdateType;
5+
6+
public record VersionPolicyResDto(
7+
int policyVersion,
8+
Platform platform,
9+
Current current,
10+
Threshold minSupported,
11+
Threshold latest,
12+
UpdateType update,
13+
String storeUrl,
14+
String message
15+
) {
16+
public record Current(String version, int build) {}
17+
public record Threshold(String version, int build) {}
18+
}
Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,70 @@
11
package com.example.ForDay.domain.app.entity;
22

3+
import com.example.ForDay.domain.app.type.Platform;
4+
import com.example.ForDay.global.common.mapped.BaseTimeEntity;
35
import jakarta.persistence.*;
4-
import lombok.AllArgsConstructor;
5-
import lombok.Builder;
6-
import lombok.Getter;
7-
import lombok.NoArgsConstructor;
6+
import lombok.*;
7+
import org.hibernate.annotations.CreationTimestamp;
8+
import org.hibernate.annotations.UpdateTimestamp;
89

910
import java.time.LocalDateTime;
1011

1112
@Entity
12-
@Table(name = "app_version")
13+
@Table(name = "app_version") // ν…Œμ΄λΈ”λͺ…은 μ„œλΉ„μŠ€μ— 맞좰 λ³€κ²½ν•˜μ„Έμš”
1314
@Getter
14-
@NoArgsConstructor
15+
@NoArgsConstructor(access = AccessLevel.PROTECTED)
1516
@AllArgsConstructor
1617
@Builder
17-
public class AppVersion {
18+
public class AppVersion extends BaseTimeEntity {
19+
1820
@Id
1921
@GeneratedValue(strategy = GenerationType.IDENTITY)
20-
@Column(name = "app_version_id")
2122
private Long id;
22-
private String description;
23-
private String version;
23+
24+
@Column(nullable = false, length = 20)
25+
@Enumerated(EnumType.STRING)
26+
private Platform platform;
27+
28+
@Column(name = "policy_version", nullable = false)
29+
private Integer policyVersion;
30+
31+
@Column(name = "min_supported_version", nullable = false, length = 20)
32+
private String minSupportedVersion;
33+
34+
@Column(name = "min_supported_build", nullable = false)
35+
private Integer minSupportedBuild;
36+
37+
@Column(name = "latest_version", nullable = false, length = 20)
38+
private String latestVersion;
39+
40+
@Column(name = "latest_build", nullable = false)
41+
private Integer latestBuild;
42+
43+
@Column(name = "store_url", nullable = false, length = 255)
44+
private String storeUrl;
45+
46+
@Column(name = "force_message", nullable = false, length = 255)
47+
private String forceMessage; // κ°•μ œ μ—…λ°μ΄νŠΈ 문ꡬ
48+
49+
@Column(name = "recommend_message", nullable = false, length = 255)
50+
private String recommendMessage; // ꢌμž₯ μ—…λ°μ΄νŠΈ 문ꡬ
51+
52+
@Column(name = "block_enabled", nullable = false)
53+
@Builder.Default
54+
private Boolean blockEnabled = false; // μ„œλΉ„μŠ€ 차단 μ—¬λΆ€
55+
56+
@Column(name = "block_message", length = 255)
57+
private String blockMessage; // 차단 문ꡬ
58+
59+
@CreationTimestamp
60+
@Column(name = "created_at", nullable = false, updatable = false)
2461
private LocalDateTime createdAt;
25-
}
62+
63+
@UpdateTimestamp
64+
@Column(name = "updated_at", nullable = false)
65+
private LocalDateTime updatedAt;
66+
67+
public boolean isServiceBlocked() {
68+
return Boolean.TRUE.equals(this.blockEnabled);
69+
}
70+
}

β€Žsrc/main/java/com/example/ForDay/domain/app/repository/AppVersionRepository.javaβ€Ž

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.example.ForDay.domain.app.repository;
22

33
import com.example.ForDay.domain.app.entity.AppVersion;
4+
import com.example.ForDay.domain.app.type.Platform;
45
import org.springframework.data.jpa.repository.JpaRepository;
56
import org.springframework.data.jpa.repository.Query;
67

@@ -9,4 +10,6 @@
910
public interface AppVersionRepository extends JpaRepository<AppVersion, Long> {
1011
@Query("SELECT av FROM AppVersion av ORDER BY av.createdAt DESC LIMIT 1")
1112
Optional<AppVersion> findLatestVersion();
13+
14+
Optional<AppVersion> findFirstByPlatformOrderByCreatedAtDesc(Platform platform);
1215
}

β€Žsrc/main/java/com/example/ForDay/domain/app/service/AppService.javaβ€Ž

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@
66
import com.example.ForDay.domain.app.dto.request.GeneratePresignedReqDto;
77
import com.example.ForDay.domain.app.dto.response.AppMetaDataResDto;
88
import com.example.ForDay.domain.app.dto.response.GeneratePresignedUrlResDto;
9+
import com.example.ForDay.domain.app.dto.response.VersionPolicyResDto;
910
import com.example.ForDay.domain.app.entity.AppVersion;
1011
import com.example.ForDay.domain.app.repository.AppVersionRepository;
12+
import com.example.ForDay.domain.app.type.Platform;
13+
import com.example.ForDay.domain.app.type.UpdateType;
1114
import com.example.ForDay.domain.hobby.repository.HobbyInfoRepository;
1215
import com.example.ForDay.global.common.error.exception.CustomException;
1316
import com.example.ForDay.global.common.error.exception.ErrorCode;
@@ -49,15 +52,10 @@ public AppMetaDataResDto getMetaData() {
4952
hobbyCard.getImageCode()
5053
))
5154
.toList();
52-
53-
String latestVersion = appVersionRepository.findLatestVersion()
54-
.map(AppVersion::getVersion)
55-
.orElse("1.0.0");
56-
5755
log.info("[getMetaData] 쑰회 μ™„λ£Œ - μ·¨λ―Έ 정보 개수: {}개", hobbyCardDtos.size());
5856

5957
return new AppMetaDataResDto(
60-
latestVersion, // μ•± 버전 κ΄€λ¦¬λŠ” λ‚˜μ€‘μ— κ΅¬ν˜„
58+
"1.0.0",
6159
hobbyCardDtos
6260
);
6361
}
@@ -146,4 +144,46 @@ public MessageResDto deleteS3Image(DeleteS3ImageReqDto reqDto) {
146144
log.info("[deleteS3Image] λͺ¨λ“  κ΄€λ ¨ 이미지 μ‚­μ œ μ™„λ£Œ - Original Key: {}", originalKey);
147145
return new MessageResDto("이미지가 μ„±κ³΅μ μœΌλ‘œ μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.");
148146
}
147+
148+
@Transactional(readOnly = true)
149+
public VersionPolicyResDto getPolicy(Platform platform, String appVersion, int build) {
150+
// 1. ν•΄λ‹Ή ν”Œλž«νΌμ˜ μ΅œμ‹  μ •μ±… 쑰회 (μ—”ν‹°ν‹°)
151+
AppVersion policy = appVersionRepository.findFirstByPlatformOrderByCreatedAtDesc(platform)
152+
.orElseThrow(() -> new CustomException(ErrorCode.PLATFORM_NOT_FOUND));
153+
154+
// 2. 버전 비ꡐλ₯Ό μœ„ν•œ 객체 생성
155+
AppVersionUtil current = AppVersionUtil.of(appVersion, build);
156+
AppVersionUtil minSupported = AppVersionUtil.of(policy.getMinSupportedVersion(), policy.getMinSupportedBuild());
157+
AppVersionUtil latest = AppVersionUtil.of(policy.getLatestVersion(), policy.getLatestBuild());
158+
159+
// 3. μš°μ„ μˆœμœ„μ— λ”°λ₯Έ μ—…λ°μ΄νŠΈ νƒ€μž… 및 λ©”μ‹œμ§€ κ²°μ •
160+
UpdateType updateType;
161+
String message;
162+
163+
if (policy.getBlockEnabled()) {
164+
updateType = UpdateType.BLOCK;
165+
message = policy.getBlockMessage();
166+
} else if (current.compareTo(minSupported) < 0) {
167+
updateType = UpdateType.FORCE;
168+
message = policy.getForceMessage();
169+
} else if (current.compareTo(latest) < 0) {
170+
updateType = UpdateType.RECOMMEND;
171+
message = policy.getRecommendMessage();
172+
} else {
173+
updateType = UpdateType.NONE;
174+
message = "";
175+
}
176+
177+
// 4. DTO λ³€ν™˜ 및 λ°˜ν™˜
178+
return new VersionPolicyResDto(
179+
policy.getPolicyVersion(),
180+
platform,
181+
new VersionPolicyResDto.Current(current.versionString(), current.build()),
182+
new VersionPolicyResDto.Threshold(minSupported.versionString(), minSupported.build()),
183+
new VersionPolicyResDto.Threshold(latest.versionString(), latest.build()),
184+
updateType,
185+
policy.getStoreUrl(),
186+
message
187+
);
188+
}
149189
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package com.example.ForDay.domain.app.service;
2+
3+
import java.util.Objects;
4+
5+
public final class AppVersionUtil implements Comparable<AppVersionUtil> {
6+
private final int major;
7+
private final int minor;
8+
private final int patch;
9+
private final int build;
10+
11+
private AppVersionUtil(int major, int minor, int patch, int build) {
12+
this.major = major;
13+
this.minor = minor;
14+
this.patch = patch;
15+
this.build = build;
16+
}
17+
18+
/**
19+
* λ¬Έμžμ—΄ 버전(x.y.z)κ³Ό λΉŒλ“œ 번호λ₯Ό λ°›μ•„ AppVersion 객체λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.
20+
*/
21+
public static AppVersionUtil of(String semver, int build) {
22+
Objects.requireNonNull(semver, "Version string must not be null");
23+
24+
// μ •κ·œμ‹ . 을 κΈ°μ€€μœΌλ‘œ 뢄리 (1.2.0 -> [1, 2, 0])
25+
String[] parts = semver.trim().split("\\.");
26+
27+
// μœ μ—°ν•œ 처리λ₯Ό μœ„ν•΄ λΆ€μ‘±ν•œ 뢀뢄은 0으둜 채움 (ex: 1.1 -> 1.1.0)
28+
int major = parts.length > 0 ? parsePart(parts[0]) : 0;
29+
int minor = parts.length > 1 ? parsePart(parts[1]) : 0;
30+
int patch = parts.length > 2 ? parsePart(parts[2]) : 0;
31+
32+
return new AppVersionUtil(major, minor, patch, Math.max(0, build));
33+
}
34+
35+
private static int parsePart(String part) {
36+
try {
37+
return Integer.parseInt(part.replaceAll("[^0-9]", "")); // 숫자만 남기고 νŒŒμ‹±
38+
} catch (NumberFormatException e) {
39+
return 0;
40+
}
41+
}
42+
43+
/**
44+
* 버전 비ꡐ 둜직 (Major -> Minor -> Patch -> Build μˆœμ„œ)
45+
*/
46+
@Override
47+
public int compareTo(AppVersionUtil other) {
48+
if (this.major != other.major) return Integer.compare(this.major, other.major);
49+
if (this.minor != other.minor) return Integer.compare(this.minor, other.minor);
50+
if (this.patch != other.patch) return Integer.compare(this.patch, other.patch);
51+
return Integer.compare(this.build, other.build);
52+
}
53+
54+
public String versionString() {
55+
return String.format("%d.%d.%d", major, minor, patch);
56+
}
57+
58+
public int build() {
59+
return build;
60+
}
61+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.example.ForDay.domain.app.type;
2+
3+
public enum Platform {
4+
IOS, ANDROID
5+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.example.ForDay.domain.app.type;
2+
3+
public enum UpdateType {
4+
NONE,
5+
RECOMMEND,
6+
FORCE,
7+
BLOCK
8+
}

β€Žsrc/main/java/com/example/ForDay/global/common/error/exception/ErrorCode.javaβ€Ž

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,9 @@ public enum ErrorCode {
8484

8585
// 검색어 κ΄€λ ¨
8686
KEYWORD_NOT_FOUND(HttpStatus.NOT_FOUND, "μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” κ²€μƒ‰μ–΄μž…λ‹ˆλ‹€."),
87-
;
87+
88+
// μ•± 버전 κ΄€λ ¨
89+
PLATFORM_NOT_FOUND(HttpStatus.NOT_FOUND, "ν•΄λ‹Ή ν”Œλž«νΌμ— λŒ€ν•œ 버전 정보가 μ‘΄μž¬ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.");
8890

8991
private final HttpStatus status;
9092
private final String message;

β€Žsrc/main/java/com/example/ForDay/global/config/security/SecurityConfig.javaβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
3030
.httpBasic((auth) -> auth.disable())
3131
.authorizeHttpRequests((auth) -> auth
3232
.requestMatchers(
33-
"/app/metadata", "/health_check", "/error_check", "/swagger-ui/**", "/v3/api-docs/**", "/v3/api-docs.yaml", "/log-test", "/terms/**")
33+
"/app/metadata", "/health_check", "/error_check", "/swagger-ui/**", "/v3/api-docs/**", "/v3/api-docs.yaml", "/log-test", "/terms/**", "/app/version-policy")
3434
.permitAll()
3535
.requestMatchers(
3636
"/auth/kakao", "/auth/apple", "/auth/guest", "/auth/refresh")

0 commit comments

Comments
Β (0)