首冲奖励

This commit is contained in:
hy001 2026-05-22 10:10:15 +08:00
parent 8e37a3d333
commit e897d635ea
6 changed files with 291 additions and 17 deletions

View File

@ -0,0 +1,36 @@
package com.red.circle.mq.business.model.event.pay;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import java.io.Serializable;
import java.util.Map;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 通用充值成功事件
*/
@Data
@Accessors(chain = true)
public class RechargeSuccessEvent implements Serializable {
private static final long serialVersionUID = 1L;
private String eventId;
private String sysOrigin;
@JsonSerialize(using = ToStringSerializer.class)
private Long userId;
@JsonSerialize(using = ToStringSerializer.class)
private Long amountCents;
private String amountUsd;
private String currency;
private String payPlatform;
private String paymentMethod;
private String sourceOrderId;
private String orderId;
private String occurredAt;
private Map<String, Object> payload;
}

View File

@ -17,13 +17,18 @@ public interface QueueNameConstant {
*/
String LOGIN_REGISTER = "LOGIN_REGISTER";
/**
* 购买成功
*/
String BUY_SUCCESS = "BUY_SUCCESS";
/**
* 视频语音房间会话
/**
* 购买成功
*/
String BUY_SUCCESS = "BUY_SUCCESS";
/**
* 通用充值成功
*/
String RECHARGE_SUCCESS = "RECHARGE_SUCCESS";
/**
* 视频语音房间会话
*/
String LIVE_VOICE_SESSION = "LIVE_VOICE_SESSION";

View File

@ -0,0 +1,12 @@
package com.red.circle.mq.rocket.business.streams;
import com.red.circle.mq.rocket.business.constant.QueueNameConstant;
/**
* 通用充值成功事件
*/
public interface RechargeSuccessSink {
String INPUT = QueueNameConstant.RECHARGE_SUCCESS;
String TAG = "recharge_success_v1";
}

View File

@ -8,8 +8,9 @@ import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.response.CommonErrorCode;
import com.red.circle.framework.dto.ResultResponse;
import com.red.circle.framework.web.props.EnvProperties;
import com.red.circle.mq.business.model.event.pay.BuySuccessEvent;
import com.red.circle.order.app.command.pay.web.strategy.PayEnv;
import com.red.circle.mq.business.model.event.pay.BuySuccessEvent;
import com.red.circle.order.app.common.task.RechargeSuccessEventPublisher;
import com.red.circle.order.app.command.pay.web.strategy.PayEnv;
import com.red.circle.order.domain.gateway.UserRechargeCountGateway;
import com.red.circle.order.infra.database.mongo.order.InAppPurchaseCollectionReceiptService;
import com.red.circle.order.infra.database.mongo.order.InAppPurchaseDetailsService;
@ -76,9 +77,10 @@ public class InAppPurchaseCommon {
private final OrderPurchaseHistoryService orderPurchaseHistoryService;
private final InAppPurchaseCollectionReceiptService inAppPurchaseCollectionReceiptService;
private final UserLevelClient userLevelClient;
private final PropsActivityClient propsActivityClient;
private final PropsActivityClient propsActivityClient;
private final UserOneTimeTaskClient userOneTimeTaskClient;
private final UserProfileClient userProfileClient;
private final RechargeSuccessEventPublisher rechargeSuccessEventPublisher;
public void inAppPurchaseReceipt(InAppPurchaseDetails order) {
inAppPurchaseReceipt(order,
@ -192,13 +194,15 @@ public class InAppPurchaseCommon {
userSvipClient.incrIntegral(order.getAcceptUserId(),
amount.longValue());
// 被邀请人充值邀请人获得佣金
inviteUserClient.processRechargeCommission(amount,
order.getSysOrigin(), order.getAcceptUserId());
// 发送首充奖励
completedFirstCharge(order.getAcceptUserId(), order.getSysOrigin());
}
// 被邀请人充值邀请人获得佣金
inviteUserClient.processRechargeCommission(amount,
order.getSysOrigin(), order.getAcceptUserId());
rechargeSuccessEventPublisher.reportPaymentRecharge(order);
// 发送首充奖励
completedFirstCharge(order.getAcceptUserId(), order.getSysOrigin());
}
private void completedFirstCharge(Long userId, String sysOrigin) {
UserProfileDTO userProfileDTO = userProfileClient.getByUserId(userId).getBody();

View File

@ -0,0 +1,214 @@
package com.red.circle.order.app.common.task;
import com.red.circle.component.mq.MessageEvent;
import com.red.circle.mq.business.model.event.pay.BuySuccessEvent;
import com.red.circle.mq.business.model.event.pay.RechargeSuccessEvent;
import com.red.circle.mq.rocket.business.service.MessageSenderService;
import com.red.circle.mq.rocket.business.streams.RechargeSuccessSink;
import com.red.circle.order.infra.database.mongo.order.entity.InAppPurchaseDetails;
import com.red.circle.order.infra.database.mongo.order.entity.assist.InAppPurchaseProduct;
import com.red.circle.order.inner.model.enums.PayApplicationCommodityType;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Timestamp;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class RechargeSuccessEventPublisher {
private static final String GOOGLE = "GOOGLE";
private static final String APPLE = "APPLE";
private static final String THIRD_PARTY = "THIRD_PARTY";
private static final String UNKNOWN = "UNKNOWN";
private final ObjectProvider<MessageSenderService> messageSenderServiceProvider;
@Value("${recharge.success.mq.enabled:true}")
private boolean mqEnabled;
@Value("${recharge.success.mq.topic:RC_DEFAULT_APP_ORDINARY}")
private String mqTopic;
@Value("${recharge.success.mq.tag:" + RechargeSuccessSink.TAG + "}")
private String mqTag;
public RechargeSuccessEventPublisher(ObjectProvider<MessageSenderService> messageSenderServiceProvider) {
this.messageSenderServiceProvider = messageSenderServiceProvider;
}
public void reportAppPurchaseRecharge(BuySuccessEvent event) {
if (Objects.isNull(event)) {
return;
}
Long userId = event.getAcceptUserId();
Long purchaseHistoryId = event.getPurchaseHistoryId();
BigDecimal amountUsd = Objects.nonNull(event.getProductConfig())
? event.getProductConfig().getUnitPrice()
: null;
Long amountCents = toCents(amountUsd);
if (Objects.isNull(userId) || Objects.isNull(purchaseHistoryId) || !positive(amountCents)) {
return;
}
Map<String, Object> payload = new HashMap<>();
payload.put("payerUserId", event.getUserId());
payload.put("acceptUserId", userId);
payload.put("purchaseHistoryId", purchaseHistoryId);
payload.put("candyQuantity", event.getCandyQuantity());
payload.put("platform", event.getPlatform());
payload.put("payPlatform", event.getPayPlatform());
String sourceOrderId = String.valueOf(purchaseHistoryId);
String payPlatform = normalizePayPlatform(defaultIfBlank(event.getPayPlatform(), event.getPlatform()));
String paymentMethod = classifyPaymentMethod(payPlatform);
publish(new RechargeSuccessEvent()
.setEventId("RECHARGE_SUCCESS:" + paymentMethod + ":" + sourceOrderId)
.setSysOrigin(event.getSysOrigin())
.setUserId(userId)
.setAmountCents(amountCents)
.setAmountUsd(formatAmount(amountUsd))
.setCurrency("USD")
.setPayPlatform(payPlatform)
.setPaymentMethod(paymentMethod)
.setSourceOrderId(sourceOrderId)
.setOrderId(sourceOrderId)
.setOccurredAt(toInstantString(event.getBuyDateTime()))
.setPayload(payload));
}
public void reportPaymentRecharge(InAppPurchaseDetails order) {
if (Objects.isNull(order) || !order.receiptTypeEqPayment()) {
return;
}
InAppPurchaseProduct product = order.firstProduct();
if (Objects.isNull(product) || !PayApplicationCommodityType.GOLD.eq(product.getCode())) {
return;
}
String payPlatform = normalizePayPlatform(Objects.nonNull(order.getFactory()) ? order.getFactory().getFactoryCode() : "");
String paymentMethod = classifyPaymentMethod(payPlatform);
BigDecimal amountUsd = firstPositive(order.getAmountUsd(), product.getAmountUsd());
Long amountCents = toCents(amountUsd);
if (Objects.isNull(order.getAcceptUserId()) || isBlank(order.getId()) || !positive(amountCents)) {
return;
}
Map<String, Object> payload = new HashMap<>();
payload.put("trackId", order.getTrackId());
payload.put("sourceOrderId", order.getId());
payload.put("orderId", order.getOrderId());
payload.put("factoryCode", payPlatform);
payload.put("factoryChannelCode", Objects.nonNull(order.getFactory()) ? order.getFactory().getFactoryChannelCode() : null);
payload.put("platform", Objects.nonNull(order.getFactory()) ? order.getFactory().getPlatform() : null);
payload.put("productCode", product.getCode());
payload.put("productContent", product.getContent());
publish(new RechargeSuccessEvent()
.setEventId("RECHARGE_SUCCESS:" + paymentMethod + ":" + payPlatform + ":" + order.getId())
.setSysOrigin(order.getSysOrigin())
.setUserId(order.getAcceptUserId())
.setAmountCents(amountCents)
.setAmountUsd(formatAmount(amountUsd))
.setCurrency(defaultIfBlank(order.getCurrency(), "USD"))
.setPayPlatform(payPlatform)
.setPaymentMethod(paymentMethod)
.setSourceOrderId(order.getId())
.setOrderId(defaultIfBlank(order.getOrderId(), order.getId()))
.setOccurredAt(firstTime(order.getPurchaseDateMs(), order.getUpdateTime(), order.getCreateTime()))
.setPayload(payload));
}
private void publish(RechargeSuccessEvent event) {
if (!mqEnabled) {
return;
}
try {
MessageSenderService senderService = messageSenderServiceProvider.getIfAvailable();
if (Objects.isNull(senderService)) {
log.warn("Recharge success mq sender missing, skip event report. eventId={}", event.getEventId());
return;
}
senderService.sendEventMessage(MessageEvent.builder()
.consumeId(event.getEventId())
.topicString(defaultIfBlank(mqTopic, "RC_DEFAULT_APP_ORDINARY"))
.tag(defaultIfBlank(mqTag, RechargeSuccessSink.TAG))
.body(event)
.build());
} catch (Exception e) {
log.warn("Recharge success mq report error. eventId={}", event.getEventId(), e);
}
}
private String normalizePayPlatform(String value) {
String normalized = Objects.toString(value, "").trim().toUpperCase();
return normalized.isEmpty() ? UNKNOWN : normalized;
}
private String classifyPaymentMethod(String payPlatform) {
if (Objects.equals(GOOGLE, payPlatform)) {
return GOOGLE;
}
if (Objects.equals(APPLE, payPlatform)) {
return APPLE;
}
if (Objects.equals(UNKNOWN, payPlatform)) {
return UNKNOWN;
}
return THIRD_PARTY;
}
private Long toCents(BigDecimal amount) {
if (Objects.isNull(amount) || amount.compareTo(BigDecimal.ZERO) <= 0) {
return null;
}
return amount.multiply(BigDecimal.valueOf(100)).setScale(0, RoundingMode.HALF_UP).longValue();
}
private BigDecimal firstPositive(BigDecimal first, BigDecimal second) {
if (Objects.nonNull(first) && first.compareTo(BigDecimal.ZERO) > 0) {
return first;
}
return second;
}
private boolean positive(Long value) {
return Objects.nonNull(value) && value > 0;
}
private String toInstantString(Timestamp timestamp) {
return Objects.nonNull(timestamp) ? timestamp.toInstant().toString() : Instant.now().toString();
}
private String firstTime(Timestamp first, Timestamp second, Timestamp third) {
if (Objects.nonNull(first)) {
return first.toInstant().toString();
}
if (Objects.nonNull(second)) {
return second.toInstant().toString();
}
if (Objects.nonNull(third)) {
return third.toInstant().toString();
}
return Instant.now().toString();
}
private String formatAmount(BigDecimal amount) {
return Objects.isNull(amount) ? null : amount.stripTrailingZeros().toPlainString();
}
private String defaultIfBlank(String value, String fallback) {
return isBlank(value) ? fallback : value.trim();
}
private boolean isBlank(String value) {
return Objects.toString(value, "").trim().isEmpty();
}
}

View File

@ -17,6 +17,7 @@ import com.red.circle.framework.dto.ResultResponse;
import com.red.circle.mq.business.model.event.pay.BuySuccessEvent;
import com.red.circle.mq.business.model.event.pay.PurchaseProductEvent;
import com.red.circle.mq.rocket.business.streams.BuySuccessSink;
import com.red.circle.order.app.common.task.RechargeSuccessEventPublisher;
import com.red.circle.order.app.common.task.TaskCenterGoClient;
import com.red.circle.other.inner.endpoint.activity.CumulativeRechargeClient;
import com.red.circle.other.inner.endpoint.activity.PropsActivityClient;
@ -71,6 +72,7 @@ public class BuySuccessListener implements MessageListener {
private final CumulativeRechargeClient cumulativeRechargeClient;
private final RedisService redisService;
private final TaskCenterGoClient taskCenterGoClient;
private final RechargeSuccessEventPublisher rechargeSuccessEventPublisher;
@Override
public Action consume(ConsumerMessage message) {
@ -133,6 +135,7 @@ public class BuySuccessListener implements MessageListener {
eventBody.getPlatform(),
eventBody.getPayPlatform(),
Objects.nonNull(eventBody.getBuyDateTime()) ? eventBody.getBuyDateTime().toInstant() : null);
rechargeSuccessEventPublisher.reportAppPurchaseRecharge(eventBody);
userExpandClient.updatePurchasingToTrue(eventBody.getUserId());