yeg
2026-09-12 d32e0dee609ab13220908ab28a11390bede07c84
talent-card-server/src/main/java/com/talentcard/server/business/merchant/AutoWithdrawalScheduler.java
@@ -6,19 +6,28 @@
import org.springframework.stereotype.Component;
/**
 * 北京时间每日凌晨 00:10 触发前一个自然日的自动提现汇总(cron 的时区固定为北京时间,
 * 不依赖服务器或 JVM 的默认时区)。
 * 自动提现的定时入口。
 *
 * <p>每天北京时间 00:10({@code app.merchant.auto-withdrawal.cron},时区固定为北京时间)汇总前一个
 * 自然日并立即把新记录提交给斗拱;随后每隔一段时间(默认 5 分钟)把还没提交的记录补交一次,并查询
 * 处理中的记录收敛状态。查询要等记录提交后过了 {@code query-after-minutes} 才进行,避免把通道「还没
 * 落库」误判成失败。
 *
 * <p>提交本身是幂等的:只有 {@code pending} 的记录能被认领,重复执行或两个实例并发都不会重复出账。
 */
@Component
public class AutoWithdrawalScheduler {
    private static final Logger log = LoggerFactory.getLogger(AutoWithdrawalScheduler.class);
    private final AutoWithdrawalAggregationService service;
    private final AutoWithdrawalAggregationService aggregation;
    private final AutoWithdrawalResultService results;
    private final AutoWithdrawalProperties properties;
    public AutoWithdrawalScheduler(AutoWithdrawalAggregationService service, AutoWithdrawalProperties properties) {
        this.service = service;
    public AutoWithdrawalScheduler(AutoWithdrawalAggregationService aggregation, AutoWithdrawalResultService results,
                                   AutoWithdrawalProperties properties) {
        this.aggregation = aggregation;
        this.results = results;
        this.properties = properties;
    }
@@ -26,10 +35,37 @@
    public void aggregateDaily() {
        if (!properties.isEnabled()) return;
        try {
            AutoWithdrawalAggregationService.Result result = service.run();
            AutoWithdrawalAggregationService.Result result = aggregation.run();
            log.info("Auto withdrawal aggregation {}", result);
        } catch (RuntimeException exception) {
            log.warn("Auto withdrawal aggregation run failed: {}", exception.getClass().getSimpleName());
        }
        // 汇总之后马上提交:金额当晚就能动,不用等到下一次轮询。
        submitPending();
    }
    @Scheduled(fixedDelayString = "${app.merchant.auto-withdrawal.result-check-fixed-delay-ms:300000}")
    public void processResults() {
        if (!properties.isEnabled() || !properties.isResultCheckEnabled()) return;
        submitPending();
        reconcile();
    }
    private void submitPending() {
        try {
            int submitted = results.submitPending();
            if (submitted > 0) log.info("Submitted {} auto withdrawals to Huifu", submitted);
        } catch (RuntimeException exception) {
            log.warn("Auto withdrawal submission failed: {}", exception.getClass().getSimpleName());
        }
    }
    private void reconcile() {
        try {
            int settled = results.reconcileProcessing();
            if (settled > 0) log.info("{} auto withdrawals reached a final state", settled);
        } catch (RuntimeException exception) {
            log.warn("Auto withdrawal result reconciliation failed: {}", exception.getClass().getSimpleName());
        }
    }
}