refactor(ActionData): migrate to Kotlin sealed class and data classes, update planner/scheduler usage

This commit is contained in:
2026-02-08 16:27:44 +08:00
parent 8ca2b9998d
commit cdea8d6322
7 changed files with 122 additions and 108 deletions

View File

@@ -1,80 +1,143 @@
package work.slhaf.partner.core.action.entity;
package work.slhaf.partner.core.action.entity
import lombok.Data;
import work.slhaf.partner.module.modules.action.dispatcher.executor.entity.HistoryAction;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import work.slhaf.partner.module.modules.action.dispatcher.executor.entity.HistoryAction
import java.time.ZonedDateTime
import java.util.*
/**
* 行动模块传递的行动数据包含行动uuid、倾向、状态、行动链、结果、发起原因、行动描述等信息。
*/
@Data
public abstract class ActionData {
sealed class ActionData {
/**
* 行动ID
*/
protected String uuid;
val uuid: String = UUID.randomUUID().toString()
/**
* 行动倾向
*/
protected String tendency;
abstract val tendency: String
/**
* 行动状态
*/
protected ActionStatus status;
var status: ActionStatus = ActionStatus.PREPARE
/**
* 行动链
*/
protected Map<Integer, List<MetaAction>> actionChain;
abstract val actionChain: MutableMap<Int, MutableList<MetaAction>>
/**
* 行动阶段(当前阶段)
*/
protected int executingStage;
var executingStage: Int = 0
/**
* 行动结果
*/
protected String result;
protected Map<Integer, List<HistoryAction>> history = new HashMap<>();
lateinit var result: String
val history: MutableMap<Int, MutableList<HistoryAction>> = mutableMapOf()
/**
* 修复上下文
*/
protected Map<Integer, List<String>> additionalContext;
val additionalContext: MutableMap<Int, MutableList<String>> = mutableMapOf()
/**
* 行动原因
*/
protected String reason;
abstract val reason: String
/**
* 行动描述
*/
protected String description;
abstract val description: String
/**
* 行动来源
*/
protected String source;
abstract val source: String
public enum ActionStatus {
enum class ActionStatus {
/**
* 执行成功
*/
SUCCESS,
/**
* 执行失败
*/
FAILED,
/**
* 执行中
*/
EXECUTING,
/**
* 暂时中断
*/
INTERRUPTED,
/**
* 预备执行
*/
PREPARE
}
}
/**
* 计划行动数据类,继承自{@link ActionData},扩展了属性{@link ScheduledActionData#type}和{@link ScheduledActionData#scheduleContent},用于标识计划类型(单次还是周期性任务)和计划内容
*/
data class ScheduledActionData(
override val tendency: String,
override val actionChain: MutableMap<Int, MutableList<MetaAction>>,
override val reason: String,
override val description: String,
override val source: String,
val scheduleType: ScheduleType,
val scheduleContent: String,
) : ActionData() {
val scheduleHistories = ArrayList<ScheduleHistory>()
fun recordAndReset() {
val newHistory = ScheduleHistory(ZonedDateTime.now(), result, history.toMap())
scheduleHistories.add(newHistory)
additionalContext.clear()
executingStage = 0
for (entry in actionChain) {
for (action in entry.value) {
action.params.clear()
action.result = MetaAction.Result()
}
}
status = ActionStatus.PREPARE
}
enum class ScheduleType {
CYCLE,
ONCE
}
data class ScheduleHistory(
val endTime: ZonedDateTime,
val result: String,
val history: Map<Int, List<HistoryAction>>
)
}
/**
* 即时行动数据类
*/
data class ImmediateActionData(
override val tendency: String,
override val actionChain: MutableMap<Int, MutableList<MetaAction>>,
override val reason: String,
override val description: String,
override val source: String,
) : ActionData()

View File

@@ -1,12 +0,0 @@
package work.slhaf.partner.core.action.entity;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 即时行动数据类,继承自{@link ActionData}
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class ImmediateActionData extends ActionData {
}

View File

@@ -1,44 +0,0 @@
package work.slhaf.partner.core.action.entity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import work.slhaf.partner.module.modules.action.dispatcher.executor.entity.HistoryAction;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 计划行动数据类,继承自{@link ActionData},扩展了属性{@link ScheduledActionData#type}和{@link ScheduledActionData#scheduleContent},用于标识计划类型(单次还是周期性任务)和计划内容
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class ScheduledActionData extends ActionData {
private ScheduledType type;
private String scheduleContent; //如果为周期则对应cron表达式如果为一次性则对应为ZonedDateTime字符串
private List<ScheduledHistory> scheduledHistories = new ArrayList<>();
public void recordAndReset() {
this.scheduledHistories.add(new ScheduledHistory(ZonedDateTime.now(), this.result, Map.copyOf(this.history)));
// 清理执行时内容
this.additionalContext.clear();
this.executingStage = 0;
this.getActionChain().forEach((stage, actions) -> {
for (MetaAction action : actions) {
action.getParams().clear();
action.setResult(new MetaAction.Result());
}
});
this.setStatus(ActionStatus.PREPARE);
}
public enum ScheduledType {
CYCLE, ONCE
}
private record ScheduledHistory(ZonedDateTime endTime, String result, Map<Integer, List<HistoryAction>> history) {
}
}

View File

@@ -90,7 +90,7 @@ class ActionScheduler : AgentRunningSubModule<Set<ScheduledActionData>, Void>()
}
val parseToZonedDateTime = parseToZonedDateTime(
actionData.type,
actionData.scheduleType,
actionData.scheduleContent,
ZonedDateTime.now()
) ?: run {
@@ -226,7 +226,7 @@ class ActionScheduler : AgentRunningSubModule<Set<ScheduledActionData>, Void>()
for (actionData in primaryActions) {
val latestExecutingTime =
parseToZonedDateTime(
actionData.type,
actionData.scheduleType,
actionData.scheduleContent,
now
) ?: run {
@@ -250,12 +250,13 @@ class ActionScheduler : AgentRunningSubModule<Set<ScheduledActionData>, Void>()
}
private fun parseToZonedDateTime(
scheduleType: ScheduledActionData.ScheduledType,
scheduleType: ScheduledActionData.ScheduleType,
scheduleContent: String,
now: ZonedDateTime
): ZonedDateTime? {
return when (scheduleType) {
ScheduledActionData.ScheduledType.CYCLE -> {
ScheduledActionData.ScheduleType.CYCLE
-> {
val cron = try {
cronParser.parse(scheduleContent).validate()
} catch (_: Exception) {
@@ -265,7 +266,7 @@ class ActionScheduler : AgentRunningSubModule<Set<ScheduledActionData>, Void>()
executionTime.nextExecution(now).getOrNull()
}
ScheduledActionData.ScheduledType.ONCE -> {
ScheduledActionData.ScheduleType.ONCE -> {
val executionTime = try {
ZonedDateTime.parse(scheduleContent)
} catch (_: Exception) {

View File

@@ -153,7 +153,7 @@ public class ActionPlanner extends PreRunningModule {
private void putActionData(List<EvaluatorResult> evaluatorResults, PartnerRunningFlowContext context) {
for (EvaluatorResult evaluatorResult : evaluatorResults) {
ActionData actionData = assemblyHelper.buildActionData(evaluatorResult);
ActionData actionData = assemblyHelper.buildActionData(evaluatorResult, context.getUserId());
if (evaluatorResult.isNeedConfirm()) {
actionCapability.putPendingActions(context.getUserId(), actionData);
} else {
@@ -231,24 +231,25 @@ public class ActionPlanner extends PreRunningModule {
return input;
}
private ActionData buildActionData(EvaluatorResult evaluatorResult) {
private ActionData buildActionData(EvaluatorResult evaluatorResult, String userId) {
Map<Integer, List<MetaAction>> actionChain = getActionChain(evaluatorResult);
return switch (evaluatorResult.getType()) {
case PLANNING -> {
ScheduledActionData actionInfo = new ScheduledActionData();
actionInfo.setActionChain(actionChain);
actionInfo.setScheduleContent(evaluatorResult.getScheduleContent());
actionInfo.setStatus(ActionData.ActionStatus.PREPARE);
actionInfo.setUuid(UUID.randomUUID().toString());
yield actionInfo;
}
case IMMEDIATE -> {
ImmediateActionData actionInfo = new ImmediateActionData();
actionInfo.setActionChain(actionChain);
actionInfo.setStatus(ActionData.ActionStatus.PREPARE);
actionInfo.setUuid(UUID.randomUUID().toString());
yield actionInfo;
}
case PLANNING -> new ScheduledActionData(
evaluatorResult.getTendency(),
actionChain,
evaluatorResult.getReason(),
evaluatorResult.getDescription(),
userId,
evaluatorResult.getScheduleType(),
evaluatorResult.getScheduleContent()
);
case IMMEDIATE -> new ImmediateActionData(
evaluatorResult.getTendency(),
actionChain,
evaluatorResult.getReason(),
evaluatorResult.getDescription(),
userId
);
};
}

View File

@@ -1,6 +1,7 @@
package work.slhaf.partner.module.modules.action.planner.evaluator.entity;
import lombok.Data;
import work.slhaf.partner.core.action.entity.ScheduledActionData;
import java.util.List;
import java.util.Map;
@@ -11,8 +12,11 @@ public class EvaluatorResult {
private boolean needConfirm;
private ActionType type;
private String scheduleContent;
private ScheduledActionData.ScheduleType scheduleType;
private Map<Integer, List<String>> primaryActionChain;
private String tendency;
private String reason;
private String description;
public enum ActionType {
IMMEDIATE, PLANNING

View File

@@ -1,6 +1,7 @@
package work.slhaf.partner.module.modules.action.dispatcher.executor;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
@@ -373,15 +374,15 @@ class ActionExecutorTest {
}
private ImmediateActionData buildActionData(Map<Integer, List<MetaAction>> actionChain) {
ImmediateActionData actionData = new ImmediateActionData();
actionData.setStatus(ActionData.ActionStatus.PREPARE);
actionData.setActionChain(actionChain);
actionData.setAdditionalContext(initAdditionalContext(actionChain));
actionData.setReason("reason");
actionData.setDescription("desc");
actionData.setSource("source");
actionData.setTendency("tendency");
return actionData;
val immediateActionData = new ImmediateActionData(
"tendency",
actionChain,
"reason",
"desc",
"source"
);
immediateActionData.getAdditionalContext().putAll(initAdditionalContext(actionChain));
return immediateActionData;
}
private Map<Integer, List<MetaAction>> singleStageChain(boolean io) {