每日任务钱包充值

This commit is contained in:
hy001 2026-05-08 20:53:01 +08:00
parent 3e4251ba3b
commit 835b6d5367
10 changed files with 495 additions and 119 deletions

View File

@ -10,6 +10,7 @@ import com.red.circle.mq.business.model.event.gift.GiveGiftConfig;
import com.red.circle.mq.business.model.event.task.TaskApprovalEvent; import com.red.circle.mq.business.model.event.task.TaskApprovalEvent;
import com.red.circle.mq.rocket.business.producer.GiftMqMessage; import com.red.circle.mq.rocket.business.producer.GiftMqMessage;
import com.red.circle.mq.rocket.business.producer.TaskMqMessage; import com.red.circle.mq.rocket.business.producer.TaskMqMessage;
import com.red.circle.other.app.common.task.TaskCenterGoClient;
import com.red.circle.other.app.dto.cmd.gift.GiveAwayGiftBatchCmd; import com.red.circle.other.app.dto.cmd.gift.GiveAwayGiftBatchCmd;
import com.red.circle.other.infra.database.cache.service.other.GiftCacheService; import com.red.circle.other.infra.database.cache.service.other.GiftCacheService;
import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService; import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService;
@ -48,6 +49,7 @@ public class BlessingsGiftGiveCmdExe {
private final TaskMqMessage taskMqMessage; private final TaskMqMessage taskMqMessage;
private final WalletGoldClient walletGoldClient; private final WalletGoldClient walletGoldClient;
private final CpRelationshipService cpRelationshipService; private final CpRelationshipService cpRelationshipService;
private final TaskCenterGoClient taskCenterGoClient;
public BigDecimal execute(GiveAwayGiftBatchCmd cmd) { public BigDecimal execute(GiveAwayGiftBatchCmd cmd) {
// 过滤赠送人非法值 // 过滤赠送人非法值
@ -82,6 +84,7 @@ public class BlessingsGiftGiveCmdExe {
// log.warn("cptesttest execute:{}", JacksonUtils.toJson(giftBatchEvent)); // log.warn("cptesttest execute:{}", JacksonUtils.toJson(giftBatchEvent));
giftBatchEvent.setCheckBlessingsGift(Boolean.TRUE); giftBatchEvent.setCheckBlessingsGift(Boolean.TRUE);
giftMqMessage.sendGift(receipt.getAssetRecordId().toString(), giftBatchEvent); giftMqMessage.sendGift(receipt.getAssetRecordId().toString(), giftBatchEvent);
reportTaskCenterGiftEvent(cmd, giftConfig, receipt);
@ -117,6 +120,26 @@ public class BlessingsGiftGiveCmdExe {
} }
private void reportTaskCenterGiftEvent(GiveAwayGiftBatchCmd cmd, GiftConfigDTO giftConfig,
WalletReceiptResDTO receipt) {
if (Objects.isNull(receipt)) {
return;
}
taskCenterGoClient.reportGiftConsumeGold(cmd.requireReqSysOrigin(),
cmd.requiredReqUserId(),
receipt.getAssetRecordId(),
giftConfig.getId(),
giftConfig.getGiftName(),
giftConfig.getGiftTab(),
giftConfig.getGiftCandy(),
cmd.getQuantity(),
cmd.filterAcceptUserId().size(),
receipt.getOpAmount().getDollarAmount(),
cmd.getRoomId(),
cmd.getDynamicContentId(),
"BLESSINGS_GIFT");
}
private GiveAwayGiftBatchEvent toGiveAwayGiftBatchEvent(GiveAwayGiftBatchCmd cmd, private GiveAwayGiftBatchEvent toGiveAwayGiftBatchEvent(GiveAwayGiftBatchCmd cmd,
GiftConfigDTO giftConfig, Long trackId) { GiftConfigDTO giftConfig, Long trackId) {

View File

@ -8,6 +8,7 @@ import com.red.circle.framework.core.response.ResponseErrorCode;
import com.red.circle.mq.business.model.event.task.TaskApprovalEvent; import com.red.circle.mq.business.model.event.task.TaskApprovalEvent;
import com.red.circle.mq.rocket.business.producer.GiftMqMessage; import com.red.circle.mq.rocket.business.producer.GiftMqMessage;
import com.red.circle.mq.rocket.business.producer.TaskMqMessage; import com.red.circle.mq.rocket.business.producer.TaskMqMessage;
import com.red.circle.other.app.common.task.TaskCenterGoClient;
import com.red.circle.other.app.convertor.material.GiftAppConvertor; import com.red.circle.other.app.convertor.material.GiftAppConvertor;
import com.red.circle.other.app.dto.cmd.gift.GiveAwayGiftBatchCmd; import com.red.circle.other.app.dto.cmd.gift.GiveAwayGiftBatchCmd;
import com.red.circle.other.app.service.task.TaskService; import com.red.circle.other.app.service.task.TaskService;
@ -55,6 +56,7 @@ public class GiftGiveAwayBatchCmdExe {
private final WalletDiamondClient walletDiamondClient; private final WalletDiamondClient walletDiamondClient;
private final TaskService taskService; private final TaskService taskService;
private final UserRegionGateway userRegionGateway; private final UserRegionGateway userRegionGateway;
private final TaskCenterGoClient taskCenterGoClient;
public BigDecimal execute(GiveAwayGiftBatchCmd cmd) { public BigDecimal execute(GiveAwayGiftBatchCmd cmd) {
@ -86,6 +88,7 @@ public class GiftGiveAwayBatchCmdExe {
giftMqMessage.sendGift(receipt.getConsumeId().toString(), giftMqMessage.sendGift(receipt.getConsumeId().toString(),
giftAppConvertor.toGiveAwayGiftBatchEvent(cmd, giftConfig, receipt.getConsumeId())); giftAppConvertor.toGiveAwayGiftBatchEvent(cmd, giftConfig, receipt.getConsumeId()));
reportTaskCenterGiftEvent(cmd, giftConfig, receipt);
userProfileGateway.removeCache(cmd.requiredReqUserId()); userProfileGateway.removeCache(cmd.requiredReqUserId());
// 检查是否未完成任务 // 检查是否未完成任务
@ -163,6 +166,26 @@ public class GiftGiveAwayBatchCmdExe {
throw new IllegalArgumentException("param error."); throw new IllegalArgumentException("param error.");
} }
private void reportTaskCenterGiftEvent(GiveAwayGiftBatchCmd cmd, GiftConfigDTO giftConfig,
WalletReceiptDTO receipt) {
if (!isGiftTypeEqGold(giftConfig) || Objects.isNull(receipt)) {
return;
}
taskCenterGoClient.reportGiftConsumeGold(cmd.requireReqSysOrigin(),
cmd.requiredReqUserId(),
receipt.getConsumeId(),
giftConfig.getId(),
giftConfig.getGiftName(),
giftConfig.getGiftTab(),
giftConfig.getGiftCandy(),
cmd.getQuantity(),
cmd.filterAcceptUserId().size(),
receipt.getProcessAmount(),
cmd.getRoomId(),
cmd.getDynamicContentId(),
"NORMAL_GIFT");
}
private boolean isGiftTypeEqDiamond(GiftConfigDTO giftConfigInfo) { private boolean isGiftTypeEqDiamond(GiftConfigDTO giftConfigInfo) {
return Objects.equals(giftConfigInfo.getType(), GiftCurrencyType.DIAMOND.name()); return Objects.equals(giftConfigInfo.getType(), GiftCurrencyType.DIAMOND.name());
} }

View File

@ -7,6 +7,7 @@ import com.red.circle.mq.business.model.event.task.TaskApprovalEvent;
import com.red.circle.mq.rocket.business.producer.GiftMqMessage; import com.red.circle.mq.rocket.business.producer.GiftMqMessage;
import com.red.circle.mq.rocket.business.producer.TaskMqMessage; import com.red.circle.mq.rocket.business.producer.TaskMqMessage;
import com.red.circle.other.app.common.gift.GameLuckyGiftCommon; import com.red.circle.other.app.common.gift.GameLuckyGiftCommon;
import com.red.circle.other.app.common.task.TaskCenterGoClient;
import com.red.circle.other.app.convertor.material.GiftAppConvertor; import com.red.circle.other.app.convertor.material.GiftAppConvertor;
import com.red.circle.other.app.dto.cmd.gift.GiveAwayGiftBatchCmd; import com.red.circle.other.app.dto.cmd.gift.GiveAwayGiftBatchCmd;
import com.red.circle.other.app.service.task.TaskService; import com.red.circle.other.app.service.task.TaskService;
@ -49,6 +50,7 @@ public class LuckyGiftGiveCmdExe {
private final GameLuckyGiftCommon gameLuckyGiftCommon; private final GameLuckyGiftCommon gameLuckyGiftCommon;
private final TaskMqMessage taskMqMessage; private final TaskMqMessage taskMqMessage;
private final TaskService taskService; private final TaskService taskService;
private final TaskCenterGoClient taskCenterGoClient;
public BigDecimal execute(GiveAwayGiftBatchCmd cmd) { public BigDecimal execute(GiveAwayGiftBatchCmd cmd) {
@ -98,6 +100,7 @@ public class LuckyGiftGiveCmdExe {
giftAppConvertor.toGiveAwayGiftBatchEvent(cmd, giftConfig, trackId) giftAppConvertor.toGiveAwayGiftBatchEvent(cmd, giftConfig, trackId)
); );
} }
reportTaskCenterGiftEvent(cmd, giftConfig, receipt);
// 抽奖mq // 抽奖mq
if (luckyGift) { if (luckyGift) {
@ -153,4 +156,27 @@ public class LuckyGiftGiveCmdExe {
} }
private void reportTaskCenterGiftEvent(GiveAwayGiftBatchCmd cmd, GiftConfigDTO giftConfig,
WalletReceiptResDTO receipt) {
if (Objects.isNull(receipt)) {
return;
}
String source = Objects.equals(giftConfig.getGiftTab(), GiftTabEnum.MAGIC.name())
? "MAGIC_GIFT"
: "LUCKY_GIFT";
taskCenterGoClient.reportGiftConsumeGold(cmd.requireReqSysOrigin(),
cmd.requiredReqUserId(),
receipt.getAssetRecordId(),
giftConfig.getId(),
giftConfig.getGiftName(),
giftConfig.getGiftTab(),
giftConfig.getGiftCandy(),
cmd.getQuantity(),
cmd.filterAcceptUserId().size(),
receipt.getOpAmount().getDollarAmount(),
cmd.getRoomId(),
cmd.getDynamicContentId(),
source);
}
} }

View File

@ -3,7 +3,9 @@ package com.red.circle.other.app.common.task;
import cn.hutool.http.HttpResponse; import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil; import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import java.math.BigDecimal;
import java.time.Instant; import java.time.Instant;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import lombok.Data; import lombok.Data;
@ -17,6 +19,7 @@ import org.springframework.stereotype.Component;
public class TaskCenterGoClient { public class TaskCenterGoClient {
private static final String EVENT_TYPE_GAME_CONSUME_GOLD = "GAME_CONSUME_GOLD"; private static final String EVENT_TYPE_GAME_CONSUME_GOLD = "GAME_CONSUME_GOLD";
private static final String EVENT_TYPE_GIFT_CONSUME_GOLD = "GIFT_CONSUME_GOLD";
@Value("${task.center.go.enabled:true}") @Value("${task.center.go.enabled:true}")
private boolean enabled; private boolean enabled;
@ -51,6 +54,52 @@ public class TaskCenterGoClient {
.setPayload(payload)); .setPayload(payload));
} }
public void reportGiftConsumeGold(String sysOrigin,
Long userId,
Long trackId,
Long giftId,
String giftName,
String giftTab,
BigDecimal giftCandy,
Integer quantity,
Integer acceptUserSize,
BigDecimal consumeGold,
Long roomId,
Long dynamicContentId,
String source) {
if (Objects.isNull(userId) || Objects.isNull(trackId) || Objects.isNull(consumeGold)
|| consumeGold.compareTo(BigDecimal.ZERO) <= 0) {
return;
}
long deltaGold = consumeGold.longValue();
if (deltaGold <= 0) {
return;
}
Map<String, Object> payload = new HashMap<>();
payload.put("trackId", trackId);
payload.put("giftId", giftId);
payload.put("giftName", giftName);
payload.put("giftTab", giftTab);
payload.put("giftCandy", giftCandy);
payload.put("quantity", quantity);
payload.put("acceptUserSize", acceptUserSize);
payload.put("actualAmount", consumeGold);
payload.put("roomId", roomId);
payload.put("dynamicContentId", dynamicContentId);
payload.put("source", source);
reportEvent(new TaskCenterEventRequest()
.setSysOrigin(sysOrigin)
.setEventId("GIFT_CONSUME:" + trackId)
.setEventType(EVENT_TYPE_GIFT_CONSUME_GOLD)
.setUserId(userId)
.setDeltaValue(deltaGold)
.setOccurredAt(Instant.now().toString())
.setPayload(payload));
}
private void reportEvent(TaskCenterEventRequest request) { private void reportEvent(TaskCenterEventRequest request) {
if (!enabled) { if (!enabled) {
return; return;

View File

@ -34,6 +34,7 @@ import com.red.circle.tool.core.num.ArithmeticUtils;
import com.red.circle.tool.core.tuple.PennyAmount; import com.red.circle.tool.core.tuple.PennyAmount;
import com.red.circle.other.inner.asserts.user.UserErrorCode; import com.red.circle.other.inner.asserts.user.UserErrorCode;
import com.red.circle.wallet.app.common.invite.InviteCampaignGoClient; import com.red.circle.wallet.app.common.invite.InviteCampaignGoClient;
import com.red.circle.wallet.app.common.task.TaskCenterGoClient;
import com.red.circle.wallet.app.dto.cmd.FreightShipSendCmd; import com.red.circle.wallet.app.dto.cmd.FreightShipSendCmd;
import com.red.circle.wallet.app.dto.cmd.SalaryWithdrawCmd; import com.red.circle.wallet.app.dto.cmd.SalaryWithdrawCmd;
import com.red.circle.wallet.domain.gateway.FreightShipGateway; import com.red.circle.wallet.domain.gateway.FreightShipGateway;
@ -96,6 +97,7 @@ public class SendFreightShipCmdExe {
private final InviteCampaignGoClient inviteCampaignGoClient; private final InviteCampaignGoClient inviteCampaignGoClient;
private final UserRegionClient userRegionClient; private final UserRegionClient userRegionClient;
private final OfficialNoticeClient officialNoticeClient; private final OfficialNoticeClient officialNoticeClient;
private final TaskCenterGoClient taskCenterGoClient;
public BigDecimal execute(FreightShipSendCmd cmd) { public BigDecimal execute(FreightShipSendCmd cmd) {
@ -190,6 +192,13 @@ public class SendFreightShipCmdExe {
// 累计充值 // 累计充值
incrRechargeQuantity(cmd); incrRechargeQuantity(cmd);
taskCenterGoClient.reportShippingAgentRechargeGold(cmd.requireReqSysOrigin(),
cmd.getAcceptUserId(),
runningWater.getId(),
cmd.getQuantity(),
getGoldToSUD(cmd),
cmd.requiredReqUserId(),
cmd.getReqTraceId());
notifyInviteCampaignRecharge(cmd, runningWater); notifyInviteCampaignRecharge(cmd, runningWater);
//累计今日充值每日累计充值抽奖活动 //累计今日充值每日累计充值抽奖活动

View File

@ -0,0 +1,145 @@
package com.red.circle.wallet.app.common.task;
import com.alibaba.fastjson.JSON;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import lombok.Data;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class TaskCenterGoClient {
private static final String EVENT_TYPE_RECHARGE_GOLD = "RECHARGE_GOLD";
@Value("${task.center.go.enabled:true}")
private boolean enabled;
@Value("${task.center.go.base-url:}")
private String baseUrl;
@Value("${task.center.go.internal-token:}")
private String internalToken;
@Value("${task.center.go.timeout-millis:3000}")
private Integer timeoutMillis;
public void reportShippingAgentRechargeGold(String sysOrigin,
Long userId,
Long runningWaterId,
BigDecimal goldQuantity,
BigDecimal usdQuantity,
Long freightUserId,
String traceId) {
if (Objects.isNull(userId) || Objects.isNull(runningWaterId) || Objects.isNull(goldQuantity)) {
return;
}
long deltaValue = goldQuantity.setScale(0, RoundingMode.DOWN).longValue();
if (deltaValue <= 0) {
return;
}
Map<String, Object> payload = new HashMap<>();
payload.put("channel", "SHIPPING_AGENT");
payload.put("runningWaterId", runningWaterId);
payload.put("goldQuantity", goldQuantity);
payload.put("usdQuantity", usdQuantity);
payload.put("freightUserId", freightUserId);
payload.put("traceId", traceId);
reportEvent(new TaskCenterEventRequest()
.setSysOrigin(sysOrigin)
.setEventId("RECHARGE_GOLD:SHIPPING_AGENT:" + runningWaterId)
.setEventType(EVENT_TYPE_RECHARGE_GOLD)
.setUserId(userId)
.setDeltaValue(deltaValue)
.setOccurredAt(Instant.now().toString())
.setPayload(payload));
}
private void reportEvent(TaskCenterEventRequest request) {
if (!enabled) {
return;
}
String normalizedBaseUrl = trimRightSlash(Objects.toString(baseUrl, "").trim());
if (normalizedBaseUrl.isEmpty()) {
log.warn("Task center go baseUrl is blank, skip event report. eventId={}", request.getEventId());
return;
}
HttpURLConnection connection = null;
try {
URL url = new URL(normalizedBaseUrl + "/internal/task-center/event");
connection = (HttpURLConnection) url.openConnection();
int timeout = Objects.requireNonNullElse(timeoutMillis, 3000);
connection.setConnectTimeout(timeout);
connection.setReadTimeout(timeout);
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("Accept", "application/json");
connection.setRequestProperty("X-Internal-Token", Objects.toString(internalToken, ""));
byte[] body = JSON.toJSONString(request).getBytes(StandardCharsets.UTF_8);
try (OutputStream outputStream = connection.getOutputStream()) {
outputStream.write(body);
}
int status = connection.getResponseCode();
if (status < 200 || status >= 300) {
log.warn("Task center go report failed. status={}, body={}, eventId={}",
status, readResponse(connection), request.getEventId());
}
} catch (Exception e) {
log.warn("Task center go report error. eventId={}", request.getEventId(), e);
} finally {
if (Objects.nonNull(connection)) {
connection.disconnect();
}
}
}
private String readResponse(HttpURLConnection connection) {
try (InputStream inputStream = connection.getErrorStream() != null
? connection.getErrorStream()
: connection.getInputStream()) {
if (Objects.isNull(inputStream)) {
return "";
}
return new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
} catch (Exception e) {
return "";
}
}
private String trimRightSlash(String value) {
while (value.endsWith("/")) {
value = value.substring(0, value.length() - 1);
}
return value;
}
@Data
@Accessors(chain = true)
private static class TaskCenterEventRequest {
private String sysOrigin;
private String eventId;
private String eventType;
private Long userId;
private Long deltaValue;
private String occurredAt;
private Map<String, Object> payload;
}
}

View File

@ -18,7 +18,7 @@ public interface WalletGoldBalanceService extends BaseService<WalletGoldBalance>
/** /**
* 初始化钱包不存在创建. * 初始化钱包不存在创建.
*/ */
void init(String sysOrigin, Long userId, Long income, Long expenditure); boolean init(String sysOrigin, Long userId, Long income, Long expenditure);
/** /**
* 获取用户余额(注意可能出现负数). * 获取用户余额(注意可能出现负数).
@ -30,12 +30,12 @@ public interface WalletGoldBalanceService extends BaseService<WalletGoldBalance>
/** /**
* 增加收入. * 增加收入.
*/ */
void incrIncome(Long userId, Long amount); boolean incrIncome(Long userId, Long amount);
/** /**
* 增加支出. * 增加支出.
*/ */
void incrExpenditure(Long userId, Long amount); boolean incrExpenditure(Long userId, Long amount);
/** /**
* 增加支出. * 增加支出.

View File

@ -32,9 +32,9 @@ public class WalletGoldBalanceServiceImpl extends
} }
@Override @Override
public void init(String sysOrigin, Long userId, Long income, Long expenditure) { public boolean init(String sysOrigin, Long userId, Long income, Long expenditure) {
try { try {
save(new WalletGoldBalance() return save(new WalletGoldBalance()
.setId(userId) .setId(userId)
.setSysOrigin(sysOrigin) .setSysOrigin(sysOrigin)
.setIncome(income) .setIncome(income)
@ -42,6 +42,7 @@ public class WalletGoldBalanceServiceImpl extends
); );
} catch (DuplicateKeyException ex) { } catch (DuplicateKeyException ex) {
// ignore // ignore
return false;
} }
} }
@ -61,8 +62,8 @@ public class WalletGoldBalanceServiceImpl extends
@Override @Override
public void incrIncome(Long userId, Long amount) { public boolean incrIncome(Long userId, Long amount) {
update().set(WalletGoldBalance::getUpdateTime, DateUtils.now()) return update().set(WalletGoldBalance::getUpdateTime, DateUtils.now())
.setSql(String.format("income=income+%s", amount)) .setSql(String.format("income=income+%s", amount))
.eq(WalletGoldBalance::getId, userId) .eq(WalletGoldBalance::getId, userId)
.last(PageConstant.LIMIT_ONE) .last(PageConstant.LIMIT_ONE)
@ -70,8 +71,8 @@ public class WalletGoldBalanceServiceImpl extends
} }
@Override @Override
public void incrExpenditure(Long userId, Long amount) { public boolean incrExpenditure(Long userId, Long amount) {
update().set(WalletGoldBalance::getUpdateTime, DateUtils.now()) return update().set(WalletGoldBalance::getUpdateTime, DateUtils.now())
.setSql(String.format("expenditure=expenditure+%s", amount)) .setSql(String.format("expenditure=expenditure+%s", amount))
.eq(WalletGoldBalance::getId, userId) .eq(WalletGoldBalance::getId, userId)
.last(PageConstant.LIMIT_ONE) .last(PageConstant.LIMIT_ONE)

View File

@ -150,18 +150,48 @@ public class WalletAssetSyncTool {
*/ */
public void syncGold(WalletReceiptSyncEvent receipt) { public void syncGold(WalletReceiptSyncEvent receipt) {
try { try {
if (receipt.checkTypeIncome()) { if (!syncGoldBalanceChange(receipt)) {
walletGoldBalanceService.incrIncome(receipt.getUserId(), initMissingGoldBalance(receipt);
receipt.getAmount().getPennyAmount());
}
if (receipt.checkExpenditure()) {
walletGoldBalanceService.incrExpenditure(receipt.getUserId(),
receipt.getAmount().getPennyAmount());
} }
} catch (Exception ex) { } catch (Exception ex) {
saveErrorLog(receipt, WalletErrorLogReason.REST_BALANCE); saveErrorLog(receipt, WalletErrorLogReason.REST_BALANCE);
ex.printStackTrace(); throw new IllegalStateException("sync gold balance failed", ex);
}
}
private boolean syncGoldBalanceChange(WalletReceiptSyncEvent receipt) {
if (receipt.checkTypeIncome()) {
return walletGoldBalanceService.incrIncome(receipt.getUserId(),
receipt.getAmount().getPennyAmount());
}
if (receipt.checkExpenditure()) {
return walletGoldBalanceService.incrExpenditure(receipt.getUserId(),
receipt.getAmount().getPennyAmount());
}
return true;
}
private void initMissingGoldBalance(WalletReceiptSyncEvent receipt) {
long amount = receipt.getAmount().getPennyAmount();
long balance = receipt.getBalance().getPennyAmount();
long income = Math.max(balance, 0L);
long expenditure = 0L;
if (receipt.checkExpenditure()) {
income = Math.max(balance + amount, 0L);
expenditure = Math.max(amount, 0L);
}
if (walletGoldBalanceService.init(receipt.getSysOrigin(), receipt.getUserId(), income,
expenditure)) {
log.warn("初始化缺失金币余额记录: userId={}, eventType={}, eventId={}",
receipt.getUserId(), receipt.getEventType(), receipt.getEventId());
return;
}
if (!syncGoldBalanceChange(receipt)) {
throw new IllegalStateException(
"sync gold balance no rows after init: userId=" + receipt.getUserId());
} }
} }

View File

@ -0,0 +1,70 @@
package com.red.circle.wallet.infra.tool;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.red.circle.common.business.core.enums.ReceiptType;
import com.red.circle.mq.business.model.event.wallet.WalletReceiptSyncEvent;
import com.red.circle.tool.core.tuple.PennyAmount;
import com.red.circle.wallet.infra.database.rds.service.WalletGoldBalanceService;
import org.junit.jupiter.api.Test;
import org.mockito.InOrder;
class WalletAssetSyncToolTest {
@Test
void syncGoldInitializesMissingBalanceWhenExpenditureUpdateMissesRow() {
WalletGoldBalanceService balanceService = mock(WalletGoldBalanceService.class);
when(balanceService.incrExpenditure(1001L, 1_500_000L)).thenReturn(false);
when(balanceService.init("LIKEI", 1001L, 10_000_000L, 1_500_000L)).thenReturn(true);
WalletAssetSyncTool tool = newTool(balanceService);
WalletReceiptSyncEvent event = new WalletReceiptSyncEvent();
event.setReceiptType(ReceiptType.EXPENDITURE);
event.setSysOrigin("LIKEI");
event.setUserId(1001L);
event.setEventType("VOICE_ROOM_RED_PACKET_SEND");
event.setEventId("VRPS:123");
event.setAmount(PennyAmount.ofPenny(1_500_000L));
event.setBalance(PennyAmount.ofPenny(8_500_000L));
tool.syncGold(event);
InOrder ordered = inOrder(balanceService);
ordered.verify(balanceService).incrExpenditure(1001L, 1_500_000L);
ordered.verify(balanceService).init("LIKEI", 1001L, 10_000_000L, 1_500_000L);
verify(balanceService, never()).incrIncome(1001L, 1_500_000L);
}
@Test
void syncGoldInitializesMissingBalanceWhenIncomeUpdateMissesRow() {
WalletGoldBalanceService balanceService = mock(WalletGoldBalanceService.class);
when(balanceService.incrIncome(1002L, 1_500_000L)).thenReturn(false);
when(balanceService.init("LIKEI", 1002L, 11_500_000L, 0L)).thenReturn(true);
WalletAssetSyncTool tool = newTool(balanceService);
WalletReceiptSyncEvent event = new WalletReceiptSyncEvent();
event.setReceiptType(ReceiptType.INCOME);
event.setSysOrigin("LIKEI");
event.setUserId(1002L);
event.setEventType("VOICE_ROOM_RED_PACKET_CLAIM");
event.setEventId("VRPC:123");
event.setAmount(PennyAmount.ofPenny(1_500_000L));
event.setBalance(PennyAmount.ofPenny(11_500_000L));
tool.syncGold(event);
InOrder ordered = inOrder(balanceService);
ordered.verify(balanceService).incrIncome(1002L, 1_500_000L);
ordered.verify(balanceService).init("LIKEI", 1002L, 11_500_000L, 0L);
verify(balanceService, never()).incrExpenditure(1002L, 1_500_000L);
}
private WalletAssetSyncTool newTool(WalletGoldBalanceService balanceService) {
return new WalletAssetSyncTool(null, null, null, null, null, balanceService, null, null, null,
null);
}
}