refactor(ActionScheduler): support receiving single data that implements Schedulable and Action in ActionScheduler

This commit is contained in:
2026-03-07 15:30:25 +08:00
parent d9e384960f
commit ae1b7fc033
4 changed files with 24 additions and 31 deletions

View File

@@ -16,7 +16,6 @@ import work.slhaf.partner.module.modules.action.scheduler.ActionScheduler;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Phaser; import java.util.concurrent.Phaser;
@@ -160,7 +159,7 @@ public class ActionExecutor extends AbstractAgentModule.Standalone {
// 如果是 ScheduledActionData, 则重置 ActionData 内容,记录执行历史与最终结果 // 如果是 ScheduledActionData, 则重置 ActionData 内容,记录执行历史与最终结果
if (executableAction instanceof SchedulableExecutableAction scheduledActionData) { if (executableAction instanceof SchedulableExecutableAction scheduledActionData) {
scheduledActionData.recordAndReset(); scheduledActionData.recordAndReset();
actionScheduler.schedule(Set.of(scheduledActionData)); actionScheduler.schedule(scheduledActionData);
} else { } else {
executableAction.setStatus(Action.Status.SUCCESS); executableAction.setStatus(Action.Status.SUCCESS);
} }

View File

@@ -152,7 +152,7 @@ public class ActionPlanner extends PreRunningAbstractAgentModuleAbstract {
} }
// execute or schedule it immediately // execute or schedule it immediately
switch (executableAction) { switch (executableAction) {
case SchedulableExecutableAction action -> actionScheduler.schedule(Set.of(action)); case SchedulableExecutableAction action -> actionScheduler.schedule(action);
case ImmediateExecutableAction action -> actionExecutor.execute(action); case ImmediateExecutableAction action -> actionExecutor.execute(action);
default -> log.error("unknown executable action type: {}", executableAction.getClass().getSimpleName()); default -> log.error("unknown executable action type: {}", executableAction.getClass().getSimpleName());
} }

View File

