fix(deduction): make apply idempotent
Some checks failed
Build and Push Backend / docker (push) Has been cancelled
Some checks failed
Build and Push Backend / docker (push) Has been cancelled
This commit is contained in:
@@ -228,6 +228,10 @@ public class EarningsDeductionBatchServiceImpl
|
||||
if (item == null || !StringUtils.hasText(item.getClerkId())) {
|
||||
continue;
|
||||
}
|
||||
if (StringUtils.hasText(item.getAdjustmentId())) {
|
||||
adjustmentService.triggerApplyAsync(item.getAdjustmentId());
|
||||
continue;
|
||||
}
|
||||
BigDecimal amount = item.getApplyAmount() == null ? BigDecimal.ZERO : item.getApplyAmount();
|
||||
if (amount.compareTo(BigDecimal.ZERO) == 0) {
|
||||
markItemFailed(item.getId(), "applyAmount=0");
|
||||
@@ -241,7 +245,7 @@ public class EarningsDeductionBatchServiceImpl
|
||||
EarningsAdjustmentReasonType.MANUAL,
|
||||
batch.getReasonDescription(),
|
||||
idempotencyKey,
|
||||
LocalDateTime.now());
|
||||
null);
|
||||
|
||||
if (!StringUtils.hasText(item.getAdjustmentId())) {
|
||||
EarningsDeductionItemEntity patch = new EarningsDeductionItemEntity();
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
package com.starry.admin.modules.withdraw.service.impl;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.starry.admin.api.AbstractApiTest;
|
||||
import com.starry.admin.common.apitest.ApiTestDataSeeder;
|
||||
import com.starry.admin.modules.withdraw.entity.EarningsDeductionBatchEntity;
|
||||
import com.starry.admin.modules.withdraw.entity.EarningsDeductionBatchLogEntity;
|
||||
import com.starry.admin.modules.withdraw.entity.EarningsDeductionItemEntity;
|
||||
import com.starry.admin.modules.withdraw.entity.EarningsLineEntity;
|
||||
import com.starry.admin.modules.withdraw.enums.EarningsDeductionOperationType;
|
||||
import com.starry.admin.modules.withdraw.enums.EarningsDeductionRuleType;
|
||||
import com.starry.admin.modules.withdraw.enums.EarningsSourceType;
|
||||
import com.starry.admin.modules.withdraw.mapper.EarningsDeductionBatchLogMapper;
|
||||
import com.starry.admin.modules.withdraw.mapper.EarningsDeductionItemMapper;
|
||||
import com.starry.admin.modules.withdraw.service.IEarningsAdjustmentService;
|
||||
import com.starry.admin.modules.withdraw.service.IEarningsService;
|
||||
import com.starry.admin.utils.SecurityUtils;
|
||||
import com.starry.common.utils.IdUtils;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
class EarningsDeductionBatchServiceImplIdempotencyIntegrationTest extends AbstractApiTest {
|
||||
|
||||
@Autowired
|
||||
private EarningsDeductionBatchServiceImpl batchService;
|
||||
|
||||
@Autowired
|
||||
private EarningsDeductionItemMapper itemMapper;
|
||||
|
||||
@Autowired
|
||||
private EarningsDeductionBatchLogMapper logMapper;
|
||||
|
||||
@Autowired
|
||||
private IEarningsAdjustmentService adjustmentService;
|
||||
|
||||
@Autowired
|
||||
private IEarningsService earningsService;
|
||||
|
||||
private String batchId;
|
||||
private String clerkId;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
SecurityUtils.setTenantId(ApiTestDataSeeder.DEFAULT_TENANT_ID);
|
||||
clerkId = IdUtils.getUuid();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
SecurityUtils.setTenantId(ApiTestDataSeeder.DEFAULT_TENANT_ID);
|
||||
if (batchId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<EarningsDeductionItemEntity> items = itemMapper.selectList(Wrappers.lambdaQuery(EarningsDeductionItemEntity.class)
|
||||
.eq(EarningsDeductionItemEntity::getTenantId, ApiTestDataSeeder.DEFAULT_TENANT_ID)
|
||||
.eq(EarningsDeductionItemEntity::getBatchId, batchId)
|
||||
.eq(EarningsDeductionItemEntity::getDeleted, false));
|
||||
|
||||
List<String> adjustmentIds = items.stream()
|
||||
.map(EarningsDeductionItemEntity::getAdjustmentId)
|
||||
.filter(id -> id != null && !id.isBlank())
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (!adjustmentIds.isEmpty()) {
|
||||
earningsService.lambdaUpdate()
|
||||
.eq(EarningsLineEntity::getTenantId, ApiTestDataSeeder.DEFAULT_TENANT_ID)
|
||||
.eq(EarningsLineEntity::getClerkId, clerkId)
|
||||
.eq(EarningsLineEntity::getSourceType, EarningsSourceType.ADJUSTMENT)
|
||||
.in(EarningsLineEntity::getSourceId, adjustmentIds)
|
||||
.remove();
|
||||
adjustmentService.removeByIds(adjustmentIds);
|
||||
}
|
||||
|
||||
itemMapper.delete(Wrappers.lambdaQuery(EarningsDeductionItemEntity.class)
|
||||
.eq(EarningsDeductionItemEntity::getTenantId, ApiTestDataSeeder.DEFAULT_TENANT_ID)
|
||||
.eq(EarningsDeductionItemEntity::getBatchId, batchId));
|
||||
|
||||
logMapper.delete(Wrappers.lambdaQuery(EarningsDeductionBatchLogEntity.class)
|
||||
.eq(EarningsDeductionBatchLogEntity::getTenantId, ApiTestDataSeeder.DEFAULT_TENANT_ID)
|
||||
.eq(EarningsDeductionBatchLogEntity::getBatchId, batchId));
|
||||
|
||||
batchService.removeById(batchId);
|
||||
}
|
||||
|
||||
@Test
|
||||
void applyOnce_canBeTriggeredTwiceWithoutBreakingIdempotency() {
|
||||
String tenantId = ApiTestDataSeeder.DEFAULT_TENANT_ID;
|
||||
LocalDateTime begin = LocalDateTime.now().minusDays(1);
|
||||
LocalDateTime end = LocalDateTime.now();
|
||||
String idempotencyKey = "e2e-deduct-idem-" + UUID.randomUUID();
|
||||
|
||||
EarningsDeductionBatchEntity batch = batchService.createOrGetProcessing(
|
||||
tenantId,
|
||||
List.of(clerkId),
|
||||
begin,
|
||||
end,
|
||||
EarningsDeductionRuleType.FIXED,
|
||||
new BigDecimal("1.00"),
|
||||
EarningsDeductionOperationType.PUNISHMENT,
|
||||
"idempotency test",
|
||||
idempotencyKey);
|
||||
assertNotNull(batch);
|
||||
batchId = batch.getId();
|
||||
|
||||
batchService.applyOnce(batchId);
|
||||
|
||||
EarningsDeductionItemEntity item = itemMapper.selectOne(Wrappers.lambdaQuery(EarningsDeductionItemEntity.class)
|
||||
.eq(EarningsDeductionItemEntity::getTenantId, tenantId)
|
||||
.eq(EarningsDeductionItemEntity::getBatchId, batchId)
|
||||
.eq(EarningsDeductionItemEntity::getClerkId, clerkId)
|
||||
.last("limit 1"));
|
||||
assertNotNull(item);
|
||||
assertTrue(item.getAdjustmentId() != null && !item.getAdjustmentId().isBlank());
|
||||
|
||||
assertDoesNotThrow(() -> batchService.applyOnce(batchId));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user