Compare commits
2 Commits
c8f8311cae
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| e657a22b10 | |||
| d654777a02 |
@@ -146,7 +146,7 @@ public class GooglePlayApiClient {
|
|||||||
JsonNode offerDetails = firstLineItem.path("productOfferDetails");
|
JsonNode offerDetails = firstLineItem.path("productOfferDetails");
|
||||||
|
|
||||||
String state = mapOneTimeState(text(root.path("purchaseStateContext"), "purchaseState"));
|
String state = mapOneTimeState(text(root.path("purchaseStateContext"), "purchaseState"));
|
||||||
|
String purchaseOptionId = text(offerDetails, "purchaseOptionId");
|
||||||
// 修正:一次性购买的订单号字段名为 "orderId"
|
// 修正:一次性购买的订单号字段名为 "orderId"
|
||||||
String googleOrderId = text(root, "orderId");
|
String googleOrderId = text(root, "orderId");
|
||||||
|
|
||||||
@@ -156,6 +156,7 @@ public class GooglePlayApiClient {
|
|||||||
.productId(text(firstLineItem, "productId"))
|
.productId(text(firstLineItem, "productId"))
|
||||||
.productType(GooglePlayConstants.PRODUCT_TYPE_ONE_TIME)
|
.productType(GooglePlayConstants.PRODUCT_TYPE_ONE_TIME)
|
||||||
.purchaseToken(purchaseToken)
|
.purchaseToken(purchaseToken)
|
||||||
|
.purchaseOptionId(purchaseOptionId)
|
||||||
.orderKey(resolveOrderKey(googleOrderId, purchaseToken))
|
.orderKey(resolveOrderKey(googleOrderId, purchaseToken))
|
||||||
.googleOrderId(googleOrderId)
|
.googleOrderId(googleOrderId)
|
||||||
.linkedPurchaseToken(null)
|
.linkedPurchaseToken(null)
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ public final class GooglePlayConstants {
|
|||||||
public static final String CONSUMPTION_NOT_APPLICABLE = "NOT_APPLICABLE";
|
public static final String CONSUMPTION_NOT_APPLICABLE = "NOT_APPLICABLE";
|
||||||
|
|
||||||
public static final String DELIVERY_PENDING = "PENDING";
|
public static final String DELIVERY_PENDING = "PENDING";
|
||||||
|
public static final String DELIVERY_PROCESSING = "PROCESSING";
|
||||||
public static final String DELIVERY_DELIVERED = "DELIVERED";
|
public static final String DELIVERY_DELIVERED = "DELIVERED";
|
||||||
public static final String DELIVERY_REVOKED = "REVOKED";
|
public static final String DELIVERY_REVOKED = "REVOKED";
|
||||||
public static final String DELIVERY_NOT_REQUIRED = "NOT_REQUIRED";
|
public static final String DELIVERY_NOT_REQUIRED = "NOT_REQUIRED";
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ public class GooglePlayEntitlementApplier {
|
|||||||
private final GooglePlayUserEntitlementMapper entitlementMapper;
|
private final GooglePlayUserEntitlementMapper entitlementMapper;
|
||||||
private final GooglePlayVipBenefitService vipBenefitService;
|
private final GooglePlayVipBenefitService vipBenefitService;
|
||||||
private final GooglePlayWalletBenefitService walletBenefitService;
|
private final GooglePlayWalletBenefitService walletBenefitService;
|
||||||
|
private final GooglePlayOrderDeliveryGuard orderDeliveryGuard;
|
||||||
|
|
||||||
public GooglePlayUserEntitlement apply(Long userId,
|
public GooglePlayUserEntitlement apply(Long userId,
|
||||||
KeyboardProductItems product,
|
KeyboardProductItems product,
|
||||||
@@ -29,6 +30,7 @@ public class GooglePlayEntitlementApplier {
|
|||||||
GooglePlayOrder order) {
|
GooglePlayOrder order) {
|
||||||
String benefitType = resolveBenefitType(product, snapshot);
|
String benefitType = resolveBenefitType(product, snapshot);
|
||||||
String entitlementKey = resolveEntitlementKey(benefitType, product.getProductId());
|
String entitlementKey = resolveEntitlementKey(benefitType, product.getProductId());
|
||||||
|
boolean grantOwned = orderDeliveryGuard.prepareGrant(benefitType, snapshot, order);
|
||||||
GooglePlayUserEntitlement entitlement = loadEntitlement(snapshot.getPurchaseToken(), entitlementKey);
|
GooglePlayUserEntitlement entitlement = loadEntitlement(snapshot.getPurchaseToken(), entitlementKey);
|
||||||
if (entitlement == null) {
|
if (entitlement == null) {
|
||||||
entitlement = new GooglePlayUserEntitlement();
|
entitlement = new GooglePlayUserEntitlement();
|
||||||
@@ -37,9 +39,11 @@ public class GooglePlayEntitlementApplier {
|
|||||||
fillCommonFields(entitlement, userId, product, snapshot, order, benefitType, entitlementKey);
|
fillCommonFields(entitlement, userId, product, snapshot, order, benefitType, entitlementKey);
|
||||||
switch (benefitType) {
|
switch (benefitType) {
|
||||||
case GooglePlayConstants.ENTITLEMENT_VIP_SUBSCRIPTION -> applySubscriptionVip(userId, product, snapshot, order, entitlement);
|
case GooglePlayConstants.ENTITLEMENT_VIP_SUBSCRIPTION -> applySubscriptionVip(userId, product, snapshot, order, entitlement);
|
||||||
case GooglePlayConstants.ENTITLEMENT_VIP_ONE_TIME -> applyOneTimeVip(userId, product, snapshot, order, entitlement);
|
case GooglePlayConstants.ENTITLEMENT_VIP_ONE_TIME ->
|
||||||
case GooglePlayConstants.ENTITLEMENT_WALLET_TOP_UP -> applyWalletTopUp(userId, product, snapshot, order, entitlement);
|
applyOneTimeVip(userId, product, snapshot, order, entitlement, grantOwned);
|
||||||
default -> applyNonConsumable(snapshot, order, entitlement);
|
case GooglePlayConstants.ENTITLEMENT_WALLET_TOP_UP ->
|
||||||
|
applyWalletTopUp(userId, product, snapshot, order, entitlement, grantOwned);
|
||||||
|
default -> applyNonConsumable(snapshot, order, entitlement, grantOwned);
|
||||||
}
|
}
|
||||||
saveEntitlement(entitlement);
|
saveEntitlement(entitlement);
|
||||||
return entitlement;
|
return entitlement;
|
||||||
@@ -94,9 +98,10 @@ public class GooglePlayEntitlementApplier {
|
|||||||
KeyboardProductItems product,
|
KeyboardProductItems product,
|
||||||
GooglePlayPurchaseSnapshot snapshot,
|
GooglePlayPurchaseSnapshot snapshot,
|
||||||
GooglePlayOrder order,
|
GooglePlayOrder order,
|
||||||
GooglePlayUserEntitlement entitlement) {
|
GooglePlayUserEntitlement entitlement,
|
||||||
|
boolean grantOwned) {
|
||||||
if (GooglePlayConstants.STATE_ACTIVE.equals(snapshot.getState())) {
|
if (GooglePlayConstants.STATE_ACTIVE.equals(snapshot.getState())) {
|
||||||
grantOneTimeVip(userId, product, order, entitlement);
|
grantOneTimeVip(userId, product, order, entitlement, grantOwned);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
revokeVipEntitlement(userId, order, entitlement);
|
revokeVipEntitlement(userId, order, entitlement);
|
||||||
@@ -106,13 +111,14 @@ public class GooglePlayEntitlementApplier {
|
|||||||
KeyboardProductItems product,
|
KeyboardProductItems product,
|
||||||
GooglePlayPurchaseSnapshot snapshot,
|
GooglePlayPurchaseSnapshot snapshot,
|
||||||
GooglePlayOrder order,
|
GooglePlayOrder order,
|
||||||
GooglePlayUserEntitlement entitlement) {
|
GooglePlayUserEntitlement entitlement,
|
||||||
|
boolean grantOwned) {
|
||||||
BigDecimal amount = resolveWalletAmount(product);
|
BigDecimal amount = resolveWalletAmount(product);
|
||||||
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
|
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
throw new BusinessException(ErrorCode.PRODUCT_QUOTA_NOT_SET);
|
throw new BusinessException(ErrorCode.PRODUCT_QUOTA_NOT_SET);
|
||||||
}
|
}
|
||||||
if (GooglePlayConstants.STATE_ACTIVE.equals(snapshot.getState())) {
|
if (GooglePlayConstants.STATE_ACTIVE.equals(snapshot.getState())) {
|
||||||
grantWalletTopUp(userId, product, order, entitlement, amount);
|
grantWalletTopUp(userId, product, order, entitlement, amount, grantOwned);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
revokeWalletTopUp(userId, order, entitlement, amount);
|
revokeWalletTopUp(userId, order, entitlement, amount);
|
||||||
@@ -120,9 +126,14 @@ public class GooglePlayEntitlementApplier {
|
|||||||
|
|
||||||
private void applyNonConsumable(GooglePlayPurchaseSnapshot snapshot,
|
private void applyNonConsumable(GooglePlayPurchaseSnapshot snapshot,
|
||||||
GooglePlayOrder order,
|
GooglePlayOrder order,
|
||||||
GooglePlayUserEntitlement entitlement) {
|
GooglePlayUserEntitlement entitlement,
|
||||||
|
boolean grantOwned) {
|
||||||
boolean active = GooglePlayConstants.STATE_ACTIVE.equals(snapshot.getState())
|
boolean active = GooglePlayConstants.STATE_ACTIVE.equals(snapshot.getState())
|
||||||
|| GooglePlayConstants.STATE_CANCELED.equals(snapshot.getState());
|
|| GooglePlayConstants.STATE_CANCELED.equals(snapshot.getState());
|
||||||
|
if (!grantOwned && active) {
|
||||||
|
entitlement.setActive(GooglePlayConstants.DELIVERY_DELIVERED.equals(order.getDeliveryStatus()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
entitlement.setActive(active);
|
entitlement.setActive(active);
|
||||||
entitlement.setStartTime(snapshot.getStartTime());
|
entitlement.setStartTime(snapshot.getStartTime());
|
||||||
entitlement.setEndTime(snapshot.getExpiryTime());
|
entitlement.setEndTime(snapshot.getExpiryTime());
|
||||||
@@ -139,9 +150,10 @@ public class GooglePlayEntitlementApplier {
|
|||||||
private void grantOneTimeVip(Long userId,
|
private void grantOneTimeVip(Long userId,
|
||||||
KeyboardProductItems product,
|
KeyboardProductItems product,
|
||||||
GooglePlayOrder order,
|
GooglePlayOrder order,
|
||||||
GooglePlayUserEntitlement entitlement) {
|
GooglePlayUserEntitlement entitlement,
|
||||||
if (GooglePlayConstants.DELIVERY_DELIVERED.equals(order.getDeliveryStatus())) {
|
boolean grantOwned) {
|
||||||
entitlement.setActive(true);
|
if (!grantOwned || GooglePlayConstants.DELIVERY_DELIVERED.equals(order.getDeliveryStatus())) {
|
||||||
|
entitlement.setActive(GooglePlayConstants.DELIVERY_DELIVERED.equals(order.getDeliveryStatus()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Date expiry = resolveOneTimeVipExpiry(product);
|
Date expiry = resolveOneTimeVipExpiry(product);
|
||||||
@@ -172,9 +184,10 @@ public class GooglePlayEntitlementApplier {
|
|||||||
KeyboardProductItems product,
|
KeyboardProductItems product,
|
||||||
GooglePlayOrder order,
|
GooglePlayOrder order,
|
||||||
GooglePlayUserEntitlement entitlement,
|
GooglePlayUserEntitlement entitlement,
|
||||||
BigDecimal amount) {
|
BigDecimal amount,
|
||||||
if (GooglePlayConstants.DELIVERY_DELIVERED.equals(order.getDeliveryStatus())) {
|
boolean grantOwned) {
|
||||||
entitlement.setActive(true);
|
if (!grantOwned || GooglePlayConstants.DELIVERY_DELIVERED.equals(order.getDeliveryStatus())) {
|
||||||
|
entitlement.setActive(GooglePlayConstants.DELIVERY_DELIVERED.equals(order.getDeliveryStatus()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
walletBenefitService.grant(userId, order.getId(), product.getName(), amount);
|
walletBenefitService.grant(userId, order.getId(), product.getName(), amount);
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package com.yolo.keyborad.googleplay;
|
||||||
|
|
||||||
|
import com.yolo.keyborad.googleplay.model.GooglePlayPurchaseSnapshot;
|
||||||
|
import com.yolo.keyborad.mapper.GooglePlayOrderMapper;
|
||||||
|
import com.yolo.keyborad.model.entity.googleplay.GooglePlayOrder;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class GooglePlayOrderDeliveryGuard {
|
||||||
|
|
||||||
|
private final GooglePlayOrderMapper orderMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 一次性商品在真正发权益前先抢占处理资格,避免并发请求重复发货。
|
||||||
|
*/
|
||||||
|
public boolean prepareGrant(String benefitType, GooglePlayPurchaseSnapshot snapshot, GooglePlayOrder order) {
|
||||||
|
if (!requiresGrantGuard(benefitType, snapshot) || order.getId() == null) {
|
||||||
|
order.setDeliveryOwnershipGranted(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (GooglePlayConstants.DELIVERY_DELIVERED.equals(order.getDeliveryStatus())) {
|
||||||
|
order.setDeliveryOwnershipGranted(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Date now = new Date();
|
||||||
|
int updated = orderMapper.updateDeliveryStatusIfMatch(
|
||||||
|
order.getId(),
|
||||||
|
GooglePlayConstants.DELIVERY_PENDING,
|
||||||
|
GooglePlayConstants.DELIVERY_PROCESSING,
|
||||||
|
now
|
||||||
|
);
|
||||||
|
if (updated == 1) {
|
||||||
|
order.setDeliveryStatus(GooglePlayConstants.DELIVERY_PROCESSING);
|
||||||
|
order.setDeliveryOwnershipGranted(true);
|
||||||
|
order.setUpdatedAt(now);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
refreshDeliveryState(order);
|
||||||
|
order.setDeliveryOwnershipGranted(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean requiresGrantGuard(String benefitType, GooglePlayPurchaseSnapshot snapshot) {
|
||||||
|
if (!GooglePlayConstants.PRODUCT_TYPE_ONE_TIME.equals(snapshot.getProductType())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (GooglePlayConstants.ENTITLEMENT_NON_CONSUMABLE.equals(benefitType)) {
|
||||||
|
return GooglePlayConstants.STATE_ACTIVE.equals(snapshot.getState())
|
||||||
|
|| GooglePlayConstants.STATE_CANCELED.equals(snapshot.getState());
|
||||||
|
}
|
||||||
|
return GooglePlayConstants.STATE_ACTIVE.equals(snapshot.getState());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshDeliveryState(GooglePlayOrder order) {
|
||||||
|
GooglePlayOrder latestOrder = orderMapper.selectById(order.getId());
|
||||||
|
if (latestOrder == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
order.setDeliveryStatus(latestOrder.getDeliveryStatus());
|
||||||
|
order.setGrantedQuantity(latestOrder.getGrantedQuantity());
|
||||||
|
order.setUpdatedAt(latestOrder.getUpdatedAt());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,6 +15,7 @@ import com.yolo.keyborad.model.entity.googleplay.GooglePlayUserEntitlement;
|
|||||||
import com.yolo.keyborad.service.KeyboardProductItemsService;
|
import com.yolo.keyborad.service.KeyboardProductItemsService;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.dao.DuplicateKeyException;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
@@ -32,11 +33,13 @@ public class GooglePlayStateService {
|
|||||||
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public GooglePlaySyncResult sync(GooglePlaySyncCommand command, GooglePlayPurchaseSnapshot snapshot) {
|
public GooglePlaySyncResult sync(GooglePlaySyncCommand command, GooglePlayPurchaseSnapshot snapshot) {
|
||||||
KeyboardProductItems product = loadProduct(snapshot.getBasePlanId());
|
String productId = (snapshot.getBasePlanId() != null) ?
|
||||||
|
snapshot.getBasePlanId() :
|
||||||
|
snapshot.getPurchaseOptionId();
|
||||||
|
KeyboardProductItems product = loadProduct(productId);
|
||||||
GooglePlayOrder order = buildOrder(command, snapshot);
|
GooglePlayOrder order = buildOrder(command, snapshot);
|
||||||
GooglePlayPurchaseToken token = buildToken(command, snapshot);
|
GooglePlayPurchaseToken token = buildToken(command, snapshot);
|
||||||
// 先保存订单以确保 order.id 已生成,钱包充值等权益分发依赖 order.id 写入交易流水
|
persistOrderIfNew(order);
|
||||||
saveOrder(order);
|
|
||||||
GooglePlayUserEntitlement entitlement = null;
|
GooglePlayUserEntitlement entitlement = null;
|
||||||
if (command.getUserId() != null) {
|
if (command.getUserId() != null) {
|
||||||
entitlement = entitlementApplier.apply(command.getUserId(), product, snapshot, order);
|
entitlement = entitlementApplier.apply(command.getUserId(), product, snapshot, order);
|
||||||
@@ -51,8 +54,8 @@ public class GooglePlayStateService {
|
|||||||
.order(order)
|
.order(order)
|
||||||
.token(token)
|
.token(token)
|
||||||
.entitlement(entitlement)
|
.entitlement(entitlement)
|
||||||
.acknowledgeRequired(requiresAcknowledge(snapshot, command.getUserId()))
|
.acknowledgeRequired(requiresAcknowledge(snapshot, command.getUserId(), order))
|
||||||
.consumeRequired(requiresConsume(snapshot, entitlement, command.getUserId()))
|
.consumeRequired(requiresConsume(snapshot, entitlement, command.getUserId(), order))
|
||||||
.linkedPurchaseTokenToSync(resolveLinkedToken(snapshot))
|
.linkedPurchaseTokenToSync(resolveLinkedToken(snapshot))
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
@@ -144,15 +147,22 @@ public class GooglePlayStateService {
|
|||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean requiresAcknowledge(GooglePlayPurchaseSnapshot snapshot, Long userId) {
|
private boolean requiresAcknowledge(GooglePlayPurchaseSnapshot snapshot, Long userId, GooglePlayOrder order) {
|
||||||
return userId != null
|
if (userId == null
|
||||||
&& GooglePlayConstants.ACK_PENDING.equals(snapshot.getAcknowledgementState())
|
|| !GooglePlayConstants.ACK_PENDING.equals(snapshot.getAcknowledgementState())
|
||||||
&& GooglePlayConstants.STATE_ACTIVE.equals(snapshot.getState());
|
|| !GooglePlayConstants.STATE_ACTIVE.equals(snapshot.getState())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!GooglePlayConstants.PRODUCT_TYPE_ONE_TIME.equals(snapshot.getProductType())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return Boolean.TRUE.equals(order.getDeliveryOwnershipGranted());
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean requiresConsume(GooglePlayPurchaseSnapshot snapshot,
|
private boolean requiresConsume(GooglePlayPurchaseSnapshot snapshot,
|
||||||
GooglePlayUserEntitlement entitlement,
|
GooglePlayUserEntitlement entitlement,
|
||||||
Long userId) {
|
Long userId,
|
||||||
|
GooglePlayOrder order) {
|
||||||
if (userId == null || entitlement == null) {
|
if (userId == null || entitlement == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -160,7 +170,8 @@ public class GooglePlayStateService {
|
|||||||
boolean wallet = GooglePlayConstants.ENTITLEMENT_WALLET_TOP_UP.equals(entitlement.getBenefitType());
|
boolean wallet = GooglePlayConstants.ENTITLEMENT_WALLET_TOP_UP.equals(entitlement.getBenefitType());
|
||||||
boolean active = GooglePlayConstants.STATE_ACTIVE.equals(snapshot.getState());
|
boolean active = GooglePlayConstants.STATE_ACTIVE.equals(snapshot.getState());
|
||||||
boolean pending = GooglePlayConstants.CONSUMPTION_PENDING.equals(snapshot.getConsumptionState());
|
boolean pending = GooglePlayConstants.CONSUMPTION_PENDING.equals(snapshot.getConsumptionState());
|
||||||
return oneTime && wallet && active && pending;
|
boolean owned = Boolean.TRUE.equals(order.getDeliveryOwnershipGranted());
|
||||||
|
return oneTime && wallet && active && pending && owned;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String resolveLinkedToken(GooglePlayPurchaseSnapshot snapshot) {
|
private String resolveLinkedToken(GooglePlayPurchaseSnapshot snapshot) {
|
||||||
@@ -172,20 +183,67 @@ public class GooglePlayStateService {
|
|||||||
|
|
||||||
private void saveOrder(GooglePlayOrder order) {
|
private void saveOrder(GooglePlayOrder order) {
|
||||||
if (order.getId() == null) {
|
if (order.getId() == null) {
|
||||||
orderMapper.insert(order);
|
insertOrder(order);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
orderMapper.updateById(order);
|
orderMapper.updateById(order);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仅在新订单场景预落库,避免并发请求用旧快照把发货状态回写成 PENDING。
|
||||||
|
*/
|
||||||
|
private void persistOrderIfNew(GooglePlayOrder order) {
|
||||||
|
if (order.getId() == null) {
|
||||||
|
saveOrder(order);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void saveToken(GooglePlayPurchaseToken token) {
|
private void saveToken(GooglePlayPurchaseToken token) {
|
||||||
if (token.getId() == null) {
|
if (token.getId() == null) {
|
||||||
purchaseTokenMapper.insert(token);
|
insertToken(token);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
purchaseTokenMapper.updateById(token);
|
purchaseTokenMapper.updateById(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 并发首写同一订单时,唯一键冲突后回读已存在记录继续流程。
|
||||||
|
*/
|
||||||
|
private void insertOrder(GooglePlayOrder order) {
|
||||||
|
try {
|
||||||
|
orderMapper.insert(order);
|
||||||
|
} catch (DuplicateKeyException e) {
|
||||||
|
GooglePlayOrder existingOrder = orderMapper.selectOne(Wrappers.<GooglePlayOrder>lambdaQuery()
|
||||||
|
.eq(GooglePlayOrder::getOrderKey, order.getOrderKey())
|
||||||
|
.last("LIMIT 1"));
|
||||||
|
if (existingOrder == null) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
order.setId(existingOrder.getId());
|
||||||
|
order.setCreatedAt(existingOrder.getCreatedAt());
|
||||||
|
order.setDeliveryStatus(existingOrder.getDeliveryStatus());
|
||||||
|
order.setGrantedQuantity(existingOrder.getGrantedQuantity());
|
||||||
|
orderMapper.updateById(order);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* purchaseToken 首次并发写入时回读已有记录,避免重复插入直接失败。
|
||||||
|
*/
|
||||||
|
private void insertToken(GooglePlayPurchaseToken token) {
|
||||||
|
try {
|
||||||
|
purchaseTokenMapper.insert(token);
|
||||||
|
} catch (DuplicateKeyException e) {
|
||||||
|
GooglePlayPurchaseToken existingToken = findToken(token.getPurchaseToken());
|
||||||
|
if (existingToken == null) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
token.setId(existingToken.getId());
|
||||||
|
token.setCreatedAt(existingToken.getCreatedAt());
|
||||||
|
purchaseTokenMapper.updateById(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void updateAckState(String purchaseToken, String orderKey, String state) {
|
private void updateAckState(String purchaseToken, String orderKey, String state) {
|
||||||
GooglePlayPurchaseToken token = findToken(purchaseToken);
|
GooglePlayPurchaseToken token = findToken(purchaseToken);
|
||||||
if (token != null) {
|
if (token != null) {
|
||||||
|
|||||||
@@ -56,4 +56,6 @@ public class GooglePlayPurchaseSnapshot {
|
|||||||
private Date lastSyncedAt;
|
private Date lastSyncedAt;
|
||||||
|
|
||||||
private String rawResponse;
|
private String rawResponse;
|
||||||
|
|
||||||
|
private String purchaseOptionId;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,23 @@ package com.yolo.keyborad.mapper;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
import com.yolo.keyborad.model.entity.googleplay.GooglePlayOrder;
|
import com.yolo.keyborad.model.entity.googleplay.GooglePlayOrder;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.apache.ibatis.annotations.Update;
|
||||||
|
|
||||||
public interface GooglePlayOrderMapper extends BaseMapper<GooglePlayOrder> {
|
public interface GooglePlayOrderMapper extends BaseMapper<GooglePlayOrder> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 原子抢占发货资格,只有当前状态匹配时才允许进入处理中。
|
||||||
|
*/
|
||||||
|
@Update("""
|
||||||
|
UPDATE google_play_order
|
||||||
|
SET delivery_status = #{targetStatus},
|
||||||
|
updated_at = #{updatedAt}
|
||||||
|
WHERE id = #{orderId}
|
||||||
|
AND delivery_status = #{expectedStatus}
|
||||||
|
""")
|
||||||
|
int updateDeliveryStatusIfMatch(@Param("orderId") Long orderId,
|
||||||
|
@Param("expectedStatus") String expectedStatus,
|
||||||
|
@Param("targetStatus") String targetStatus,
|
||||||
|
@Param("updatedAt") java.util.Date updatedAt);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,4 +81,10 @@ public class GooglePlayOrder {
|
|||||||
|
|
||||||
@TableField("updated_at")
|
@TableField("updated_at")
|
||||||
private Date updatedAt;
|
private Date updatedAt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前线程是否拿到了本次发货资格,仅用于本次请求内控制幂等,不落库。
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private Boolean deliveryOwnershipGranted;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user