@@ -70,16 +70,14 @@ class ActionScheduler : AbstractAgentModule.Standalone() {
// TODO any implementations of Action should be record into ActionCore // TODO any implementations of Action should be record into ActionCore
// TODO the method in ActionCapability should be compatible with different Action types // TODO the method in ActionCapability should be compatible with different Action types
fun schedule(input: Set<Schedulable>) = schedulerScope.launch { fun <T> schedule(schedulableAction: T) where T : Action, T : Schedulable = schedulerScope.launch {
for (schedulableData in input) { if (!schedulableAction.enabled) {
if (!schedulableData.enabled) { return@launch
continue }
} log.debug("New data to schedule: {}", schedulableAction)
log.debug("New data to schedule: {}", schedulableData) timeWheel.schedule(schedulableAction)
timeWheel.schedule(schedulableData) if (schedulableAction is SchedulableExecutableAction) {
if (schedulableData is SchedulableExecutableAction) { actionCapability.putAction(schedulableAction)
actionCapability.putAction(schedulableData)
}
} }
} }

View File

@@ -14,11 +14,11 @@ import org.mockito.Mockito.verify
import org.mockito.junit.jupiter.MockitoExtension import org.mockito.junit.jupiter.MockitoExtension
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import work.slhaf.partner.core.action.ActionCapability import work.slhaf.partner.core.action.ActionCapability
import work.slhaf.partner.core.action.entity.Action
import work.slhaf.partner.core.action.entity.ExecutableAction import work.slhaf.partner.core.action.entity.ExecutableAction
import work.slhaf.partner.core.action.entity.Schedulable import work.slhaf.partner.core.action.entity.Schedulable
import work.slhaf.partner.core.action.entity.SchedulableExecutableAction import work.slhaf.partner.core.action.entity.SchedulableExecutableAction
import work.slhaf.partner.module.modules.action.executor.ActionExecutor import work.slhaf.partner.module.modules.action.executor.ActionExecutor
import work.slhaf.partner.module.modules.action.executor.entity.ActionExecutorInput
import work.slhaf.partner.module.modules.action.scheduler.ActionScheduler import work.slhaf.partner.module.modules.action.scheduler.ActionScheduler
import java.time.ZonedDateTime import java.time.ZonedDateTime
import java.time.temporal.ChronoUnit import java.time.temporal.ChronoUnit
@@ -83,10 +83,8 @@ class ActionSchedulerTest {
.thenReturn(actions as Set<ExecutableAction>) .thenReturn(actions as Set<ExecutableAction>)
Mockito.`when`(actionExecutor.execute(any())) Mockito.`when`(actionExecutor.execute(any()))
.thenAnswer { .thenAnswer {
val input = it.arguments[0] as ActionExecutorInput val actionData = it.arguments[0] as ExecutableAction
for (actionData in input.actions) { log.info("Executed action $actionData at ${ZonedDateTime.now()}")
log.info("Executed action $actionData at ${ZonedDateTime.now()}")
}
null null
} }
actionScheduler.init() actionScheduler.init()
@@ -94,11 +92,7 @@ class ActionSchedulerTest {
val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default + CoroutineName("ActionSchedulerTest")) val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default + CoroutineName("ActionSchedulerTest"))
scope.launch { scope.launch {
actionScheduler.schedule( actionScheduler.schedule(buildAction(now.plusSeconds(5)))
setOf(
buildAction(now.plusSeconds(5)),
)
)
} }
readlnOrNull() readlnOrNull()
} }
@@ -112,7 +106,7 @@ class ActionSchedulerTest {
ZonedDateTime.now().plusHours(1).toString() ZonedDateTime.now().plusHours(1).toString()
) )
actionScheduler.schedule(setOf(action)) actionScheduler.schedule(action)
verify(actionCapability, times(1)).putAction(action) verify(actionCapability, times(1)).putAction(action)
val timeWheel = timeWheel() val timeWheel = timeWheel()
@@ -128,7 +122,7 @@ class ActionSchedulerTest {
type = Schedulable.ScheduleType.ONCE type = Schedulable.ScheduleType.ONCE
) )
actionScheduler.schedule(setOf(action)) actionScheduler.schedule(action)
verify(actionCapability, times(1)).putAction(action) verify(actionCapability, times(1)).putAction(action)
val allScheduled = allScheduledActions(timeWheel()) val allScheduled = allScheduledActions(timeWheel())
@@ -143,7 +137,7 @@ class ActionSchedulerTest {
type = Schedulable.ScheduleType.ONCE type = Schedulable.ScheduleType.ONCE
) )
actionScheduler.schedule(setOf(action)) actionScheduler.schedule(action)
val allScheduled = allScheduledActions(timeWheel()) val allScheduled = allScheduledActions(timeWheel())
assertFalse(allScheduled.contains(action)) assertFalse(allScheduled.contains(action))
@@ -158,7 +152,7 @@ class ActionSchedulerTest {
scheduleContentOverride = "invalid-cron" scheduleContentOverride = "invalid-cron"
) )
actionScheduler.schedule(setOf(action)) actionScheduler.schedule(action)
val allScheduled = allScheduledActions(timeWheel()) val allScheduled = allScheduledActions(timeWheel())
assertFalse(allScheduled.contains(action)) assertFalse(allScheduled.contains(action))
@@ -176,7 +170,7 @@ class ActionSchedulerTest {
.putAction(action) .putAction(action)
assertThrows(RuntimeException::class.java) { assertThrows(RuntimeException::class.java) {
actionScheduler.schedule(setOf(action)) actionScheduler.schedule(action)
} }
} }
@@ -194,7 +188,7 @@ class ActionSchedulerTest {
setCurrentHour(timeWheel, actionHour) setCurrentHour(timeWheel, actionHour)
setWheelState(timeWheel, "SLEEPING") setWheelState(timeWheel, "SLEEPING")
actionScheduler.schedule(setOf(action)) actionScheduler.schedule(action)
assertEquals("ACTIVE", wheelStateName(timeWheel)) assertEquals("ACTIVE", wheelStateName(timeWheel))
} }
@@ -211,13 +205,15 @@ class ActionSchedulerTest {
type = Schedulable.ScheduleType.ONCE, type = Schedulable.ScheduleType.ONCE,
scheduleContentOverride = ZonedDateTime.now().plusMinutes(2).toString() scheduleContentOverride = ZonedDateTime.now().plusMinutes(2).toString()
) )
nonPrepare.status = ExecutableAction.Status.FAILED nonPrepare.status = Action.Status.FAILED
val invalid = buildScheduledAction( val invalid = buildScheduledAction(
type = Schedulable.ScheduleType.CYCLE, type = Schedulable.ScheduleType.CYCLE,
scheduleContentOverride = "invalid-cron" scheduleContentOverride = "invalid-cron"
) )
actionScheduler.schedule(setOf(ok, nonPrepare, invalid)) actionScheduler.schedule(ok)
actionScheduler.schedule(nonPrepare)
actionScheduler.schedule(invalid)
verify(actionCapability, times(1)).putAction(ok) verify(actionCapability, times(1)).putAction(ok)
verify(actionCapability, times(1)).putAction(nonPrepare) verify(actionCapability, times(1)).putAction(nonPrepare)