914 lines
43 KiB
Java
914 lines
43 KiB
Java
package com.starry.admin.api;
|
|
|
|
import static org.assertj.core.api.Assertions.assertThat;
|
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
|
|
|
import com.fasterxml.jackson.databind.JsonNode;
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
import com.starry.admin.common.apitest.ApiTestDataSeeder;
|
|
import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity;
|
|
import com.starry.admin.modules.clerk.service.IPlayClerkUserInfoService;
|
|
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
|
import com.starry.admin.modules.order.service.IPlayOrderInfoService;
|
|
import com.starry.admin.modules.withdraw.entity.EarningsLineEntity;
|
|
import com.starry.admin.modules.withdraw.enums.EarningsSourceType;
|
|
import com.starry.admin.modules.withdraw.enums.EarningsType;
|
|
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.time.ZoneId;
|
|
import java.time.format.DateTimeFormatter;
|
|
import java.util.ArrayList;
|
|
import java.util.Date;
|
|
import java.util.List;
|
|
import java.util.UUID;
|
|
import java.util.concurrent.Callable;
|
|
import java.util.concurrent.ExecutorService;
|
|
import java.util.concurrent.Executors;
|
|
import java.util.concurrent.Future;
|
|
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;
|
|
import org.springframework.http.MediaType;
|
|
import org.springframework.jdbc.core.JdbcTemplate;
|
|
import org.springframework.test.web.servlet.MvcResult;
|
|
|
|
/**
|
|
* End-to-end contract tests for admin batch deductions (bonus/punishment across clerks).
|
|
*
|
|
* <p>These tests are expected to FAIL until the batch deduction system is implemented end-to-end.</p>
|
|
*/
|
|
class AdminEarningsDeductionBatchControllerApiTest extends AbstractApiTest {
|
|
|
|
private static final String IDEMPOTENCY_HEADER = "Idempotency-Key";
|
|
private static final String PERMISSIONS_HEADER = "X-Test-Permissions";
|
|
private static final String PERMISSIONS_CREATE_READ = "withdraw:deduction:create,withdraw:deduction:read";
|
|
|
|
private static final String BASE_URL = "/admin/earnings/deductions";
|
|
|
|
private static final DateTimeFormatter DATE_TIME = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
|
|
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
|
|
|
@Autowired
|
|
private JdbcTemplate jdbcTemplate;
|
|
|
|
@Autowired
|
|
private IPlayOrderInfoService orderInfoService;
|
|
|
|
@Autowired
|
|
private IPlayClerkUserInfoService clerkUserInfoService;
|
|
|
|
@Autowired
|
|
private IEarningsService earningsService;
|
|
|
|
@Autowired
|
|
private IEarningsAdjustmentService adjustmentService;
|
|
|
|
private final List<String> ordersToCleanup = new ArrayList<>();
|
|
private final List<String> earningsToCleanup = new ArrayList<>();
|
|
private final List<String> batchIdsToCleanup = new ArrayList<>();
|
|
private final List<String> clerkIdsToCleanup = new ArrayList<>();
|
|
|
|
@BeforeEach
|
|
void setUp() {
|
|
SecurityUtils.setTenantId(ApiTestDataSeeder.DEFAULT_TENANT_ID);
|
|
ordersToCleanup.clear();
|
|
earningsToCleanup.clear();
|
|
batchIdsToCleanup.clear();
|
|
}
|
|
|
|
@AfterEach
|
|
void tearDown() {
|
|
SecurityUtils.setTenantId(ApiTestDataSeeder.DEFAULT_TENANT_ID);
|
|
cleanupBatches(batchIdsToCleanup);
|
|
if (!earningsToCleanup.isEmpty()) {
|
|
earningsService.removeByIds(earningsToCleanup);
|
|
}
|
|
if (!ordersToCleanup.isEmpty()) {
|
|
orderInfoService.removeByIds(ordersToCleanup);
|
|
}
|
|
if (!clerkIdsToCleanup.isEmpty()) {
|
|
clerkUserInfoService.removeByIds(clerkIdsToCleanup);
|
|
}
|
|
}
|
|
|
|
@Test
|
|
void previewRequiresRequiredFieldsReturns400() throws Exception {
|
|
mockMvc.perform(post(BASE_URL + "/preview")
|
|
.header(USER_HEADER, ApiTestDataSeeder.DEFAULT_ADMIN_USER_ID)
|
|
.header(TENANT_HEADER, DEFAULT_TENANT)
|
|
.header(PERMISSIONS_HEADER, PERMISSIONS_CREATE_READ)
|
|
.contentType(MediaType.APPLICATION_JSON)
|
|
.content("{}"))
|
|
.andExpect(status().isBadRequest());
|
|
}
|
|
|
|
@Test
|
|
void previewPercentageUsesOnlyOrderLinesPositiveAmountsAndOrderEndTimeWindow() throws Exception {
|
|
LocalDateTime now = LocalDateTime.now().withNano(0);
|
|
LocalDateTime begin = now.minusDays(7);
|
|
LocalDateTime end = now.plusSeconds(1);
|
|
String clerkId = ensureTestClerkInDefaultTenant();
|
|
|
|
// In window: order1 + order2
|
|
String order1 = seedOrder(clerkId, now.minusDays(1), new BigDecimal("100.00"));
|
|
seedOrderEarningLine(clerkId, order1, new BigDecimal("100.00"), "withdrawn", EarningsType.ORDER);
|
|
seedOrderEarningLine(clerkId, order1, new BigDecimal("-30.00"), "available", EarningsType.ADJUSTMENT);
|
|
|
|
String order2 = seedOrder(clerkId, now.minusDays(2), new BigDecimal("50.00"));
|
|
seedOrderEarningLine(clerkId, order2, new BigDecimal("50.00"), "available", EarningsType.ORDER);
|
|
|
|
// Out of window: order3 should not contribute
|
|
String order3 = seedOrder(clerkId, now.minusDays(30), new BigDecimal("999.00"));
|
|
seedOrderEarningLine(clerkId, order3, new BigDecimal("999.00"), "withdrawn", EarningsType.ORDER);
|
|
|
|
// Adjustment line (order_id=null) should not contribute even if positive
|
|
seedAdjustmentEarningLine(clerkId, new BigDecimal("1000.00"), "available");
|
|
|
|
String payload = "{" +
|
|
"\"clerkIds\":[\"" + clerkId + "\"]," +
|
|
"\"beginTime\":\"" + DATE_TIME.format(begin) + "\"," +
|
|
"\"endTime\":\"" + DATE_TIME.format(end) + "\"," +
|
|
"\"ruleType\":\"PERCENTAGE\"," +
|
|
"\"percentage\":\"10.00\"," +
|
|
"\"operation\":\"BONUS\"," +
|
|
"\"reasonDescription\":\"week bonus\"" +
|
|
"}";
|
|
|
|
MvcResult result = mockMvc.perform(post(BASE_URL + "/preview")
|
|
.header(USER_HEADER, ApiTestDataSeeder.DEFAULT_ADMIN_USER_ID)
|
|
.header(TENANT_HEADER, DEFAULT_TENANT)
|
|
.header(PERMISSIONS_HEADER, PERMISSIONS_CREATE_READ)
|
|
.contentType(MediaType.APPLICATION_JSON)
|
|
.content(payload))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.code").value(200))
|
|
.andExpect(jsonPath("$.data.items").isArray())
|
|
.andReturn();
|
|
|
|
JsonNode root = objectMapper.readTree(result.getResponse().getContentAsString());
|
|
JsonNode item = root.path("data").path("items").get(0);
|
|
assertThat(item.path("clerkId").asText()).isEqualTo(clerkId);
|
|
|
|
BigDecimal baseAmount = new BigDecimal(item.path("baseAmount").asText("0"));
|
|
BigDecimal applyAmount = new BigDecimal(item.path("applyAmount").asText("0"));
|
|
assertThat(baseAmount).isEqualByComparingTo("150.00"); // 100 + 50 (negative & non-order excluded)
|
|
assertThat(applyAmount).isEqualByComparingTo("15.00"); // 10% bonus
|
|
}
|
|
|
|
@Test
|
|
void previewWindowIsInclusiveOnBoundaries() throws Exception {
|
|
LocalDateTime now = LocalDateTime.now().withNano(0);
|
|
LocalDateTime begin = now.minusDays(7);
|
|
LocalDateTime end = now.minusDays(1);
|
|
String clerkId = ensureTestClerkInDefaultTenant();
|
|
|
|
String orderBegin = seedOrder(clerkId, begin, new BigDecimal("20.00"));
|
|
seedOrderEarningLine(clerkId, orderBegin, new BigDecimal("20.00"), "withdrawn", EarningsType.ORDER);
|
|
|
|
String orderEnd = seedOrder(clerkId, end, new BigDecimal("30.00"));
|
|
seedOrderEarningLine(clerkId, orderEnd, new BigDecimal("30.00"), "withdrawn", EarningsType.ORDER);
|
|
|
|
String payload = "{" +
|
|
"\"clerkIds\":[\"" + clerkId + "\"]," +
|
|
"\"beginTime\":\"" + DATE_TIME.format(begin) + "\"," +
|
|
"\"endTime\":\"" + DATE_TIME.format(end) + "\"," +
|
|
"\"ruleType\":\"PERCENTAGE\"," +
|
|
"\"percentage\":\"10.00\"," +
|
|
"\"operation\":\"BONUS\"," +
|
|
"\"reasonDescription\":\"boundary\"" +
|
|
"}";
|
|
|
|
MvcResult result = mockMvc.perform(post(BASE_URL + "/preview")
|
|
.header(USER_HEADER, ApiTestDataSeeder.DEFAULT_ADMIN_USER_ID)
|
|
.header(TENANT_HEADER, DEFAULT_TENANT)
|
|
.header(PERMISSIONS_HEADER, PERMISSIONS_CREATE_READ)
|
|
.contentType(MediaType.APPLICATION_JSON)
|
|
.content(payload))
|
|
.andExpect(status().isOk())
|
|
.andReturn();
|
|
|
|
JsonNode root = objectMapper.readTree(result.getResponse().getContentAsString());
|
|
JsonNode item = root.path("data").path("items").get(0);
|
|
BigDecimal baseAmount = new BigDecimal(item.path("baseAmount").asText("0"));
|
|
assertThat(baseAmount).isEqualByComparingTo("50.00"); // 20 + 30 (both boundary-included)
|
|
}
|
|
|
|
@Test
|
|
void previewRejectsCrossTenantClerkScopeReturns403() throws Exception {
|
|
LocalDateTime now = LocalDateTime.now().withNano(0);
|
|
LocalDateTime begin = now.minusDays(7);
|
|
LocalDateTime end = now.plusSeconds(1);
|
|
|
|
String order1 = seedOrder(ApiTestDataSeeder.DEFAULT_CLERK_ID, now.minusDays(1), new BigDecimal("100.00"));
|
|
seedOrderEarningLine(ApiTestDataSeeder.DEFAULT_CLERK_ID, order1, new BigDecimal("100.00"), "withdrawn", EarningsType.ORDER);
|
|
|
|
String payload = "{" +
|
|
"\"clerkIds\":[\"" + ApiTestDataSeeder.DEFAULT_CLERK_ID + "\"]," +
|
|
"\"beginTime\":\"" + DATE_TIME.format(begin) + "\"," +
|
|
"\"endTime\":\"" + DATE_TIME.format(end) + "\"," +
|
|
"\"ruleType\":\"PERCENTAGE\"," +
|
|
"\"percentage\":\"10.00\"," +
|
|
"\"operation\":\"BONUS\"," +
|
|
"\"reasonDescription\":\"tenant isolation\"" +
|
|
"}";
|
|
|
|
mockMvc.perform(post(BASE_URL + "/preview")
|
|
.header(USER_HEADER, ApiTestDataSeeder.DEFAULT_ADMIN_USER_ID)
|
|
.header(TENANT_HEADER, "tenant-other")
|
|
.header(PERMISSIONS_HEADER, PERMISSIONS_CREATE_READ)
|
|
.contentType(MediaType.APPLICATION_JSON)
|
|
.content(payload))
|
|
.andExpect(status().isForbidden());
|
|
}
|
|
|
|
@Test
|
|
void createReturns202AndProvidesPollingHandle() throws Exception {
|
|
LocalDateTime now = LocalDateTime.now().withNano(0);
|
|
LocalDateTime begin = now.minusDays(7);
|
|
LocalDateTime end = now.plusSeconds(1);
|
|
|
|
String order1 = seedOrder(ApiTestDataSeeder.DEFAULT_CLERK_ID, now.minusDays(1), new BigDecimal("100.00"));
|
|
seedOrderEarningLine(ApiTestDataSeeder.DEFAULT_CLERK_ID, order1, new BigDecimal("100.00"), "available", EarningsType.ORDER);
|
|
|
|
String key = UUID.randomUUID().toString();
|
|
String payload = "{" +
|
|
"\"clerkIds\":[\"" + ApiTestDataSeeder.DEFAULT_CLERK_ID + "\"]," +
|
|
"\"beginTime\":\"" + DATE_TIME.format(begin) + "\"," +
|
|
"\"endTime\":\"" + DATE_TIME.format(end) + "\"," +
|
|
"\"ruleType\":\"PERCENTAGE\"," +
|
|
"\"percentage\":\"10.00\"," +
|
|
"\"operation\":\"PUNISHMENT\"," +
|
|
"\"reasonDescription\":\"week penalty\"" +
|
|
"}";
|
|
|
|
MvcResult result = mockMvc.perform(post(BASE_URL)
|
|
.header(USER_HEADER, ApiTestDataSeeder.DEFAULT_ADMIN_USER_ID)
|
|
.header(TENANT_HEADER, DEFAULT_TENANT)
|
|
.header(PERMISSIONS_HEADER, PERMISSIONS_CREATE_READ)
|
|
.header(IDEMPOTENCY_HEADER, key)
|
|
.contentType(MediaType.APPLICATION_JSON)
|
|
.content(payload))
|
|
.andExpect(status().isAccepted())
|
|
.andExpect(header().string("Location", BASE_URL + "/idempotency/" + key))
|
|
.andExpect(jsonPath("$.code").value(202))
|
|
.andExpect(jsonPath("$.data.batchId").isNotEmpty())
|
|
.andExpect(jsonPath("$.data.idempotencyKey").value(key))
|
|
.andExpect(jsonPath("$.data.status").value("PROCESSING"))
|
|
.andReturn();
|
|
|
|
String batchId = extractBatchId(result);
|
|
batchIdsToCleanup.add(batchId);
|
|
|
|
awaitApplied(key);
|
|
}
|
|
|
|
@Test
|
|
void createIsIdempotentWithSameKeyAndSameBody() throws Exception {
|
|
LocalDateTime now = LocalDateTime.now().withNano(0);
|
|
LocalDateTime begin = now.minusDays(7);
|
|
LocalDateTime end = now.plusSeconds(1);
|
|
|
|
String order1 = seedOrder(ApiTestDataSeeder.DEFAULT_CLERK_ID, now.minusDays(1), new BigDecimal("100.00"));
|
|
seedOrderEarningLine(ApiTestDataSeeder.DEFAULT_CLERK_ID, order1, new BigDecimal("100.00"), "available", EarningsType.ORDER);
|
|
|
|
String key = UUID.randomUUID().toString();
|
|
String payload = "{" +
|
|
"\"clerkIds\":[\"" + ApiTestDataSeeder.DEFAULT_CLERK_ID + "\"]," +
|
|
"\"beginTime\":\"" + DATE_TIME.format(begin) + "\"," +
|
|
"\"endTime\":\"" + DATE_TIME.format(end) + "\"," +
|
|
"\"ruleType\":\"FIXED\"," +
|
|
"\"amount\":\"50.00\"," +
|
|
"\"operation\":\"BONUS\"," +
|
|
"\"reasonDescription\":\"fixed bonus\"" +
|
|
"}";
|
|
|
|
MvcResult first = mockMvc.perform(post(BASE_URL)
|
|
.header(USER_HEADER, ApiTestDataSeeder.DEFAULT_ADMIN_USER_ID)
|
|
.header(TENANT_HEADER, DEFAULT_TENANT)
|
|
.header(PERMISSIONS_HEADER, PERMISSIONS_CREATE_READ)
|
|
.header(IDEMPOTENCY_HEADER, key)
|
|
.contentType(MediaType.APPLICATION_JSON)
|
|
.content(payload))
|
|
.andExpect(status().isAccepted())
|
|
.andReturn();
|
|
String batchA = extractBatchId(first);
|
|
|
|
MvcResult second = mockMvc.perform(post(BASE_URL)
|
|
.header(USER_HEADER, ApiTestDataSeeder.DEFAULT_ADMIN_USER_ID)
|
|
.header(TENANT_HEADER, DEFAULT_TENANT)
|
|
.header(PERMISSIONS_HEADER, PERMISSIONS_CREATE_READ)
|
|
.header(IDEMPOTENCY_HEADER, key)
|
|
.contentType(MediaType.APPLICATION_JSON)
|
|
.content(payload))
|
|
.andExpect(status().isAccepted())
|
|
.andReturn();
|
|
String batchB = extractBatchId(second);
|
|
|
|
assertThat(batchB).isEqualTo(batchA);
|
|
batchIdsToCleanup.add(batchA);
|
|
awaitApplied(key);
|
|
}
|
|
|
|
@Test
|
|
void createConcurrentRequestsSameKeyOnlyOneBatchCreated() throws Exception {
|
|
LocalDateTime now = LocalDateTime.now().withNano(0);
|
|
LocalDateTime begin = now.minusDays(7);
|
|
LocalDateTime end = now.plusSeconds(1);
|
|
|
|
String order1 = seedOrder(ApiTestDataSeeder.DEFAULT_CLERK_ID, now.minusDays(1), new BigDecimal("100.00"));
|
|
seedOrderEarningLine(ApiTestDataSeeder.DEFAULT_CLERK_ID, order1, new BigDecimal("100.00"), "available", EarningsType.ORDER);
|
|
|
|
String key = UUID.randomUUID().toString();
|
|
String payload = "{" +
|
|
"\"clerkIds\":[\"" + ApiTestDataSeeder.DEFAULT_CLERK_ID + "\"]," +
|
|
"\"beginTime\":\"" + DATE_TIME.format(begin) + "\"," +
|
|
"\"endTime\":\"" + DATE_TIME.format(end) + "\"," +
|
|
"\"ruleType\":\"FIXED\"," +
|
|
"\"amount\":\"10.00\"," +
|
|
"\"operation\":\"BONUS\"," +
|
|
"\"reasonDescription\":\"concurrent\"" +
|
|
"}";
|
|
|
|
ExecutorService pool = Executors.newFixedThreadPool(2);
|
|
try {
|
|
Callable<String> call = () -> {
|
|
MvcResult result = mockMvc.perform(post(BASE_URL)
|
|
.header(USER_HEADER, ApiTestDataSeeder.DEFAULT_ADMIN_USER_ID)
|
|
.header(TENANT_HEADER, DEFAULT_TENANT)
|
|
.header(PERMISSIONS_HEADER, PERMISSIONS_CREATE_READ)
|
|
.header(IDEMPOTENCY_HEADER, key)
|
|
.contentType(MediaType.APPLICATION_JSON)
|
|
.content(payload))
|
|
.andExpect(status().isAccepted())
|
|
.andReturn();
|
|
return extractBatchId(result);
|
|
};
|
|
List<Future<String>> futures = new ArrayList<>();
|
|
futures.add(pool.submit(call));
|
|
futures.add(pool.submit(call));
|
|
|
|
String a = futures.get(0).get();
|
|
String b = futures.get(1).get();
|
|
assertThat(a).isNotBlank();
|
|
assertThat(b).isEqualTo(a);
|
|
batchIdsToCleanup.add(a);
|
|
awaitApplied(key);
|
|
} finally {
|
|
pool.shutdownNow();
|
|
}
|
|
}
|
|
|
|
@Test
|
|
void createSameKeyDifferentBodyReturns409() throws Exception {
|
|
LocalDateTime now = LocalDateTime.now().withNano(0);
|
|
LocalDateTime begin = now.minusDays(7);
|
|
LocalDateTime end = now.plusSeconds(1);
|
|
|
|
String order1 = seedOrder(ApiTestDataSeeder.DEFAULT_CLERK_ID, now.minusDays(1), new BigDecimal("100.00"));
|
|
seedOrderEarningLine(ApiTestDataSeeder.DEFAULT_CLERK_ID, order1, new BigDecimal("100.00"), "available", EarningsType.ORDER);
|
|
|
|
String key = UUID.randomUUID().toString();
|
|
String payloadA = "{" +
|
|
"\"clerkIds\":[\"" + ApiTestDataSeeder.DEFAULT_CLERK_ID + "\"]," +
|
|
"\"beginTime\":\"" + DATE_TIME.format(begin) + "\"," +
|
|
"\"endTime\":\"" + DATE_TIME.format(end) + "\"," +
|
|
"\"ruleType\":\"FIXED\"," +
|
|
"\"amount\":\"50.00\"," +
|
|
"\"operation\":\"BONUS\"," +
|
|
"\"reasonDescription\":\"fixed bonus\"" +
|
|
"}";
|
|
String payloadB = payloadA.replace("\"50.00\"", "\"60.00\"");
|
|
|
|
MvcResult first = mockMvc.perform(post(BASE_URL)
|
|
.header(USER_HEADER, ApiTestDataSeeder.DEFAULT_ADMIN_USER_ID)
|
|
.header(TENANT_HEADER, DEFAULT_TENANT)
|
|
.header(PERMISSIONS_HEADER, PERMISSIONS_CREATE_READ)
|
|
.header(IDEMPOTENCY_HEADER, key)
|
|
.contentType(MediaType.APPLICATION_JSON)
|
|
.content(payloadA))
|
|
.andExpect(status().isAccepted())
|
|
.andReturn();
|
|
String batchId = extractBatchId(first);
|
|
batchIdsToCleanup.add(batchId);
|
|
|
|
mockMvc.perform(post(BASE_URL)
|
|
.header(USER_HEADER, ApiTestDataSeeder.DEFAULT_ADMIN_USER_ID)
|
|
.header(TENANT_HEADER, DEFAULT_TENANT)
|
|
.header(PERMISSIONS_HEADER, PERMISSIONS_CREATE_READ)
|
|
.header(IDEMPOTENCY_HEADER, key)
|
|
.contentType(MediaType.APPLICATION_JSON)
|
|
.content(payloadB))
|
|
.andExpect(status().isConflict());
|
|
}
|
|
|
|
@Test
|
|
void pollMissingIdempotencyKeyReturns404() throws Exception {
|
|
mockMvc.perform(get(BASE_URL + "/idempotency/" + UUID.randomUUID())
|
|
.header(USER_HEADER, ApiTestDataSeeder.DEFAULT_ADMIN_USER_ID)
|
|
.header(TENANT_HEADER, DEFAULT_TENANT)
|
|
.header(PERMISSIONS_HEADER, PERMISSIONS_CREATE_READ))
|
|
.andExpect(status().isNotFound());
|
|
}
|
|
|
|
@Test
|
|
void idempotencyKeyIsTenantScoped() throws Exception {
|
|
LocalDateTime now = LocalDateTime.now().withNano(0);
|
|
LocalDateTime begin = now.minusDays(7);
|
|
LocalDateTime end = now.plusSeconds(1);
|
|
|
|
String order1 = seedOrder(ApiTestDataSeeder.DEFAULT_CLERK_ID, now.minusDays(1), new BigDecimal("100.00"));
|
|
seedOrderEarningLine(ApiTestDataSeeder.DEFAULT_CLERK_ID, order1, new BigDecimal("100.00"), "available", EarningsType.ORDER);
|
|
|
|
String key = UUID.randomUUID().toString();
|
|
String payload = "{" +
|
|
"\"clerkIds\":[\"" + ApiTestDataSeeder.DEFAULT_CLERK_ID + "\"]," +
|
|
"\"beginTime\":\"" + DATE_TIME.format(begin) + "\"," +
|
|
"\"endTime\":\"" + DATE_TIME.format(end) + "\"," +
|
|
"\"ruleType\":\"FIXED\"," +
|
|
"\"amount\":\"10.00\"," +
|
|
"\"operation\":\"PUNISHMENT\"," +
|
|
"\"reasonDescription\":\"tenant scope\"" +
|
|
"}";
|
|
|
|
MvcResult create = mockMvc.perform(post(BASE_URL)
|
|
.header(USER_HEADER, ApiTestDataSeeder.DEFAULT_ADMIN_USER_ID)
|
|
.header(TENANT_HEADER, DEFAULT_TENANT)
|
|
.header(PERMISSIONS_HEADER, PERMISSIONS_CREATE_READ)
|
|
.header(IDEMPOTENCY_HEADER, key)
|
|
.contentType(MediaType.APPLICATION_JSON)
|
|
.content(payload))
|
|
.andExpect(status().isAccepted())
|
|
.andReturn();
|
|
String batchId = extractBatchId(create);
|
|
batchIdsToCleanup.add(batchId);
|
|
awaitApplied(key);
|
|
|
|
mockMvc.perform(get(BASE_URL + "/idempotency/" + key)
|
|
.header(USER_HEADER, ApiTestDataSeeder.DEFAULT_ADMIN_USER_ID)
|
|
.header(TENANT_HEADER, "tenant-other")
|
|
.header(PERMISSIONS_HEADER, PERMISSIONS_CREATE_READ))
|
|
.andExpect(status().isNotFound());
|
|
}
|
|
|
|
@Test
|
|
void itemsAfterAppliedHaveAdjustmentIdAndNoDuplicateEarningsLines() throws Exception {
|
|
LocalDateTime now = LocalDateTime.now().withNano(0);
|
|
LocalDateTime begin = now.minusDays(7);
|
|
LocalDateTime end = now.plusSeconds(1);
|
|
|
|
String order1 = seedOrder(ApiTestDataSeeder.DEFAULT_CLERK_ID, now.minusDays(1), new BigDecimal("100.00"));
|
|
seedOrderEarningLine(ApiTestDataSeeder.DEFAULT_CLERK_ID, order1, new BigDecimal("100.00"), "available", EarningsType.ORDER);
|
|
|
|
String key = UUID.randomUUID().toString();
|
|
String payload = "{" +
|
|
"\"clerkIds\":[\"" + ApiTestDataSeeder.DEFAULT_CLERK_ID + "\"]," +
|
|
"\"beginTime\":\"" + DATE_TIME.format(begin) + "\"," +
|
|
"\"endTime\":\"" + DATE_TIME.format(end) + "\"," +
|
|
"\"ruleType\":\"FIXED\"," +
|
|
"\"amount\":\"50.00\"," +
|
|
"\"operation\":\"PUNISHMENT\"," +
|
|
"\"reasonDescription\":\"fixed penalty\"" +
|
|
"}";
|
|
|
|
MvcResult create = mockMvc.perform(post(BASE_URL)
|
|
.header(USER_HEADER, ApiTestDataSeeder.DEFAULT_ADMIN_USER_ID)
|
|
.header(TENANT_HEADER, DEFAULT_TENANT)
|
|
.header(PERMISSIONS_HEADER, PERMISSIONS_CREATE_READ)
|
|
.header(IDEMPOTENCY_HEADER, key)
|
|
.contentType(MediaType.APPLICATION_JSON)
|
|
.content(payload))
|
|
.andExpect(status().isAccepted())
|
|
.andReturn();
|
|
String batchId = extractBatchId(create);
|
|
batchIdsToCleanup.add(batchId);
|
|
|
|
awaitApplied(key);
|
|
|
|
MvcResult items = mockMvc.perform(get(BASE_URL + "/" + batchId + "/items")
|
|
.header(USER_HEADER, ApiTestDataSeeder.DEFAULT_ADMIN_USER_ID)
|
|
.header(TENANT_HEADER, DEFAULT_TENANT)
|
|
.header(PERMISSIONS_HEADER, PERMISSIONS_CREATE_READ)
|
|
.param("pageNum", "1")
|
|
.param("pageSize", "20"))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.code").value(200))
|
|
.andExpect(jsonPath("$.data").isArray())
|
|
.andExpect(jsonPath("$.data[0].adjustmentId").isNotEmpty())
|
|
.andReturn();
|
|
|
|
JsonNode root = objectMapper.readTree(items.getResponse().getContentAsString());
|
|
JsonNode first = root.path("data").get(0);
|
|
String adjustmentId = first.path("adjustmentId").asText();
|
|
assertThat(adjustmentId).isNotBlank();
|
|
|
|
long count = earningsService.lambdaQuery()
|
|
.eq(EarningsLineEntity::getTenantId, ApiTestDataSeeder.DEFAULT_TENANT_ID)
|
|
.eq(EarningsLineEntity::getClerkId, ApiTestDataSeeder.DEFAULT_CLERK_ID)
|
|
.eq(EarningsLineEntity::getSourceType, EarningsSourceType.ADJUSTMENT)
|
|
.eq(EarningsLineEntity::getSourceId, adjustmentId)
|
|
.count();
|
|
assertThat(count).isEqualTo(1);
|
|
}
|
|
|
|
@Test
|
|
void itemsPaginationWorks() throws Exception {
|
|
LocalDateTime now = LocalDateTime.now().withNano(0);
|
|
LocalDateTime begin = now.minusDays(7);
|
|
LocalDateTime end = now.plusSeconds(1);
|
|
|
|
String secondClerkId = ensureTestClerkInDefaultTenant();
|
|
|
|
String order1 = seedOrder(ApiTestDataSeeder.DEFAULT_CLERK_ID, now.minusDays(1), new BigDecimal("100.00"));
|
|
seedOrderEarningLine(ApiTestDataSeeder.DEFAULT_CLERK_ID, order1, new BigDecimal("100.00"), "available", EarningsType.ORDER);
|
|
|
|
String order2 = seedOrder(secondClerkId, now.minusDays(1), new BigDecimal("80.00"));
|
|
seedOrderEarningLine(secondClerkId, order2, new BigDecimal("80.00"), "available", EarningsType.ORDER);
|
|
|
|
String key = UUID.randomUUID().toString();
|
|
String payload = "{" +
|
|
"\"clerkIds\":[\"" + ApiTestDataSeeder.DEFAULT_CLERK_ID + "\",\"" + secondClerkId + "\"]," +
|
|
"\"beginTime\":\"" + DATE_TIME.format(begin) + "\"," +
|
|
"\"endTime\":\"" + DATE_TIME.format(end) + "\"," +
|
|
"\"ruleType\":\"FIXED\"," +
|
|
"\"amount\":\"10.00\"," +
|
|
"\"operation\":\"BONUS\"," +
|
|
"\"reasonDescription\":\"pagination\"" +
|
|
"}";
|
|
|
|
MvcResult create = mockMvc.perform(post(BASE_URL)
|
|
.header(USER_HEADER, ApiTestDataSeeder.DEFAULT_ADMIN_USER_ID)
|
|
.header(TENANT_HEADER, DEFAULT_TENANT)
|
|
.header(PERMISSIONS_HEADER, PERMISSIONS_CREATE_READ)
|
|
.header(IDEMPOTENCY_HEADER, key)
|
|
.contentType(MediaType.APPLICATION_JSON)
|
|
.content(payload))
|
|
.andExpect(status().isAccepted())
|
|
.andReturn();
|
|
String batchId = extractBatchId(create);
|
|
batchIdsToCleanup.add(batchId);
|
|
|
|
awaitApplied(key);
|
|
|
|
mockMvc.perform(get(BASE_URL + "/" + batchId + "/items")
|
|
.header(USER_HEADER, ApiTestDataSeeder.DEFAULT_ADMIN_USER_ID)
|
|
.header(TENANT_HEADER, DEFAULT_TENANT)
|
|
.header(PERMISSIONS_HEADER, PERMISSIONS_CREATE_READ)
|
|
.param("pageNum", "1")
|
|
.param("pageSize", "1"))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.code").value(200))
|
|
.andExpect(jsonPath("$.data").isArray())
|
|
.andExpect(jsonPath("$.data.length()").value(1))
|
|
.andExpect(jsonPath("$.total").value(2));
|
|
}
|
|
|
|
@Test
|
|
void logsContainCreatedAndFinalEvents() throws Exception {
|
|
LocalDateTime now = LocalDateTime.now().withNano(0);
|
|
LocalDateTime begin = now.minusDays(7);
|
|
LocalDateTime end = now.plusSeconds(1);
|
|
|
|
String order1 = seedOrder(ApiTestDataSeeder.DEFAULT_CLERK_ID, now.minusDays(1), new BigDecimal("100.00"));
|
|
seedOrderEarningLine(ApiTestDataSeeder.DEFAULT_CLERK_ID, order1, new BigDecimal("100.00"), "available", EarningsType.ORDER);
|
|
|
|
String key = UUID.randomUUID().toString();
|
|
String payload = "{" +
|
|
"\"clerkIds\":[\"" + ApiTestDataSeeder.DEFAULT_CLERK_ID + "\"]," +
|
|
"\"beginTime\":\"" + DATE_TIME.format(begin) + "\"," +
|
|
"\"endTime\":\"" + DATE_TIME.format(end) + "\"," +
|
|
"\"ruleType\":\"FIXED\"," +
|
|
"\"amount\":\"10.00\"," +
|
|
"\"operation\":\"BONUS\"," +
|
|
"\"reasonDescription\":\"audit log\"" +
|
|
"}";
|
|
|
|
MvcResult create = mockMvc.perform(post(BASE_URL)
|
|
.header(USER_HEADER, ApiTestDataSeeder.DEFAULT_ADMIN_USER_ID)
|
|
.header(TENANT_HEADER, DEFAULT_TENANT)
|
|
.header(PERMISSIONS_HEADER, PERMISSIONS_CREATE_READ)
|
|
.header(IDEMPOTENCY_HEADER, key)
|
|
.contentType(MediaType.APPLICATION_JSON)
|
|
.content(payload))
|
|
.andExpect(status().isAccepted())
|
|
.andReturn();
|
|
String batchId = extractBatchId(create);
|
|
batchIdsToCleanup.add(batchId);
|
|
|
|
awaitApplied(key);
|
|
|
|
mockMvc.perform(get(BASE_URL + "/" + batchId + "/logs")
|
|
.header(USER_HEADER, ApiTestDataSeeder.DEFAULT_ADMIN_USER_ID)
|
|
.header(TENANT_HEADER, DEFAULT_TENANT)
|
|
.header(PERMISSIONS_HEADER, PERMISSIONS_CREATE_READ))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.code").value(200))
|
|
.andExpect(jsonPath("$.data").isArray())
|
|
.andExpect(jsonPath("$.data[?(@.eventType=='CREATED')]").exists())
|
|
.andExpect(jsonPath("$.data[?(@.eventType=='APPLY_STARTED')]").exists())
|
|
.andExpect(jsonPath("$.data[?(@.eventType=='BATCH_APPLIED')]").exists());
|
|
}
|
|
|
|
@Test
|
|
void itemsAndLogsAreTenantScopedToBatchTenant() throws Exception {
|
|
LocalDateTime now = LocalDateTime.now().withNano(0);
|
|
LocalDateTime begin = now.minusDays(7);
|
|
LocalDateTime end = now.plusSeconds(1);
|
|
|
|
String order1 = seedOrder(ApiTestDataSeeder.DEFAULT_CLERK_ID, now.minusDays(1), new BigDecimal("100.00"));
|
|
seedOrderEarningLine(ApiTestDataSeeder.DEFAULT_CLERK_ID, order1, new BigDecimal("100.00"), "available", EarningsType.ORDER);
|
|
|
|
String key = UUID.randomUUID().toString();
|
|
String payload = "{" +
|
|
"\"clerkIds\":[\"" + ApiTestDataSeeder.DEFAULT_CLERK_ID + "\"]," +
|
|
"\"beginTime\":\"" + DATE_TIME.format(begin) + "\"," +
|
|
"\"endTime\":\"" + DATE_TIME.format(end) + "\"," +
|
|
"\"ruleType\":\"FIXED\"," +
|
|
"\"amount\":\"10.00\"," +
|
|
"\"operation\":\"BONUS\"," +
|
|
"\"reasonDescription\":\"tenant scope items\"" +
|
|
"}";
|
|
|
|
MvcResult create = mockMvc.perform(post(BASE_URL)
|
|
.header(USER_HEADER, ApiTestDataSeeder.DEFAULT_ADMIN_USER_ID)
|
|
.header(TENANT_HEADER, DEFAULT_TENANT)
|
|
.header(PERMISSIONS_HEADER, PERMISSIONS_CREATE_READ)
|
|
.header(IDEMPOTENCY_HEADER, key)
|
|
.contentType(MediaType.APPLICATION_JSON)
|
|
.content(payload))
|
|
.andExpect(status().isAccepted())
|
|
.andReturn();
|
|
String batchId = extractBatchId(create);
|
|
batchIdsToCleanup.add(batchId);
|
|
awaitApplied(key);
|
|
|
|
mockMvc.perform(get(BASE_URL + "/" + batchId + "/items")
|
|
.header(USER_HEADER, ApiTestDataSeeder.DEFAULT_ADMIN_USER_ID)
|
|
.header(TENANT_HEADER, "tenant-other")
|
|
.header(PERMISSIONS_HEADER, PERMISSIONS_CREATE_READ)
|
|
.param("pageNum", "1")
|
|
.param("pageSize", "10"))
|
|
.andExpect(status().isNotFound());
|
|
|
|
mockMvc.perform(get(BASE_URL + "/" + batchId + "/logs")
|
|
.header(USER_HEADER, ApiTestDataSeeder.DEFAULT_ADMIN_USER_ID)
|
|
.header(TENANT_HEADER, "tenant-other")
|
|
.header(PERMISSIONS_HEADER, PERMISSIONS_CREATE_READ))
|
|
.andExpect(status().isNotFound());
|
|
}
|
|
|
|
@Test
|
|
void logsRecordOperatorInCreatedBy() throws Exception {
|
|
LocalDateTime now = LocalDateTime.now().withNano(0);
|
|
LocalDateTime begin = now.minusDays(7);
|
|
LocalDateTime end = now.plusSeconds(1);
|
|
|
|
String order1 = seedOrder(ApiTestDataSeeder.DEFAULT_CLERK_ID, now.minusDays(1), new BigDecimal("100.00"));
|
|
seedOrderEarningLine(ApiTestDataSeeder.DEFAULT_CLERK_ID, order1, new BigDecimal("100.00"), "available", EarningsType.ORDER);
|
|
|
|
String key = UUID.randomUUID().toString();
|
|
String payload = "{" +
|
|
"\"clerkIds\":[\"" + ApiTestDataSeeder.DEFAULT_CLERK_ID + "\"]," +
|
|
"\"beginTime\":\"" + DATE_TIME.format(begin) + "\"," +
|
|
"\"endTime\":\"" + DATE_TIME.format(end) + "\"," +
|
|
"\"ruleType\":\"FIXED\"," +
|
|
"\"amount\":\"10.00\"," +
|
|
"\"operation\":\"BONUS\"," +
|
|
"\"reasonDescription\":\"operator audit\"" +
|
|
"}";
|
|
|
|
MvcResult create = mockMvc.perform(post(BASE_URL)
|
|
.header(USER_HEADER, ApiTestDataSeeder.DEFAULT_ADMIN_USER_ID)
|
|
.header(TENANT_HEADER, DEFAULT_TENANT)
|
|
.header(PERMISSIONS_HEADER, PERMISSIONS_CREATE_READ)
|
|
.header(IDEMPOTENCY_HEADER, key)
|
|
.contentType(MediaType.APPLICATION_JSON)
|
|
.content(payload))
|
|
.andExpect(status().isAccepted())
|
|
.andReturn();
|
|
String batchId = extractBatchId(create);
|
|
batchIdsToCleanup.add(batchId);
|
|
awaitApplied(key);
|
|
|
|
MvcResult logs = mockMvc.perform(get(BASE_URL + "/" + batchId + "/logs")
|
|
.header(USER_HEADER, ApiTestDataSeeder.DEFAULT_ADMIN_USER_ID)
|
|
.header(TENANT_HEADER, DEFAULT_TENANT)
|
|
.header(PERMISSIONS_HEADER, PERMISSIONS_CREATE_READ))
|
|
.andExpect(status().isOk())
|
|
.andReturn();
|
|
|
|
JsonNode root = objectMapper.readTree(logs.getResponse().getContentAsString());
|
|
JsonNode data = root.path("data");
|
|
boolean hasOperator = false;
|
|
for (JsonNode node : data) {
|
|
String createdBy = node.path("createdBy").asText();
|
|
if (ApiTestDataSeeder.DEFAULT_ADMIN_USER_ID.equals(createdBy)) {
|
|
hasOperator = true;
|
|
break;
|
|
}
|
|
}
|
|
assertThat(hasOperator).isTrue();
|
|
}
|
|
|
|
private String extractBatchId(MvcResult result) throws Exception {
|
|
JsonNode root = objectMapper.readTree(result.getResponse().getContentAsString());
|
|
return root.path("data").path("batchId").asText();
|
|
}
|
|
|
|
private String awaitApplied(String idempotencyKey) throws Exception {
|
|
for (int i = 0; i < 120; i++) {
|
|
MvcResult poll = mockMvc.perform(get(BASE_URL + "/idempotency/" + idempotencyKey)
|
|
.header(USER_HEADER, ApiTestDataSeeder.DEFAULT_ADMIN_USER_ID)
|
|
.header(TENANT_HEADER, DEFAULT_TENANT)
|
|
.header(PERMISSIONS_HEADER, PERMISSIONS_CREATE_READ))
|
|
.andReturn();
|
|
if (poll.getResponse().getStatus() == 200) {
|
|
JsonNode root = objectMapper.readTree(poll.getResponse().getContentAsString());
|
|
String status = root.path("data").path("status").asText();
|
|
if ("APPLIED".equals(status)) {
|
|
return root.path("data").path("batchId").asText();
|
|
}
|
|
if ("FAILED".equals(status)) {
|
|
throw new AssertionError("batch failed unexpectedly: key=" + idempotencyKey);
|
|
}
|
|
}
|
|
Thread.sleep(50);
|
|
}
|
|
throw new AssertionError("batch not applied within timeout: key=" + idempotencyKey);
|
|
}
|
|
|
|
private String seedOrder(String clerkId, LocalDateTime endTime, BigDecimal estimatedRevenue) {
|
|
PlayOrderInfoEntity order = new PlayOrderInfoEntity();
|
|
String id = "order-deduct-" + IdUtils.getUuid();
|
|
order.setId(id);
|
|
order.setTenantId(ApiTestDataSeeder.DEFAULT_TENANT_ID);
|
|
order.setOrderNo("DED-" + System.currentTimeMillis());
|
|
order.setOrderStatus("3");
|
|
order.setOrderType("2");
|
|
order.setPlaceType("0");
|
|
order.setRewardType("0");
|
|
order.setAcceptBy(clerkId);
|
|
order.setPurchaserBy(ApiTestDataSeeder.DEFAULT_CUSTOMER_ID);
|
|
order.setCommodityId(ApiTestDataSeeder.DEFAULT_COMMODITY_ID);
|
|
order.setOrderMoney(new BigDecimal("120.50"));
|
|
order.setFinalAmount(order.getOrderMoney());
|
|
order.setEstimatedRevenue(estimatedRevenue);
|
|
order.setOrderSettlementState("1");
|
|
order.setOrderEndTime(endTime);
|
|
order.setOrderSettlementTime(endTime);
|
|
Date nowDate = toDate(LocalDateTime.now());
|
|
order.setCreatedBy(ApiTestDataSeeder.DEFAULT_ADMIN_USER_ID);
|
|
order.setCreatedTime(nowDate);
|
|
order.setUpdatedBy(ApiTestDataSeeder.DEFAULT_ADMIN_USER_ID);
|
|
order.setUpdatedTime(nowDate);
|
|
order.setDeleted(false);
|
|
orderInfoService.save(order);
|
|
ordersToCleanup.add(id);
|
|
return id;
|
|
}
|
|
|
|
private void seedOrderEarningLine(String clerkId, String orderId, BigDecimal amount, String status, EarningsType earningType) {
|
|
EarningsLineEntity entity = new EarningsLineEntity();
|
|
String id = "earn-deduct-" + IdUtils.getUuid();
|
|
entity.setId(id);
|
|
entity.setTenantId(ApiTestDataSeeder.DEFAULT_TENANT_ID);
|
|
entity.setClerkId(clerkId);
|
|
entity.setOrderId(orderId);
|
|
entity.setSourceType(EarningsSourceType.ORDER);
|
|
entity.setSourceId(orderId);
|
|
entity.setAmount(amount);
|
|
entity.setEarningType(earningType);
|
|
entity.setStatus(status);
|
|
entity.setUnlockTime(LocalDateTime.now().minusHours(1));
|
|
Date nowDate = toDate(LocalDateTime.now());
|
|
entity.setCreatedBy(ApiTestDataSeeder.DEFAULT_ADMIN_USER_ID);
|
|
entity.setCreatedTime(nowDate);
|
|
entity.setUpdatedBy(ApiTestDataSeeder.DEFAULT_ADMIN_USER_ID);
|
|
entity.setUpdatedTime(nowDate);
|
|
entity.setDeleted(false);
|
|
earningsService.save(entity);
|
|
earningsToCleanup.add(id);
|
|
}
|
|
|
|
private void seedAdjustmentEarningLine(String clerkId, BigDecimal amount, String status) {
|
|
EarningsLineEntity entity = new EarningsLineEntity();
|
|
String id = "earn-deduct-adj-" + IdUtils.getUuid();
|
|
entity.setId(id);
|
|
entity.setTenantId(ApiTestDataSeeder.DEFAULT_TENANT_ID);
|
|
entity.setClerkId(clerkId);
|
|
entity.setOrderId(null);
|
|
entity.setSourceType(EarningsSourceType.ADJUSTMENT);
|
|
entity.setSourceId("adj-seed-" + IdUtils.getUuid());
|
|
entity.setAmount(amount);
|
|
entity.setEarningType(EarningsType.ADJUSTMENT);
|
|
entity.setStatus(status);
|
|
entity.setUnlockTime(LocalDateTime.now().minusHours(1));
|
|
Date nowDate = toDate(LocalDateTime.now());
|
|
entity.setCreatedBy(ApiTestDataSeeder.DEFAULT_ADMIN_USER_ID);
|
|
entity.setCreatedTime(nowDate);
|
|
entity.setUpdatedBy(ApiTestDataSeeder.DEFAULT_ADMIN_USER_ID);
|
|
entity.setUpdatedTime(nowDate);
|
|
entity.setDeleted(false);
|
|
earningsService.save(entity);
|
|
earningsToCleanup.add(id);
|
|
}
|
|
|
|
private Date toDate(LocalDateTime time) {
|
|
return Date.from(time.atZone(ZoneId.systemDefault()).toInstant());
|
|
}
|
|
|
|
private void cleanupBatches(List<String> batchIds) {
|
|
if (batchIds == null || batchIds.isEmpty()) {
|
|
return;
|
|
}
|
|
if (!tableExists("play_earnings_deduction_batch")) {
|
|
return;
|
|
}
|
|
for (String batchId : batchIds) {
|
|
cleanupBatch(batchId);
|
|
}
|
|
batchIds.clear();
|
|
}
|
|
|
|
private void cleanupBatch(String batchId) {
|
|
if (batchId == null || batchId.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
List<String> adjustmentIds = new ArrayList<>();
|
|
if (tableExists("play_earnings_deduction_item")) {
|
|
adjustmentIds = jdbcTemplate.queryForList(
|
|
"select adjustment_id from play_earnings_deduction_item where batch_id = ? and adjustment_id is not null",
|
|
String.class,
|
|
batchId);
|
|
}
|
|
|
|
if (!adjustmentIds.isEmpty()) {
|
|
earningsService.lambdaUpdate()
|
|
.eq(EarningsLineEntity::getTenantId, ApiTestDataSeeder.DEFAULT_TENANT_ID)
|
|
.eq(EarningsLineEntity::getSourceType, EarningsSourceType.ADJUSTMENT)
|
|
.in(EarningsLineEntity::getSourceId, adjustmentIds)
|
|
.remove();
|
|
adjustmentService.removeByIds(adjustmentIds);
|
|
}
|
|
|
|
if (tableExists("play_earnings_deduction_batch_log")) {
|
|
jdbcTemplate.update("delete from play_earnings_deduction_batch_log where batch_id = ?", batchId);
|
|
}
|
|
if (tableExists("play_earnings_deduction_item")) {
|
|
jdbcTemplate.update("delete from play_earnings_deduction_item where batch_id = ?", batchId);
|
|
}
|
|
jdbcTemplate.update("delete from play_earnings_deduction_batch where id = ?", batchId);
|
|
}
|
|
|
|
private boolean tableExists(String table) {
|
|
try {
|
|
Integer count = jdbcTemplate.queryForObject(
|
|
"select count(*) from information_schema.tables where lower(table_name)=lower(?)",
|
|
Integer.class,
|
|
table);
|
|
return count != null && count > 0;
|
|
} catch (Exception ignored) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private String ensureTestClerkInDefaultTenant() {
|
|
String clerkId = "clerk-deduct-" + IdUtils.getUuid();
|
|
PlayClerkUserInfoEntity clerk = new PlayClerkUserInfoEntity();
|
|
clerk.setId(clerkId);
|
|
clerk.setTenantId(ApiTestDataSeeder.DEFAULT_TENANT_ID);
|
|
clerk.setSysUserId("sysuser-" + IdUtils.getUuid());
|
|
clerk.setOpenid("openid-" + clerkId);
|
|
clerk.setNickname("Batch Clerk");
|
|
clerk.setGroupId(ApiTestDataSeeder.DEFAULT_GROUP_ID);
|
|
clerk.setLevelId(ApiTestDataSeeder.DEFAULT_CLERK_LEVEL_ID);
|
|
clerk.setFixingLevel("1");
|
|
clerk.setSex("2");
|
|
clerk.setPhone("139" + String.valueOf(System.nanoTime()).substring(0, 8));
|
|
clerk.setWeiChatCode("wechat-" + IdUtils.getUuid());
|
|
clerk.setAvatar("https://example.com/avatar.png");
|
|
clerk.setAccountBalance(BigDecimal.ZERO);
|
|
clerk.setOnboardingState("1");
|
|
clerk.setListingState("1");
|
|
clerk.setDisplayState("1");
|
|
clerk.setOnlineState("1");
|
|
clerk.setRandomOrderState("1");
|
|
clerk.setClerkState("1");
|
|
clerk.setEntryTime(LocalDateTime.now());
|
|
clerk.setToken("token-" + IdUtils.getUuid());
|
|
clerkUserInfoService.save(clerk);
|
|
clerkIdsToCleanup.add(clerkId);
|
|
return clerkId;
|
|
}
|
|
}
|