11
This commit is contained in:
1 parent
38cabe90af
commit
b74b57b962
4 files changed
+48
-4
No files matched your search
+1
@@ -40,6 +40,7 @@ import java.util.concurrent.Executors;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
+3
-1
@@ -11,6 +11,7 @@ import jakarta.annotation.PreDestroy;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
@@ -58,7 +59,8 @@ public class MchCheckoutBillTransferJob {
|
||||
for (SysTenant tenant : response.getData()) {
|
||||
if (tenant == null || tenant.getId() == null || tenant.getId() <= 0) continue;
|
||||
List<Long> ids = new ArrayList<>();
|
||||
TenantBroker.runAs(tenant.getId(), ignored -> items.due().forEach(row -> ids.add(row.getId())));
|
||||
// 任务与租约均由应用写入LocalDateTime,扫描也采用应用时间,数据库NOW可能使用UTC会话时区。
|
||||
TenantBroker.runAs(tenant.getId(), ignored -> items.due(LocalDateTime.now()).forEach(row -> ids.add(row.getId())));
|
||||
for (int from = 0; from < ids.size() && count < ROUND_LIMIT; from += ROUND_LIMIT / WORKERS) {
|
||||
int groupSize = Math.min(ROUND_LIMIT / WORKERS, ROUND_LIMIT - count);
|
||||
List<Long> group = List.copyOf(ids.subList(from, Math.min(ids.size(), from + groupSize)));
|
||||
|
||||
+4
-3
@@ -3,6 +3,7 @@ package com.bsd.pjcloud.wallet.mapper.mch;
|
||||
import com.bsd.pjcloud.common.data.datascope.PjcloudBaseMapper;
|
||||
import com.bsd.pjcloud.wallet.api.entity.mch.MchCheckoutBillTransferItemEntity;
|
||||
import org.apache.ibatis.annotations.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/** 全部结算付款尝试及受理任务数据访问,租户条件由平台自动补充。 */
|
||||
@@ -12,7 +13,7 @@ public interface MchCheckoutBillTransferItemMapper extends PjcloudBaseMapper<Mch
|
||||
@Select("SELECT * FROM mch_checkout_bill_transfer_item WHERE id=#{id} FOR UPDATE")
|
||||
MchCheckoutBillTransferItemEntity lockItem(@Param("id") Long id);
|
||||
|
||||
/** 最多读取100条到期或租约过期的原任务,按执行时间和主键稳定排序。 */
|
||||
@Select("SELECT * FROM mch_checkout_bill_transfer_item WHERE dispatch_status IN ('READY','RETRY','PROCESSING') AND next_run_time <= NOW() AND (lease_expire_time IS NULL OR lease_expire_time < NOW()) ORDER BY next_run_time,id LIMIT 100")
|
||||
List<MchCheckoutBillTransferItemEntity> due();
|
||||
/** 最多读取100条到期原任务;now与创建、领取使用同一应用时间,避免数据库会话时区延迟任务,按执行时间和主键稳定排序。 */
|
||||
@Select("SELECT * FROM mch_checkout_bill_transfer_item WHERE dispatch_status IN ('READY','RETRY','PROCESSING') AND next_run_time <= #{now} AND (lease_expire_time IS NULL OR lease_expire_time <= #{now}) ORDER BY next_run_time,id LIMIT 100")
|
||||
List<MchCheckoutBillTransferItemEntity> due(@Param("now") LocalDateTime now);
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package com.bsd.pjcloud.wallet.mapper.mch;
|
||||
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.apache.ibatis.mapping.BoundSql;
|
||||
import org.apache.ibatis.mapping.ParameterMapping;
|
||||
import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
|
||||
import org.apache.ibatis.session.Configuration;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/** 付款任务扫描的实际MyBatis绑定回归,防止数据库会话时区与应用时间混用导致任务永久待受理。 */
|
||||
class MchCheckoutBillTransferItemMapperTest {
|
||||
|
||||
/** 到期时间与租约截止均绑定同一个应用时间,保留原可领取状态、稳定排序和每轮上限。 */
|
||||
@Test
|
||||
void dueScanBindsApplicationTimeForBothDueAndLeaseChecks() throws Exception {
|
||||
Method method = MchCheckoutBillTransferItemMapper.class.getMethod("due", LocalDateTime.class);
|
||||
String sql = String.join(" ", method.getAnnotation(Select.class).value());
|
||||
LocalDateTime now = LocalDateTime.of(2026, 11, 10, 20, 7);
|
||||
BoundSql bound = new XMLLanguageDriver().createSqlSource(new Configuration(), sql, Map.class).getBoundSql(Map.of("now", now));
|
||||
|
||||
assertEquals("now", method.getParameters()[0].getAnnotation(Param.class).value());
|
||||
assertEquals(List.of("now", "now"), bound.getParameterMappings().stream().map(ParameterMapping::getProperty).toList());
|
||||
assertEquals(now, ((Map<?, ?>) bound.getParameterObject()).get("now"));
|
||||
assertFalse(sql.toUpperCase(Locale.ROOT).contains("NOW("));
|
||||
assertFalse(sql.toUpperCase(Locale.ROOT).contains("CURRENT_TIMESTAMP"));
|
||||
assertTrue(bound.getSql().contains("next_run_time <= ?"));
|
||||
assertTrue(bound.getSql().contains("lease_expire_time IS NULL OR lease_expire_time <= ?"));
|
||||
assertTrue(sql.contains("dispatch_status IN ('READY','RETRY','PROCESSING')"));
|
||||
assertTrue(sql.endsWith("ORDER BY next_run_time,id LIMIT 100"));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user