自动脚本计费
This commit is contained in:
+203
@@ -0,0 +1,203 @@
|
||||
package com.bms.price.task;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.bms.price.config.BillingProperties;
|
||||
import com.bms.price.domain.PrcBillingTask;
|
||||
import com.bms.price.enums.BillTaskSuccessStatus;
|
||||
import com.bms.price.mapper.PrcBillingTaskMapper;
|
||||
import com.bms.price.service.IPrcBillingTaskService;
|
||||
|
||||
/**
|
||||
* Night window auto-rebill from prc_billing_task.
|
||||
*
|
||||
* <p>During [startHour, endHour) (default 00:00-09:00): pull up to {@value #DEFAULT_BATCH}
|
||||
* task ids (all success_status) -> bill them all -> immediately pull the next page.
|
||||
* Empty page => skip billing and end this run (next cron may pick up new rows).</p>
|
||||
*
|
||||
* <p>Same rebill path as monitor manual retry / filter retry (reset WAITING then process).</p>
|
||||
*/
|
||||
@Component
|
||||
public class PrcBillingTaskNightRetryTask
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(PrcBillingTaskNightRetryTask.class);
|
||||
|
||||
/** Default page size: bill this many, then pull next page */
|
||||
public static final int DEFAULT_BATCH = 500;
|
||||
public static final int MIN_BATCH = 1;
|
||||
public static final int MAX_BATCH = 1000;
|
||||
|
||||
private final AtomicBoolean running = new AtomicBoolean(false);
|
||||
|
||||
/** Keyset cursor within the current local-day window; reset when the window day changes. */
|
||||
private volatile long lastTaskId = 0L;
|
||||
private volatile int windowDayOfYear = -1;
|
||||
private volatile int windowYear = -1;
|
||||
|
||||
@Autowired
|
||||
private BillingProperties billingProperties;
|
||||
|
||||
@Autowired
|
||||
private PrcBillingTaskMapper billingTaskMapper;
|
||||
|
||||
@Autowired
|
||||
@Lazy
|
||||
private IPrcBillingTaskService billingTaskService;
|
||||
|
||||
/**
|
||||
* Fire every minute; if a previous run is still paging batches, skip.
|
||||
* Hour gate uses bms.billing.night-retry start/end.
|
||||
*/
|
||||
@Scheduled(cron = "${bms.billing.night-retry.cron:0 0/1 0-8 * * ?}")
|
||||
public void scheduledTick()
|
||||
{
|
||||
BillingProperties.NightRetry cfg = billingProperties == null ? null : billingProperties.getNightRetry();
|
||||
if (cfg == null || !cfg.isEnabled())
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!inWindow(cfg.getStartHour(), cfg.getEndHour()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!running.compareAndSet(false, true))
|
||||
{
|
||||
log.debug("billing night-retry skip: previous run still paging");
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
runUntilEmptyOrEnd(cfg);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("billing night-retry run failed", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
running.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pull batch -> bill all -> pull next batch, until no data or outside window.
|
||||
*/
|
||||
private void runUntilEmptyOrEnd(BillingProperties.NightRetry cfg)
|
||||
{
|
||||
resetCursorIfNewWindowDay();
|
||||
int batch = clampBatch(cfg.getBatchSize());
|
||||
PrcBillingTask query = new PrcBillingTask();
|
||||
int page = 0;
|
||||
int totalOk = 0;
|
||||
int totalFail = 0;
|
||||
|
||||
while (inWindow(cfg.getStartHour(), cfg.getEndHour()))
|
||||
{
|
||||
List<Long> ids = billingTaskMapper.selectTaskIdsByFilter(
|
||||
query, Long.valueOf(lastTaskId), batch);
|
||||
if (ids == null || ids.isEmpty())
|
||||
{
|
||||
if (lastTaskId > 0L)
|
||||
{
|
||||
lastTaskId = 0L;
|
||||
log.info("billing night-retry full scan done pages={} ok={} fail={}, cursor reset",
|
||||
page, totalOk, totalFail);
|
||||
}
|
||||
else
|
||||
{
|
||||
log.info("billing night-retry no data, skip billing");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
page++;
|
||||
log.info("billing night-retry page={} pull batchSize={} fetched={} lastTaskIdBefore={}",
|
||||
page, batch, ids.size(), lastTaskId);
|
||||
|
||||
int ok = 0;
|
||||
int fail = 0;
|
||||
for (Long taskId : ids)
|
||||
{
|
||||
if (taskId == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
lastTaskId = taskId.longValue();
|
||||
if (!inWindow(cfg.getStartHour(), cfg.getEndHour()))
|
||||
{
|
||||
log.info("billing night-retry stop mid-page: reached end-hour page={} ok={} fail={}",
|
||||
page, totalOk + ok, totalFail + fail);
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
PrcBillingTask task = billingTaskService.retryTask(taskId);
|
||||
if (task != null && BillTaskSuccessStatus.SUCCESS.getCode().equals(task.getSuccessStatus()))
|
||||
{
|
||||
ok++;
|
||||
}
|
||||
else
|
||||
{
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
catch (RuntimeException e)
|
||||
{
|
||||
fail++;
|
||||
log.warn("billing night-retry failed taskId={} msg={}", taskId, e.getMessage());
|
||||
}
|
||||
}
|
||||
totalOk += ok;
|
||||
totalFail += fail;
|
||||
log.info("billing night-retry page={} done fetched={} ok={} fail={} lastTaskId={}",
|
||||
page, ids.size(), ok, fail, lastTaskId);
|
||||
// This page billed; loop immediately pulls the next page.
|
||||
}
|
||||
log.info("billing night-retry window closed pages={} ok={} fail={}", page, totalOk, totalFail);
|
||||
}
|
||||
|
||||
private void resetCursorIfNewWindowDay()
|
||||
{
|
||||
Calendar cal = Calendar.getInstance();
|
||||
int year = cal.get(Calendar.YEAR);
|
||||
int day = cal.get(Calendar.DAY_OF_YEAR);
|
||||
if (year != windowYear || day != windowDayOfYear)
|
||||
{
|
||||
windowYear = year;
|
||||
windowDayOfYear = day;
|
||||
lastTaskId = 0L;
|
||||
}
|
||||
}
|
||||
|
||||
static boolean inWindow(int startHour, int endHour)
|
||||
{
|
||||
int start = Math.max(0, Math.min(23, startHour));
|
||||
int end = Math.max(1, Math.min(24, endHour));
|
||||
if (start >= end)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
|
||||
return hour >= start && hour < end;
|
||||
}
|
||||
|
||||
static int clampBatch(int batchSize)
|
||||
{
|
||||
if (batchSize < MIN_BATCH)
|
||||
{
|
||||
return DEFAULT_BATCH;
|
||||
}
|
||||
if (batchSize > MAX_BATCH)
|
||||
{
|
||||
return MAX_BATCH;
|
||||
}
|
||||
return batchSize;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user