Skip to content

Commit 27402f1

Browse files
authored
Merge pull request #211 from Central-MakeUs/release
Release
2 parents 1433f97 + 999ab11 commit 27402f1

File tree

14 files changed

+402
-22
lines changed

14 files changed

+402
-22
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/domain/record/dto/response/GetActivityRecordByStoryResDto.javaโ€Ž

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public static class RecordDto {
3030
private UserInfoDto userInfo;
3131
private boolean pressedAweSome;
3232
private String hobbyName;
33+
private boolean recordAuthor;
3334
}
3435

3536
@Data

โ€Žsrc/main/java/com/example/ForDay/domain/record/repository/ActivityRecordRepositoryImpl.javaโ€Ž

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@
1818
import com.example.ForDay.domain.user.entity.User;
1919
import com.example.ForDay.domain.user.type.Role;
2020
import com.querydsl.core.BooleanBuilder;
21+
import com.querydsl.core.types.Expression;
2122
import com.querydsl.core.types.Order;
2223
import com.querydsl.core.types.OrderSpecifier;
2324
import com.querydsl.core.types.Projections;
2425
import com.querydsl.core.types.dsl.BooleanExpression;
2526
import com.querydsl.core.types.dsl.Expressions;
27+
import com.querydsl.core.types.dsl.StringPath;
2628
import com.querydsl.jpa.JPAExpressions;
2729
import com.querydsl.jpa.impl.JPAQueryFactory;
2830
import lombok.RequiredArgsConstructor;
@@ -209,20 +211,19 @@ public List<GetActivityRecordByStoryResDto.RecordDto> getActivityRecordByStory(
209211
user.profileImageUrl
210212
),
211213
reaction.id.isNotNull(), // pressedAweSome (์ข‹์•„์š” ์—ฌ๋ถ€)
212-
hobby.hobbyName
214+
hobby.hobbyName,
215+
isRecordAuthor(record.user.id, currentUserId)
213216
))
214217
.from(record)
215218
.join(record.activity, activity)
216219
.join(record.user, user)
217220
.join(record.hobby, hobby)
218-
// ์„œ๋ธŒ์ฟผ๋ฆฌ ๋Œ€์‹  Left Join์œผ๋กœ ๋‚ด๊ฐ€ ๋ฐ˜์‘ํ–ˆ๋Š”์ง€ ์ฒดํฌ (N+1 ๋ฐฉ์ง€)
219221
.leftJoin(reaction).on(
220222
reaction.activityRecord.id.eq(record.id),
221223
reaction.reactedUser.id.eq(currentUserId)
222224
)
223225
.where(
224226
hobbyCondition(hobbyInfoId, hobbyName),
225-
user.id.ne(currentUserId),
226227
user.deleted.isFalse(),
227228
user.role.eq(Role.USER),
228229
record.deleted.isFalse(),
@@ -237,6 +238,13 @@ public List<GetActivityRecordByStoryResDto.RecordDto> getActivityRecordByStory(
237238
.fetch();
238239
}
239240

241+
private BooleanExpression isRecordAuthor(StringPath recordUserId, String currentUserId) {
242+
if (currentUserId == null) {
243+
return Expressions.FALSE;
244+
}
245+
return recordUserId.eq(currentUserId);
246+
}
247+
240248
// --- Helper Methods ---
241249

242250
private BooleanExpression hobbyCondition(Long hobbyInfoId, String hobbyName) {

0 commit comments

Comments
ย (0)