Skip to content

Commit 2b33085

Browse files
committed
feat: 입찰 시 지갑 히스토리 남김
1 parent 8f80371 commit 2b33085

File tree

4 files changed

+53
-25
lines changed

4 files changed

+53
-25
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package devut.buzzerbidder.domain.liveBid.dto;
2+
3+
public record BidAtomicResult(long code, Long balanceBefore, Long balanceAfter) {
4+
public boolean isSuccess() { return code == 1L; }
5+
}

src/main/java/devut/buzzerbidder/domain/liveBid/service/LiveBidRedisService.java

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package devut.buzzerbidder.domain.liveBid.service;
22

3+
import devut.buzzerbidder.domain.liveBid.dto.BidAtomicResult;
34
import io.micrometer.core.annotation.Timed;
45
import java.util.Arrays;
56
import java.util.List;
@@ -107,13 +108,13 @@ public long getRedisNowMs() {
107108
-- 추가: endTime 확인 (없으면 초기화 안 된 것)
108109
local endTimeStr = redis.call('HGET', liveKey, 'endTime')
109110
if (not endTimeStr) or (endTimeStr == false) then
110-
return -3
111+
return {-3}
111112
end
112113
local endTime = tonumber(endTimeStr)
113114
114115
-- 추가: 이미 종료 시간이 지났으면 입찰 거절
115116
if nowMs >= endTime then
116-
return -4
117+
return {-4}
117118
end
118119
119120
-- wallet keys
@@ -123,12 +124,12 @@ public long getRedisNowMs() {
123124
124125
-- 세션 없으면 실패
125126
if redis.call('EXISTS', sesKey) == 0 then
126-
return -3
127+
return {-3}
127128
end
128129
129130
-- verKey 없으면 실패 (TTL 꼬임/상태 이상 방지)
130131
if redis.call('EXISTS', verKey) == 0 then
131-
return -3
132+
return {-3}
132133
end
133134
134135
local maxPriceStr = redis.call('HGET', liveKey, 'maxBidPrice')
@@ -138,12 +139,12 @@ public long getRedisNowMs() {
138139
139140
-- 이미 본인이 최고입찰자면 실패
140141
if prevBidder == newBidderId then
141-
return -1
142+
return {-1}
142143
end
143144
144145
-- deposits에 bidderId가 남아있으면(정상이라면 없어야 함) -1 반환
145146
if redis.call('HEXISTS', depositsKey, newBidderId) == 1 then
146-
return -1
147+
return {-1}
147148
end
148149
149150
-- 현재 최고 입찰 금액이 최소 5% 이상, 5%가 100보다 작으면 100 이상
@@ -155,7 +156,7 @@ public long getRedisNowMs() {
155156
local minPrice = curMax + inc
156157
157158
if newPrice < minPrice then
158-
return 0
159+
return {0}
159160
end
160161
161162
-- 환불에 필요한 값은 쓰기(차감/HSET) 전에 미리 읽고 검증(부분반영 방지)
@@ -173,7 +174,7 @@ public long getRedisNowMs() {
173174
174175
local prevBalStr = redis.call('GET', prevBalKey)
175176
if (not prevBalStr) or (redis.call('EXISTS', prevVerKey) == 0) then
176-
return -3
177+
return {-3}
177178
end
178179
prevBal = tonumber(prevBalStr)
179180
end
@@ -182,15 +183,16 @@ public long getRedisNowMs() {
182183
-- deposit 차감: 잔액 체크 후 차감
183184
local balStr = redis.call('GET', balKey)
184185
if not balStr then
185-
return -3
186+
return {-3}
186187
end
187188
188189
local bal = tonumber(balStr)
189190
if bal < deposit then
190-
return -2
191+
return {-2, bal, bal}
191192
end
192193
193-
redis.call('SET', balKey, bal - deposit)
194+
local afterBal = bal - deposit
195+
redis.call('SET', balKey, afterBal)
194196
redis.call('INCR', verKey)
195197
196198
-- deposits에 deposit 기록
@@ -245,15 +247,15 @@ public long getRedisNowMs() {
245247
redis.call('EXPIRE', liveKey, balanceTtl)
246248
end
247249
248-
return 1
250+
return {1, bal, afterBal}
249251
""";
250252

251253
@Timed(
252254
value = "buzzerbidder.redis.livebid",
253255
extraTags = {"op", "atomic_update"},
254256
histogram = true
255257
)
256-
public Long updateMaxBidPriceAtomicWithDeposit(
258+
public BidAtomicResult updateMaxBidPriceAtomicWithDeposit(
257259
String redisKey,
258260
Long liveItemId,
259261
Long bidderId,
@@ -262,30 +264,51 @@ public Long updateMaxBidPriceAtomicWithDeposit(
262264
Long sessionTtlSeconds,
263265
Long balanceTtlSeconds
264266
) {
265-
DefaultRedisScript<Long> script = new DefaultRedisScript<>(LUA_BID_SCRIPT, Long.class);
267+
@SuppressWarnings({"rawtypes", "unchecked"})
268+
DefaultRedisScript<List> script = new DefaultRedisScript<>(LUA_BID_SCRIPT, List.class);
266269

267270
try {
268-
return redisTemplate.execute(
271+
List<?> raw = redisTemplate.execute(
269272
script,
270273
List.of(
271274
redisKey, // KEYS[1] 기존 LiveItem 키
272275
ENDING_ZSET_KEY, // KEYS[2] ending zset
273276
"liveItems:currentPrice", // KEYS[3] 가격 필터용 ZSET 키
274277
"liveItems:hasBid" // KEYS[4] 입찰 존재 SET 키
275278
),
276-
bidderId.toString(),
277-
bidPrice.toString(),
278-
depositAmount.toString(),
279-
sessionTtlSeconds.toString(),
280-
balanceTtlSeconds.toString(),
281-
liveItemId.toString()
279+
bidderId.toString(), // ARGV[1]
280+
bidPrice.toString(), // ARGV[2]
281+
depositAmount.toString(), // ARGV[3]
282+
sessionTtlSeconds.toString(), // ARGV[4]
283+
balanceTtlSeconds.toString(), // ARGV[5]
284+
liveItemId.toString() // ARGV[6]
282285
);
286+
287+
if (raw == null || raw.isEmpty()) {
288+
throw new IllegalStateException("Redis LUA 반환이 null/empty 입니다. redisKey=" + redisKey);
289+
}
290+
291+
long code = Long.parseLong(String.valueOf(raw.get(0)));
292+
293+
Long before = null;
294+
Long after = null;
295+
296+
if (raw.size() >= 3) {
297+
before = Long.parseLong(String.valueOf(raw.get(1)));
298+
after = Long.parseLong(String.valueOf(raw.get(2)));
299+
}
300+
301+
return new BidAtomicResult(code, before, after);
302+
283303
} catch (DataAccessException e) {
284-
// 레디스 연결/타임아웃/스크립트 실행 실패 등은 여기로 옴
285304
throw new IllegalStateException("Redis LUA 실행 실패. redisKey=" + redisKey + ", bidderId=" + bidderId, e);
305+
} catch (RuntimeException e) {
306+
// 파싱 실패 등
307+
throw new IllegalStateException("Redis LUA 반환 파싱 실패. redisKey=" + redisKey + ", bidderId=" + bidderId, e);
286308
}
287309
}
288310

311+
289312
/**
290313
* ending ZSET에서 (score <= nowMs) 인 liveItemId들을 limit 만큼 꺼냄
291314
* 여러 서버/스레드가 돌아도 같은 itemId를 중복 처리하지 않게 하려고 ZREM까지 같이 함

src/main/java/devut/buzzerbidder/domain/wallet/service/WalletHistoryService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public Page<WalletHistory> getWalletHistoriesPage(Long userId, int page) {
3030
}
3131

3232
// 지갑 히스토리 기록
33-
void recordWalletHistory(User user, Long amount, WalletTransactionType type,
33+
public void recordWalletHistory(User user, Long amount, WalletTransactionType type,
3434
Long balanceBefore, Long balanceAfter) {
3535
WalletHistory walletHistory = WalletHistory.builder()
3636
.user(user)

src/main/java/devut/buzzerbidder/domain/wallet/service/WalletService.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,12 @@ public void deductBizz(User user, Long amount) {
100100
changeBizz(user, amount, WalletTransactionType.ADMIN_DEDUCT);
101101
}
102102

103-
// 입찰 시 코인 차감
103+
// 입찰 시 차감
104104
public void bidBizz(User user, Long amount) {
105105
changeBizz(user, amount, WalletTransactionType.BID);
106106
}
107107

108-
// 입찰 실패 시 코인 환불
108+
// 입찰 실패 시 환불
109109
public void refundBidBizz(User user, Long amount) {
110110
changeBizz(user, amount, WalletTransactionType.BID_REFUND);
111111
}

0 commit comments

Comments
 (0)