values = pvmActivity.getOutcomeTransitions().values();
+ CommonGatewayHelper.leaveAndConcurrentlyForkIfNeeded(context, pvmActivity, values);
+
+ return true;
+ }
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/common/util/IdConverter.java b/core/src/main/java/com/alibaba/smart/framework/engine/common/util/IdConverter.java
new file mode 100644
index 000000000..a39ee832c
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/common/util/IdConverter.java
@@ -0,0 +1,163 @@
+package com.alibaba.smart.framework.engine.common.util;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import com.alibaba.smart.framework.engine.exception.ValidationException;
+
+/**
+ * ID type converter utility class.
+ *
+ * Provides unified ID type conversion between String (external API) and Long (database storage).
+ * Centralizes all conversion logic to eliminate scattered Long.valueOf() calls and improve error handling.
+ *
+ * @author SmartEngine Team
+ * @since 1.x.x
+ */
+public final class IdConverter {
+
+ private IdConverter() {
+ // Utility class, prevent instantiation
+ }
+
+ // ============ String → Long Conversion ============
+
+ /**
+ * Convert String ID to Long.
+ *
+ * @param id String ID
+ * @return Long ID, or null if input is null
+ * @throws ValidationException if ID format is invalid
+ */
+ public static Long toLong(String id) {
+ if (id == null) {
+ return null;
+ }
+ try {
+ return Long.parseLong(id.trim());
+ } catch (NumberFormatException e) {
+ throw new ValidationException("Invalid ID format: '" + id + "'. Expected a valid numeric ID.");
+ }
+ }
+
+ /**
+ * Convert String ID to Long with field name for better error message.
+ *
+ * @param id String ID
+ * @param fieldName field name for error message
+ * @return Long ID, or null if input is null
+ * @throws ValidationException if ID format is invalid
+ */
+ public static Long toLong(String id, String fieldName) {
+ if (id == null) {
+ return null;
+ }
+ try {
+ return Long.parseLong(id.trim());
+ } catch (NumberFormatException e) {
+ throw new ValidationException(
+ "Invalid " + fieldName + " format: '" + id + "'. Expected a valid numeric ID.");
+ }
+ }
+
+ /**
+ * Safe conversion, returns null instead of throwing exception on failure.
+ *
+ * @param id String ID
+ * @return Long ID, or null if input is null or invalid
+ */
+ public static Long toLongOrNull(String id) {
+ if (id == null) {
+ return null;
+ }
+ try {
+ return Long.parseLong(id.trim());
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Batch convert String ID list to Long list.
+ *
+ *
Returns null if input is null (important for MyBatis condition checks),
+ * returns empty list if input is empty.
+ *
+ * @param ids String ID list
+ * @return Long ID list, null if input is null, empty list if input is empty
+ * @throws ValidationException if any ID format is invalid
+ */
+ public static List toLongList(List ids) {
+ if (ids == null) {
+ return null;
+ }
+ if (ids.isEmpty()) {
+ return new ArrayList<>();
+ }
+ return ids.stream()
+ .map(IdConverter::toLong)
+ .collect(Collectors.toList());
+ }
+
+ // ============ Long → String Conversion ============
+
+ /**
+ * Convert Long ID to String.
+ *
+ * @param id Long ID
+ * @return String ID, or null if input is null
+ */
+ public static String toString(Long id) {
+ return id != null ? id.toString() : null;
+ }
+
+ /**
+ * Batch convert Long ID list to String list.
+ *
+ * @param ids Long ID list
+ * @return String ID list, or empty list if input is null/empty
+ */
+ public static List toStringList(List ids) {
+ if (ids == null || ids.isEmpty()) {
+ return new ArrayList<>();
+ }
+ return ids.stream()
+ .map(IdConverter::toString)
+ .collect(Collectors.toList());
+ }
+
+ // ============ Validation Methods ============
+
+ /**
+ * Check if string is a valid Long ID.
+ *
+ * @param id String ID
+ * @return true if valid numeric ID, false otherwise
+ */
+ public static boolean isValidLongId(String id) {
+ if (id == null || id.trim().isEmpty()) {
+ return false;
+ }
+ try {
+ Long.parseLong(id.trim());
+ return true;
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Validate ID format, throw exception if invalid.
+ *
+ * @param id String ID
+ * @param fieldName field name for error message
+ * @throws ValidationException if ID is not null and format is invalid
+ */
+ public static void validateLongId(String id, String fieldName) {
+ if (id != null && !isValidLongId(id)) {
+ throw new ValidationException(
+ "Invalid " + fieldName + ": '" + id + "' is not a valid numeric ID");
+ }
+ }
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/configuration/ConfigurationOption.java b/core/src/main/java/com/alibaba/smart/framework/engine/configuration/ConfigurationOption.java
index 0266c3c75..ea07b4bfc 100644
--- a/core/src/main/java/com/alibaba/smart/framework/engine/configuration/ConfigurationOption.java
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/configuration/ConfigurationOption.java
@@ -24,6 +24,8 @@ public interface ConfigurationOption {
ConfigurationOption EAGER_FLUSH_ENABLED_OPTION = new EagerFlushEnabledOption();
+ ConfigurationOption SHARDING_MODE_ENABLED_OPTION = new ShardingModeEnabledOption();
+
boolean isEnabled();
String getId();
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/configuration/IdGenerator.java b/core/src/main/java/com/alibaba/smart/framework/engine/configuration/IdGenerator.java
index 26e68b715..4c24c309c 100644
--- a/core/src/main/java/com/alibaba/smart/framework/engine/configuration/IdGenerator.java
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/configuration/IdGenerator.java
@@ -1,5 +1,7 @@
package com.alibaba.smart.framework.engine.configuration;
+import java.util.concurrent.atomic.AtomicReference;
+
import com.alibaba.smart.framework.engine.model.instance.Instance;
/**
@@ -7,5 +9,36 @@
*/
public interface IdGenerator {
+ /**
+ * Global ID type configuration holder.
+ * Used by MyBatis TypeHandler to determine how to bind ID parameters.
+ */
+ AtomicReference> GLOBAL_ID_TYPE = new AtomicReference<>(Long.class);
+
void generate(Instance instance);
+
+ /**
+ * Returns the Java type of generated ID.
+ *
+ * @return Long.class for numeric IDs, String.class for UUID/string IDs
+ */
+ default Class> getIdType() {
+ return Long.class;
+ }
+
+ /**
+ * Configure global ID type based on this generator's ID type.
+ * Should be called during engine initialization.
+ */
+ default void configure() {
+ GLOBAL_ID_TYPE.set(getIdType());
+ }
+
+ /**
+ * Get the globally configured ID type.
+ * Used by MyBatis TypeHandler.
+ */
+ static Class> getGlobalIdType() {
+ return GLOBAL_ID_TYPE.get();
+ }
}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/configuration/ProcessEngineConfiguration.java b/core/src/main/java/com/alibaba/smart/framework/engine/configuration/ProcessEngineConfiguration.java
index 3a8503a3b..eab61430a 100644
--- a/core/src/main/java/com/alibaba/smart/framework/engine/configuration/ProcessEngineConfiguration.java
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/configuration/ProcessEngineConfiguration.java
@@ -7,6 +7,8 @@
import com.alibaba.smart.framework.engine.annotation.Experiment;
import com.alibaba.smart.framework.engine.common.expression.evaluator.ExpressionEvaluator;
import com.alibaba.smart.framework.engine.configuration.scanner.AnnotationScanner;
+import com.alibaba.smart.framework.engine.dialect.Dialect;
+import com.alibaba.smart.framework.engine.storage.StorageRouter;
/**
* @author 高海军 帝奇 2016.11.11
@@ -104,6 +106,18 @@ public interface ProcessEngineConfiguration {
TaskAssigneeDispatcher getTaskAssigneeDispatcher();
+ /**
+ * Optional extension for task lifecycle events.
+ * When set, the engine will publish events (TASK_ASSIGNED, TASK_COMPLETED, etc.)
+ * at each task state change point.
+ *
+ * @param taskEventPublisher the publisher implementation
+ * @since 3.7.0
+ */
+ default void setTaskEventPublisher(TaskEventPublisher taskEventPublisher) {}
+
+ default TaskEventPublisher getTaskEventPublisher() { return null; }
+
/**
* 主要用于持久化变量数据。
* @param variablePersister
@@ -165,6 +179,12 @@ public interface ProcessEngineConfiguration {
void setPvmActivityTaskFactory(PvmActivityTaskFactory pvmActivityTaskFactory);
PvmActivityTaskFactory getPvmActivityTaskFactory();
+ default StorageRouter getStorageRouter() { return null; }
+ default void setStorageRouter(StorageRouter storageRouter) {}
+
+ default Dialect getDialect() { return null; }
+ default void setDialect(Dialect dialect) {}
+
// 是否要干掉 用于配置扩展,默认可以为空。设计目的是根据自己的业务需求,来自定义存储(该机制会绕过引擎自带的各种Storage机制,powerful and a little UnSafe)。。
//void setPersisterStrategy(PersisterStrategy persisterStrategy);
//
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/configuration/TaskEventPublisher.java b/core/src/main/java/com/alibaba/smart/framework/engine/configuration/TaskEventPublisher.java
new file mode 100644
index 000000000..a9053d6ba
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/configuration/TaskEventPublisher.java
@@ -0,0 +1,29 @@
+package com.alibaba.smart.framework.engine.configuration;
+
+import java.util.Map;
+
+import com.alibaba.smart.framework.engine.model.instance.TaskInstance;
+import com.alibaba.smart.framework.engine.pvm.event.EventConstant;
+
+/**
+ * SPI for publishing task lifecycle events.
+ * Platform implementations bridge these events to their own event bus (e.g. Spring ApplicationEvent).
+ *
+ * Similar to {@link TaskAssigneeDispatcher}, this is set on {@link ProcessEngineConfiguration}
+ * and invoked by the engine at task state change points.
+ *
+ * @since 3.7.0
+ */
+public interface TaskEventPublisher {
+
+ /**
+ * Publish a task lifecycle event.
+ *
+ * @param event the event type (TASK_ASSIGNED, TASK_COMPLETED, etc.)
+ * @param taskInstance the task instance involved
+ * @param tenantId tenant identifier
+ * @param extra additional context (e.g. assignees, fromUserId, toUserId, reason)
+ */
+ void publish(EventConstant event, TaskInstance taskInstance,
+ String tenantId, Map extra);
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/configuration/impl/DefaultProcessEngineConfiguration.java b/core/src/main/java/com/alibaba/smart/framework/engine/configuration/impl/DefaultProcessEngineConfiguration.java
index 53282110d..bbab4bd7a 100644
--- a/core/src/main/java/com/alibaba/smart/framework/engine/configuration/impl/DefaultProcessEngineConfiguration.java
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/configuration/impl/DefaultProcessEngineConfiguration.java
@@ -13,7 +13,9 @@
import com.alibaba.smart.framework.engine.configuration.impl.option.DefaultOptionContainer;
import com.alibaba.smart.framework.engine.configuration.scanner.AnnotationScanner;
import com.alibaba.smart.framework.engine.constant.SmartBase;
+import com.alibaba.smart.framework.engine.dialect.Dialect;
import com.alibaba.smart.framework.engine.extension.scanner.SimpleAnnotationScanner;
+import com.alibaba.smart.framework.engine.storage.StorageRouter;
import lombok.Data;
import org.slf4j.Logger;
@@ -48,6 +50,8 @@ public class DefaultProcessEngineConfiguration implements ProcessEngineConfigura
private TaskAssigneeDispatcher taskAssigneeDispatcher;
+ private TaskEventPublisher taskEventPublisher;
+
private VariablePersister variablePersister;
private MultiInstanceCounter multiInstanceCounter;
@@ -68,6 +72,10 @@ public class DefaultProcessEngineConfiguration implements ProcessEngineConfigura
private Map magicExtension;
+ private StorageRouter storageRouter;
+
+ private Dialect dialect;
+
public DefaultProcessEngineConfiguration() {
//说明:先默认设置一个id生成器,业务使用方可以根据自己的需要再覆盖掉这个值。
this.idGenerator = new DefaultIdGenerator();
@@ -87,6 +95,8 @@ public DefaultProcessEngineConfiguration() {
optionContainer.put(ConfigurationOption.EXPRESSION_COMPILE_RESULT_CACHED_OPTION);
optionContainer.put(ConfigurationOption.PROCESS_DEFINITION_MULTI_TENANT_SHARE_OPTION);
+ this.storageRouter = new StorageRouter(this);
+
buildDefaultSupportNameSpace();
}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/configuration/impl/DefaultSmartEngine.java b/core/src/main/java/com/alibaba/smart/framework/engine/configuration/impl/DefaultSmartEngine.java
index c4a0411bc..160689d90 100644
--- a/core/src/main/java/com/alibaba/smart/framework/engine/configuration/impl/DefaultSmartEngine.java
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/configuration/impl/DefaultSmartEngine.java
@@ -9,21 +9,44 @@
import com.alibaba.smart.framework.engine.configuration.scanner.ExtensionBindingResult;
import com.alibaba.smart.framework.engine.extension.annotation.ExtensionBinding;
import com.alibaba.smart.framework.engine.extension.constant.ExtensionConstant;
+import com.alibaba.smart.framework.engine.extension.scanner.SimpleAnnotationScanner;
import com.alibaba.smart.framework.engine.hook.LifeCycleHook;
+import com.alibaba.smart.framework.engine.instance.storage.ActivityInstanceStorage;
+import com.alibaba.smart.framework.engine.instance.storage.ExecutionInstanceStorage;
+import com.alibaba.smart.framework.engine.instance.storage.ProcessInstanceStorage;
+import com.alibaba.smart.framework.engine.instance.storage.TaskAssigneeStorage;
+import com.alibaba.smart.framework.engine.instance.storage.TaskInstanceStorage;
+import com.alibaba.smart.framework.engine.instance.storage.VariableInstanceStorage;
+import com.alibaba.smart.framework.engine.storage.StorageMode;
+import com.alibaba.smart.framework.engine.storage.StorageRouter;
import com.alibaba.smart.framework.engine.service.command.DeploymentCommandService;
import com.alibaba.smart.framework.engine.service.command.ExecutionCommandService;
+import com.alibaba.smart.framework.engine.service.command.NotificationCommandService;
import com.alibaba.smart.framework.engine.service.command.ProcessCommandService;
import com.alibaba.smart.framework.engine.service.command.RepositoryCommandService;
+import com.alibaba.smart.framework.engine.service.command.SupervisionCommandService;
import com.alibaba.smart.framework.engine.service.command.TaskCommandService;
import com.alibaba.smart.framework.engine.service.command.VariableCommandService;
import com.alibaba.smart.framework.engine.service.query.ActivityQueryService;
import com.alibaba.smart.framework.engine.service.query.DeploymentQueryService;
import com.alibaba.smart.framework.engine.service.query.ExecutionQueryService;
+import com.alibaba.smart.framework.engine.service.query.NotificationQueryService;
import com.alibaba.smart.framework.engine.service.query.ProcessQueryService;
import com.alibaba.smart.framework.engine.service.query.RepositoryQueryService;
+import com.alibaba.smart.framework.engine.service.query.SupervisionQueryService;
import com.alibaba.smart.framework.engine.service.query.TaskAssigneeQueryService;
import com.alibaba.smart.framework.engine.service.query.TaskQueryService;
import com.alibaba.smart.framework.engine.service.query.VariableQueryService;
+import com.alibaba.smart.framework.engine.query.DeploymentQuery;
+import com.alibaba.smart.framework.engine.query.NotificationQuery;
+import com.alibaba.smart.framework.engine.query.ProcessInstanceQuery;
+import com.alibaba.smart.framework.engine.query.SupervisionQuery;
+import com.alibaba.smart.framework.engine.query.TaskQuery;
+import com.alibaba.smart.framework.engine.query.impl.DeploymentQueryImpl;
+import com.alibaba.smart.framework.engine.query.impl.NotificationQueryImpl;
+import com.alibaba.smart.framework.engine.query.impl.ProcessInstanceQueryImpl;
+import com.alibaba.smart.framework.engine.query.impl.SupervisionQueryImpl;
+import com.alibaba.smart.framework.engine.query.impl.TaskQueryImpl;
import lombok.Getter;
import lombok.Setter;
@@ -44,15 +67,98 @@ public void init(ProcessEngineConfiguration processEngineConfiguration) {
this.setProcessEngineConfiguration(processEngineConfiguration);
processEngineConfiguration.setSmartEngine(this);
+ // Configure global ID type for MyBatis TypeHandler
+ if (processEngineConfiguration.getIdGenerator() != null) {
+ processEngineConfiguration.getIdGenerator().configure();
+ }
+
AnnotationScanner annotationScanner = processEngineConfiguration.getAnnotationScanner();
annotationScanner.scan(processEngineConfiguration, ExtensionBinding.class);
+ // Register storage implementations to StorageRouter
+ initializeStorageRouter(processEngineConfiguration, annotationScanner);
+
Map scanResult = annotationScanner.getScanResult();
lifeCycleStarted(scanResult);
}
+ @SuppressWarnings("unchecked")
+ private void initializeStorageRouter(ProcessEngineConfiguration config, AnnotationScanner scanner) {
+ StorageRouter storageRouter = config.getStorageRouter();
+ if (storageRouter == null) {
+ return;
+ }
+
+ Class>[] storageTypes = {
+ ProcessInstanceStorage.class,
+ ExecutionInstanceStorage.class,
+ ActivityInstanceStorage.class,
+ TaskInstanceStorage.class,
+ TaskAssigneeStorage.class,
+ VariableInstanceStorage.class
+ };
+
+ Map scanResult = scanner.getScanResult();
+
+ boolean hasCommon = false;
+ boolean hasCustom = false;
+
+ // Register "common" group storages as DATABASE mode
+ ExtensionBindingResult commonResult = scanResult.get(ExtensionConstant.COMMON);
+ if (commonResult != null) {
+ Map commonBindings = commonResult.getBindingMap();
+ for (Class> storageType : storageTypes) {
+ Object impl = commonBindings.get(storageType);
+ if (impl != null) {
+ storageRouter.registerStorage(StorageMode.DATABASE, (Class) storageType, impl);
+ hasCommon = true;
+ }
+ }
+ }
+
+ // Register "custom" group storages as CUSTOM mode
+ ExtensionBindingResult customResult = scanResult.get(ExtensionConstant.CUSTOM);
+ if (customResult != null) {
+ Map customBindings = customResult.getBindingMap();
+ for (Class> storageType : storageTypes) {
+ Object impl = customBindings.get(storageType);
+ if (impl != null) {
+ storageRouter.registerStorage(StorageMode.CUSTOM, (Class) storageType, impl);
+ hasCustom = true;
+ }
+ }
+ }
+
+ // When only one storage module is present, also register it as the default mode
+ // to ensure backward compatibility
+ if (hasCustom && !hasCommon) {
+ // Only custom module: also register as DATABASE (default mode) for compatibility
+ Map customBindings = customResult.getBindingMap();
+ for (Class> storageType : storageTypes) {
+ Object impl = customBindings.get(storageType);
+ if (impl != null) {
+ storageRouter.registerStorage(StorageMode.DATABASE, (Class) storageType, impl);
+ }
+ }
+ } else if (hasCommon && !hasCustom) {
+ // Only common module: also register as CUSTOM for completeness
+ Map commonBindings = commonResult.getBindingMap();
+ for (Class> storageType : storageTypes) {
+ Object impl = commonBindings.get(storageType);
+ if (impl != null) {
+ storageRouter.registerStorage(StorageMode.CUSTOM, (Class) storageType, impl);
+ }
+ }
+ }
+
+ // Set StorageRouter to Scanner for transparent proxy
+ if (scanner instanceof SimpleAnnotationScanner) {
+ ((SimpleAnnotationScanner) scanner).setStorageRouter(storageRouter);
+ }
+ }
+
protected void lifeCycleStarted(Map scanResult) {
for (Entry stringExtensionBindingResultEntry : scanResult.entrySet()) {
ExtensionBindingResult bindingResult = stringExtensionBindingResultEntry.getValue();
@@ -144,4 +250,51 @@ public TaskAssigneeQueryService getTaskAssigneeQueryService() {
return processEngineConfiguration.getAnnotationScanner().getExtensionPoint(ExtensionConstant.SERVICE,TaskAssigneeQueryService.class);
}
+ @Override
+ public SupervisionCommandService getSupervisionCommandService() {
+ return processEngineConfiguration.getAnnotationScanner().getExtensionPoint(ExtensionConstant.SERVICE, SupervisionCommandService.class);
+ }
+
+ @Override
+ public SupervisionQueryService getSupervisionQueryService() {
+ return processEngineConfiguration.getAnnotationScanner().getExtensionPoint(ExtensionConstant.SERVICE, SupervisionQueryService.class);
+ }
+
+ @Override
+ public NotificationCommandService getNotificationCommandService() {
+ return processEngineConfiguration.getAnnotationScanner().getExtensionPoint(ExtensionConstant.SERVICE, NotificationCommandService.class);
+ }
+
+ @Override
+ public NotificationQueryService getNotificationQueryService() {
+ return processEngineConfiguration.getAnnotationScanner().getExtensionPoint(ExtensionConstant.SERVICE, NotificationQueryService.class);
+ }
+
+ // ============ Fluent Query API ============
+
+ @Override
+ public TaskQuery createTaskQuery() {
+ return new TaskQueryImpl(processEngineConfiguration);
+ }
+
+ @Override
+ public ProcessInstanceQuery createProcessQuery() {
+ return new ProcessInstanceQueryImpl(processEngineConfiguration);
+ }
+
+ @Override
+ public SupervisionQuery createSupervisionQuery() {
+ return new SupervisionQueryImpl(processEngineConfiguration);
+ }
+
+ @Override
+ public NotificationQuery createNotificationQuery() {
+ return new NotificationQueryImpl(processEngineConfiguration);
+ }
+
+ @Override
+ public DeploymentQuery createDeploymentQuery() {
+ return new DeploymentQueryImpl(processEngineConfiguration);
+ }
+
}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/configuration/impl/SnowflakeIdGenerator.java b/core/src/main/java/com/alibaba/smart/framework/engine/configuration/impl/SnowflakeIdGenerator.java
new file mode 100644
index 000000000..e18462d0f
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/configuration/impl/SnowflakeIdGenerator.java
@@ -0,0 +1,189 @@
+package com.alibaba.smart.framework.engine.configuration.impl;
+
+import com.alibaba.smart.framework.engine.configuration.IdGenerator;
+import com.alibaba.smart.framework.engine.model.instance.Instance;
+
+/**
+ * Snowflake-based distributed ID generator.
+ *
+ * Bit layout (64 bits total):
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |0| timestamp (41 bits) |
+ * +-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | | nodeId(5) | sequence (17 bits)|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ *
+ *
+ * - 1 bit - sign (always 0, positive)
+ * - 41 bits - millisecond timestamp offset from custom epoch (supports ~69 years)
+ * - 5 bits - node ID (0-31, supports 32 nodes)
+ * - 17 bits - sequence number per millisecond (0-131071, supports 131072 IDs/ms/node)
+ *
+ *
+ * Custom epoch: 2024-01-01 00:00:00 UTC (1704067200000L)
+ *
+ *
Clock rollback protection: tolerates up to 5ms of clock drift by busy-waiting;
+ * throws {@link IllegalStateException} for larger rollbacks.
+ *
+ *
Thread safety: all mutable state is protected by {@code synchronized}.
+ */
+public class SnowflakeIdGenerator implements IdGenerator {
+
+ // Custom epoch: 2024-01-01 00:00:00 UTC
+ private static final long EPOCH = 1704067200000L;
+
+ // Bit lengths
+ private static final int NODE_ID_BITS = 5;
+ private static final int SEQUENCE_BITS = 17;
+
+ // Max values
+ private static final long MAX_NODE_ID = (1L << NODE_ID_BITS) - 1; // 31
+ private static final long MAX_SEQUENCE = (1L << SEQUENCE_BITS) - 1; // 131071
+
+ // Shift amounts
+ private static final int NODE_ID_SHIFT = SEQUENCE_BITS; // 17
+ private static final int TIMESTAMP_SHIFT = NODE_ID_BITS + SEQUENCE_BITS; // 22
+
+ // Maximum tolerable clock rollback in milliseconds
+ private static final long MAX_CLOCK_BACKWARD_MS = 5L;
+
+ private final long nodeId;
+ private long lastTimestamp = -1L;
+ private long sequence = 0L;
+
+ /**
+ * Create a SnowflakeIdGenerator with the specified node ID.
+ *
+ * @param nodeId node identifier, must be between 0 and 31 (inclusive)
+ * @throws IllegalArgumentException if nodeId is out of range
+ */
+ public SnowflakeIdGenerator(long nodeId) {
+ if (nodeId < 0 || nodeId > MAX_NODE_ID) {
+ throw new IllegalArgumentException(
+ "nodeId must be between 0 and " + MAX_NODE_ID + ", got: " + nodeId);
+ }
+ this.nodeId = nodeId;
+ }
+
+ /**
+ * Create a SnowflakeIdGenerator with default node ID 0.
+ */
+ public SnowflakeIdGenerator() {
+ this(0);
+ }
+
+ @Override
+ public Class> getIdType() {
+ return Long.class;
+ }
+
+ @Override
+ public void generate(Instance instance) {
+ long id = nextId();
+ instance.setInstanceId(String.valueOf(id));
+ }
+
+ /**
+ * Generate the next unique snowflake ID.
+ *
+ * @return a unique 64-bit snowflake ID
+ * @throws IllegalStateException if clock moved backward by more than 5ms
+ */
+ public synchronized long nextId() {
+ long currentTimestamp = currentTimeMillis();
+
+ if (currentTimestamp < lastTimestamp) {
+ long offset = lastTimestamp - currentTimestamp;
+ if (offset <= MAX_CLOCK_BACKWARD_MS) {
+ // Wait for the clock to catch up
+ try {
+ Thread.sleep(offset);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new IllegalStateException(
+ "Interrupted while waiting for clock to catch up", e);
+ }
+ currentTimestamp = currentTimeMillis();
+ if (currentTimestamp < lastTimestamp) {
+ throw new IllegalStateException(
+ "Clock moved backward by " + (lastTimestamp - currentTimestamp)
+ + "ms after waiting. Refusing to generate ID.");
+ }
+ } else {
+ throw new IllegalStateException(
+ "Clock moved backward by " + offset
+ + "ms (exceeds tolerance of " + MAX_CLOCK_BACKWARD_MS + "ms). "
+ + "Refusing to generate ID.");
+ }
+ }
+
+ if (currentTimestamp == lastTimestamp) {
+ sequence = (sequence + 1) & MAX_SEQUENCE;
+ if (sequence == 0) {
+ // Sequence exhausted for this millisecond, wait for next ms
+ currentTimestamp = waitForNextMillis(lastTimestamp);
+ }
+ } else {
+ sequence = 0;
+ }
+
+ lastTimestamp = currentTimestamp;
+
+ long timestampOffset = currentTimestamp - EPOCH;
+ return (timestampOffset << TIMESTAMP_SHIFT)
+ | (nodeId << NODE_ID_SHIFT)
+ | sequence;
+ }
+
+ /**
+ * Parse the timestamp (millis since Unix epoch) from a snowflake ID.
+ *
+ * @param id the snowflake ID
+ * @return the absolute timestamp in milliseconds (Unix epoch)
+ */
+ public static long parseTimestamp(long id) {
+ return (id >> TIMESTAMP_SHIFT) + EPOCH;
+ }
+
+ /**
+ * Parse the node ID from a snowflake ID.
+ *
+ * @param id the snowflake ID
+ * @return the node ID (0-31)
+ */
+ public static long parseNodeId(long id) {
+ return (id >> NODE_ID_SHIFT) & MAX_NODE_ID;
+ }
+
+ /**
+ * Parse the sequence number from a snowflake ID.
+ *
+ * @param id the snowflake ID
+ * @return the sequence number (0-131071)
+ */
+ public static long parseSequence(long id) {
+ return id & MAX_SEQUENCE;
+ }
+
+ /**
+ * Block until the system clock advances past the given timestamp.
+ */
+ private long waitForNextMillis(long lastTs) {
+ long ts = currentTimeMillis();
+ while (ts <= lastTs) {
+ ts = currentTimeMillis();
+ }
+ return ts;
+ }
+
+ /**
+ * Returns current time in milliseconds. Extracted for testability.
+ */
+ long currentTimeMillis() {
+ return System.currentTimeMillis();
+ }
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/configuration/impl/option/ShardingModeEnabledOption.java b/core/src/main/java/com/alibaba/smart/framework/engine/configuration/impl/option/ShardingModeEnabledOption.java
new file mode 100644
index 000000000..f26f60dbe
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/configuration/impl/option/ShardingModeEnabledOption.java
@@ -0,0 +1,26 @@
+package com.alibaba.smart.framework.engine.configuration.impl.option;
+
+import com.alibaba.smart.framework.engine.configuration.ConfigurationOption;
+
+/**
+ * Sharding mode configuration option.
+ * When enabled, index tables (se_user_task_index, se_user_notification_index) will be maintained,
+ * and queries without sharding key (processInstanceId) will throw ShardingKeyRequiredException.
+ */
+public class ShardingModeEnabledOption implements ConfigurationOption {
+
+ @Override
+ public boolean isEnabled() {
+ return true;
+ }
+
+ @Override
+ public String getId() {
+ return "shardingModeEnabled";
+ }
+
+ @Override
+ public Object getData() {
+ return null;
+ }
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/constant/NotificationConstant.java b/core/src/main/java/com/alibaba/smart/framework/engine/constant/NotificationConstant.java
new file mode 100644
index 000000000..ee293f2a0
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/constant/NotificationConstant.java
@@ -0,0 +1,25 @@
+package com.alibaba.smart.framework.engine.constant;
+
+/**
+ * 知会通知相关常量
+ *
+ * @author SmartEngine Team
+ */
+public interface NotificationConstant {
+
+ /**
+ * 通知类型
+ */
+ interface NotificationType {
+ String CC = "cc"; // 抄送
+ String INFORM = "inform"; // 知会
+ }
+
+ /**
+ * 读取状态
+ */
+ interface ReadStatus {
+ String UNREAD = "unread"; // 未读
+ String READ = "read"; // 已读
+ }
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/constant/SupervisionConstant.java b/core/src/main/java/com/alibaba/smart/framework/engine/constant/SupervisionConstant.java
new file mode 100644
index 000000000..6748f3bf0
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/constant/SupervisionConstant.java
@@ -0,0 +1,26 @@
+package com.alibaba.smart.framework.engine.constant;
+
+/**
+ * 督办相关常量
+ *
+ * @author SmartEngine Team
+ */
+public interface SupervisionConstant {
+
+ /**
+ * 督办类型
+ */
+ interface SupervisionType {
+ String URGE = "urge"; // 催办
+ String TRACK = "track"; // 跟踪
+ String REMIND = "remind"; // 提醒
+ }
+
+ /**
+ * 督办状态
+ */
+ interface SupervisionStatus {
+ String ACTIVE = "active"; // 活跃
+ String CLOSED = "closed"; // 已关闭
+ }
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/context/ExecutionContext.java b/core/src/main/java/com/alibaba/smart/framework/engine/context/ExecutionContext.java
index b9c6c9762..9294da5ac 100644
--- a/core/src/main/java/com/alibaba/smart/framework/engine/context/ExecutionContext.java
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/context/ExecutionContext.java
@@ -8,6 +8,7 @@
import com.alibaba.smart.framework.engine.model.instance.ActivityInstance;
import com.alibaba.smart.framework.engine.model.instance.ExecutionInstance;
import com.alibaba.smart.framework.engine.model.instance.ProcessInstance;
+import com.alibaba.smart.framework.engine.model.instance.TaskInstance;
public interface ExecutionContext {
@@ -31,6 +32,10 @@ public interface ExecutionContext {
void setActivityInstance(ActivityInstance activityInstance);
+ TaskInstance getTaskInstance();
+
+ void setTaskInstance(TaskInstance taskInstance);
+
/* END 1 */
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/context/impl/DefaultExecutionContext.java b/core/src/main/java/com/alibaba/smart/framework/engine/context/impl/DefaultExecutionContext.java
index 5ba3c094f..425dcf8a8 100644
--- a/core/src/main/java/com/alibaba/smart/framework/engine/context/impl/DefaultExecutionContext.java
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/context/impl/DefaultExecutionContext.java
@@ -10,6 +10,7 @@
import com.alibaba.smart.framework.engine.model.instance.ActivityInstance;
import com.alibaba.smart.framework.engine.model.instance.ExecutionInstance;
import com.alibaba.smart.framework.engine.model.instance.ProcessInstance;
+import com.alibaba.smart.framework.engine.model.instance.TaskInstance;
import com.alibaba.smart.framework.engine.util.ObjectUtil;
import lombok.Data;
@@ -30,6 +31,8 @@ public class DefaultExecutionContext implements ExecutionContext {
private ActivityInstance activityInstance;
+ private TaskInstance taskInstance;
+
private ProcessDefinition processDefinition;
private ProcessEngineConfiguration processEngineConfiguration;
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/dialect/Dialect.java b/core/src/main/java/com/alibaba/smart/framework/engine/dialect/Dialect.java
new file mode 100644
index 000000000..6955407ca
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/dialect/Dialect.java
@@ -0,0 +1,213 @@
+package com.alibaba.smart.framework.engine.dialect;
+
+/**
+ * Database dialect interface for handling database-specific SQL syntax.
+ * Implementations provide database-specific SQL generation for pagination,
+ * ID generation, data types, and other database-specific features.
+ *
+ * @author SmartEngine Team
+ */
+public interface Dialect {
+
+ /**
+ * Get the dialect name (e.g., "mysql", "postgresql", "oracle").
+ *
+ * @return the dialect name
+ */
+ String getName();
+
+ /**
+ * Get the display name for this dialect.
+ *
+ * @return the display name
+ */
+ String getDisplayName();
+
+ // ============ Pagination ============
+
+ /**
+ * Build a paginated SQL statement.
+ *
+ * @param sql the original SQL
+ * @param offset the offset (0-based)
+ * @param limit the maximum number of rows to return
+ * @return the paginated SQL
+ */
+ String buildPageSql(String sql, int offset, int limit);
+
+ /**
+ * Check if this dialect supports LIMIT OFFSET syntax.
+ *
+ * @return true if LIMIT OFFSET is supported
+ */
+ boolean supportsLimitOffset();
+
+ // ============ Time functions ============
+
+ /**
+ * Get the SQL expression for current timestamp.
+ *
+ * @return the current timestamp expression
+ */
+ String getCurrentTimestamp();
+
+ /**
+ * Get the SQL expression for date difference calculation.
+ *
+ * @param startColumn the start date column
+ * @param endColumn the end date column
+ * @return the date difference expression (result in days)
+ */
+ String getDateDiff(String startColumn, String endColumn);
+
+ // ============ Data types ============
+
+ /**
+ * Get the BIGINT type name for this database.
+ *
+ * @return the BIGINT type name
+ */
+ String getBigintType();
+
+ /**
+ * Get the VARCHAR type name for this database.
+ *
+ * @param length the varchar length
+ * @return the VARCHAR type definition
+ */
+ String getVarcharType(int length);
+
+ /**
+ * Get the CLOB/TEXT type name for this database.
+ *
+ * @return the CLOB type name
+ */
+ String getClobType();
+
+ /**
+ * Get the TIMESTAMP type name for this database.
+ *
+ * @return the TIMESTAMP type name
+ */
+ String getTimestampType();
+
+ // ============ ID generation ============
+
+ /**
+ * Get the ID generation type for this database.
+ *
+ * @return the ID generation type
+ */
+ IdGenerationType getIdGenerationType();
+
+ /**
+ * Get the SQL for getting next sequence value (for SEQUENCE type databases).
+ *
+ * @param sequenceName the sequence name
+ * @return the SQL expression, or null if sequences are not supported
+ */
+ String getSequenceNextValueSql(String sequenceName);
+
+ /**
+ * Get the auto-increment column definition.
+ *
+ * @return the auto-increment definition, or null if not supported
+ */
+ String getAutoIncrementDefinition();
+
+ // ============ Boolean values ============
+
+ /**
+ * Get the SQL literal for boolean true.
+ *
+ * @return the true literal
+ */
+ String getBooleanTrue();
+
+ /**
+ * Get the SQL literal for boolean false.
+ *
+ * @return the false literal
+ */
+ String getBooleanFalse();
+
+ // ============ String functions ============
+
+ /**
+ * Get the string concatenation operator or function.
+ *
+ * @param expressions the expressions to concatenate
+ * @return the concatenation expression
+ */
+ String concat(String... expressions);
+
+ /**
+ * Get the substring function.
+ *
+ * @param column the column or expression
+ * @param start the start position (1-based)
+ * @param length the length
+ * @return the substring expression
+ */
+ String substring(String column, int start, int length);
+
+ // ============ Locking ============
+
+ /**
+ * Get the FOR UPDATE clause.
+ *
+ * @return the FOR UPDATE clause
+ */
+ String getForUpdateClause();
+
+ /**
+ * Get the FOR UPDATE NOWAIT clause.
+ *
+ * @return the FOR UPDATE NOWAIT clause, or regular FOR UPDATE if not supported
+ */
+ String getForUpdateNoWaitClause();
+
+ // ============ Other ============
+
+ /**
+ * Check if this dialect supports the given feature.
+ *
+ * @param feature the feature to check
+ * @return true if the feature is supported
+ */
+ boolean supportsFeature(DialectFeature feature);
+
+ /**
+ * Quote an identifier (table name, column name, etc.).
+ *
+ * @param identifier the identifier to quote
+ * @return the quoted identifier
+ */
+ String quoteIdentifier(String identifier);
+
+ // ============ JSON functions ============
+
+ /**
+ * Generate SQL to extract text value from a JSON column by key.
+ *
+ * @param column the column reference (e.g., "task.extra")
+ * @param key single-level JSON key (e.g., "category")
+ * @return SQL expression evaluating to text value
+ */
+ String jsonExtractText(String column, String key);
+
+ /**
+ * Enumeration of database features that may or may not be supported.
+ */
+ enum DialectFeature {
+ LIMIT_OFFSET,
+ SEQUENCES,
+ WINDOW_FUNCTIONS,
+ COMMON_TABLE_EXPRESSIONS,
+ LATERAL_JOINS,
+ RETURNING_CLAUSE,
+ UPSERT,
+ JSON_FUNCTIONS,
+ ARRAY_TYPE
+ }
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/dialect/DialectRegistry.java b/core/src/main/java/com/alibaba/smart/framework/engine/dialect/DialectRegistry.java
new file mode 100644
index 000000000..c670b577e
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/dialect/DialectRegistry.java
@@ -0,0 +1,183 @@
+package com.alibaba.smart.framework.engine.dialect;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.alibaba.smart.framework.engine.dialect.impl.DmDialect;
+import com.alibaba.smart.framework.engine.dialect.impl.H2Dialect;
+import com.alibaba.smart.framework.engine.dialect.impl.KingbaseDialect;
+import com.alibaba.smart.framework.engine.dialect.impl.MySqlDialect;
+import com.alibaba.smart.framework.engine.dialect.impl.OceanBaseDialect;
+import com.alibaba.smart.framework.engine.dialect.impl.OracleDialect;
+import com.alibaba.smart.framework.engine.dialect.impl.PostgreSqlDialect;
+import com.alibaba.smart.framework.engine.dialect.impl.SqlServerDialect;
+
+/**
+ * Registry for database dialects.
+ * Provides dialect lookup by name and automatic detection from JDBC URLs.
+ *
+ * @author SmartEngine Team
+ */
+public class DialectRegistry {
+
+ private static final DialectRegistry INSTANCE = new DialectRegistry();
+
+ private final Map dialects = new ConcurrentHashMap<>();
+ private Dialect defaultDialect;
+
+ private DialectRegistry() {
+ // Register built-in dialects
+ registerBuiltInDialects();
+ }
+
+ private void registerBuiltInDialects() {
+ register(new MySqlDialect());
+ register(new PostgreSqlDialect());
+ register(new H2Dialect());
+ register(new OracleDialect());
+ register(new SqlServerDialect());
+ register(new DmDialect());
+ register(new KingbaseDialect());
+ register(new OceanBaseDialect());
+
+ // Set MySQL as default
+ defaultDialect = dialects.get("mysql");
+ }
+
+ /**
+ * Get the singleton instance.
+ *
+ * @return the registry instance
+ */
+ public static DialectRegistry getInstance() {
+ return INSTANCE;
+ }
+
+ /**
+ * Register a dialect.
+ *
+ * @param dialect the dialect to register
+ */
+ public void register(Dialect dialect) {
+ dialects.put(dialect.getName().toLowerCase(), dialect);
+ }
+
+ /**
+ * Get a dialect by name.
+ *
+ * @param name the dialect name (case-insensitive)
+ * @return the dialect, or null if not found
+ */
+ public Dialect getDialect(String name) {
+ if (name == null) {
+ return defaultDialect;
+ }
+ return dialects.get(name.toLowerCase());
+ }
+
+ /**
+ * Get a dialect by name, or throw if not found.
+ *
+ * @param name the dialect name
+ * @return the dialect
+ * @throws IllegalArgumentException if dialect not found
+ */
+ public Dialect getDialectOrThrow(String name) {
+ Dialect dialect = getDialect(name);
+ if (dialect == null) {
+ throw new IllegalArgumentException("Unknown dialect: " + name +
+ ". Available dialects: " + dialects.keySet());
+ }
+ return dialect;
+ }
+
+ /**
+ * Detect dialect from JDBC URL.
+ *
+ * @param jdbcUrl the JDBC URL
+ * @return the detected dialect, or null if not detected
+ */
+ public Dialect detectDialect(String jdbcUrl) {
+ if (jdbcUrl == null || jdbcUrl.isEmpty()) {
+ return null;
+ }
+
+ String lowerUrl = jdbcUrl.toLowerCase();
+
+ if (lowerUrl.contains(":mysql:") || lowerUrl.contains(":mariadb:")) {
+ return getDialect("mysql");
+ }
+ if (lowerUrl.contains(":postgresql:") || lowerUrl.contains(":pgsql:")) {
+ return getDialect("postgresql");
+ }
+ if (lowerUrl.contains(":h2:")) {
+ return getDialect("h2");
+ }
+ if (lowerUrl.contains(":oracle:")) {
+ return getDialect("oracle");
+ }
+ if (lowerUrl.contains(":sqlserver:") || lowerUrl.contains(":microsoft:")) {
+ return getDialect("sqlserver");
+ }
+ if (lowerUrl.contains(":dm:") || lowerUrl.contains(":dameng:")) {
+ return getDialect("dm");
+ }
+ if (lowerUrl.contains(":kingbase:")) {
+ return getDialect("kingbase");
+ }
+ if (lowerUrl.contains(":oceanbase:")) {
+ return getDialect("oceanbase");
+ }
+
+ return null;
+ }
+
+ /**
+ * Get the default dialect.
+ *
+ * @return the default dialect
+ */
+ public Dialect getDefaultDialect() {
+ return defaultDialect;
+ }
+
+ /**
+ * Set the default dialect.
+ *
+ * @param defaultDialect the default dialect
+ */
+ public void setDefaultDialect(Dialect defaultDialect) {
+ this.defaultDialect = defaultDialect;
+ }
+
+ /**
+ * Set the default dialect by name.
+ *
+ * @param dialectName the dialect name
+ * @throws IllegalArgumentException if dialect not found
+ */
+ public void setDefaultDialect(String dialectName) {
+ this.defaultDialect = getDialectOrThrow(dialectName);
+ }
+
+ /**
+ * Get all registered dialects.
+ *
+ * @return unmodifiable collection of dialects
+ */
+ public Collection getAllDialects() {
+ return Collections.unmodifiableCollection(dialects.values());
+ }
+
+ /**
+ * Check if a dialect is registered.
+ *
+ * @param name the dialect name
+ * @return true if registered
+ */
+ public boolean hasDialect(String name) {
+ return name != null && dialects.containsKey(name.toLowerCase());
+ }
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/dialect/IdGenerationType.java b/core/src/main/java/com/alibaba/smart/framework/engine/dialect/IdGenerationType.java
new file mode 100644
index 000000000..34bc3a6ee
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/dialect/IdGenerationType.java
@@ -0,0 +1,34 @@
+package com.alibaba.smart.framework.engine.dialect;
+
+/**
+ * Enumeration of ID generation strategies supported by different databases.
+ *
+ * @author SmartEngine Team
+ */
+public enum IdGenerationType {
+
+ /**
+ * Auto-increment column (MySQL, H2, SQLite, OceanBase)
+ */
+ AUTO_INCREMENT,
+
+ /**
+ * Identity column (PostgreSQL, SQL Server, 达梦, 人大金仓)
+ */
+ IDENTITY,
+
+ /**
+ * Sequence (Oracle)
+ */
+ SEQUENCE,
+
+ /**
+ * UUID generated by application
+ */
+ UUID,
+
+ /**
+ * Custom ID generator (e.g., Snowflake)
+ */
+ CUSTOM
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/dialect/impl/AbstractDialect.java b/core/src/main/java/com/alibaba/smart/framework/engine/dialect/impl/AbstractDialect.java
new file mode 100644
index 000000000..2a0ed99df
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/dialect/impl/AbstractDialect.java
@@ -0,0 +1,82 @@
+package com.alibaba.smart.framework.engine.dialect.impl;
+
+import com.alibaba.smart.framework.engine.dialect.Dialect;
+import com.alibaba.smart.framework.engine.dialect.IdGenerationType;
+
+/**
+ * Abstract base implementation of Dialect with common functionality.
+ *
+ * @author SmartEngine Team
+ */
+public abstract class AbstractDialect implements Dialect {
+
+ @Override
+ public String getBigintType() {
+ return "BIGINT";
+ }
+
+ @Override
+ public String getVarcharType(int length) {
+ return "VARCHAR(" + length + ")";
+ }
+
+ @Override
+ public String getTimestampType() {
+ return "TIMESTAMP";
+ }
+
+ @Override
+ public String getBooleanTrue() {
+ return "TRUE";
+ }
+
+ @Override
+ public String getBooleanFalse() {
+ return "FALSE";
+ }
+
+ @Override
+ public String concat(String... expressions) {
+ // Default: use CONCAT function
+ return "CONCAT(" + String.join(", ", expressions) + ")";
+ }
+
+ @Override
+ public String substring(String column, int start, int length) {
+ return "SUBSTRING(" + column + ", " + start + ", " + length + ")";
+ }
+
+ @Override
+ public String getForUpdateClause() {
+ return "FOR UPDATE";
+ }
+
+ @Override
+ public String getForUpdateNoWaitClause() {
+ return "FOR UPDATE NOWAIT";
+ }
+
+ @Override
+ public String getSequenceNextValueSql(String sequenceName) {
+ // Default: not supported
+ return null;
+ }
+
+ @Override
+ public String getAutoIncrementDefinition() {
+ // Default: not supported
+ return null;
+ }
+
+ @Override
+ public String quoteIdentifier(String identifier) {
+ // Default: use double quotes (ANSI SQL standard)
+ return "\"" + identifier + "\"";
+ }
+
+ @Override
+ public String jsonExtractText(String column, String key) {
+ // Default: Oracle/DM/SQL Server syntax
+ return "JSON_VALUE(" + column + ", '$." + key + "')";
+ }
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/dialect/impl/DmDialect.java b/core/src/main/java/com/alibaba/smart/framework/engine/dialect/impl/DmDialect.java
new file mode 100644
index 000000000..888a2c7a2
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/dialect/impl/DmDialect.java
@@ -0,0 +1,104 @@
+package com.alibaba.smart.framework.engine.dialect.impl;
+
+import com.alibaba.smart.framework.engine.dialect.IdGenerationType;
+
+/**
+ * 达梦 (DM) database dialect implementation.
+ * DM is a Chinese domestic database with high Oracle compatibility.
+ *
+ * @author SmartEngine Team
+ */
+public class DmDialect extends AbstractDialect {
+
+ @Override
+ public String getName() {
+ return "dm";
+ }
+
+ @Override
+ public String getDisplayName() {
+ return "达梦数据库 (DM)";
+ }
+
+ @Override
+ public String buildPageSql(String sql, int offset, int limit) {
+ // DM supports LIMIT OFFSET syntax
+ return sql + " LIMIT " + limit + " OFFSET " + offset;
+ }
+
+ @Override
+ public boolean supportsLimitOffset() {
+ return true;
+ }
+
+ @Override
+ public String getCurrentTimestamp() {
+ return "CURRENT_TIMESTAMP";
+ }
+
+ @Override
+ public String getDateDiff(String startColumn, String endColumn) {
+ return "DATEDIFF(DAY, " + startColumn + ", " + endColumn + ")";
+ }
+
+ @Override
+ public String getVarcharType(int length) {
+ return "VARCHAR2(" + length + ")";
+ }
+
+ @Override
+ public String getClobType() {
+ return "CLOB";
+ }
+
+ @Override
+ public String getTimestampType() {
+ return "TIMESTAMP";
+ }
+
+ @Override
+ public IdGenerationType getIdGenerationType() {
+ return IdGenerationType.IDENTITY;
+ }
+
+ @Override
+ public String getAutoIncrementDefinition() {
+ return "IDENTITY";
+ }
+
+ @Override
+ public String getSequenceNextValueSql(String sequenceName) {
+ return sequenceName + ".NEXTVAL";
+ }
+
+ @Override
+ public String concat(String... expressions) {
+ // DM supports || for concatenation (Oracle compatible)
+ return "(" + String.join(" || ", expressions) + ")";
+ }
+
+ @Override
+ public String substring(String column, int start, int length) {
+ return "SUBSTR(" + column + ", " + start + ", " + length + ")";
+ }
+
+ @Override
+ public boolean supportsFeature(DialectFeature feature) {
+ switch (feature) {
+ case LIMIT_OFFSET:
+ case SEQUENCES:
+ case WINDOW_FUNCTIONS:
+ case COMMON_TABLE_EXPRESSIONS:
+ return true;
+ case LATERAL_JOINS:
+ case JSON_FUNCTIONS:
+ return true;
+ case RETURNING_CLAUSE:
+ case ARRAY_TYPE:
+ case UPSERT:
+ return true;
+ default:
+ return false;
+ }
+ }
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/dialect/impl/H2Dialect.java b/core/src/main/java/com/alibaba/smart/framework/engine/dialect/impl/H2Dialect.java
new file mode 100644
index 000000000..aae13677e
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/dialect/impl/H2Dialect.java
@@ -0,0 +1,85 @@
+package com.alibaba.smart.framework.engine.dialect.impl;
+
+import com.alibaba.smart.framework.engine.dialect.IdGenerationType;
+
+/**
+ * H2 database dialect implementation.
+ *
+ * @author SmartEngine Team
+ */
+public class H2Dialect extends AbstractDialect {
+
+ @Override
+ public String getName() {
+ return "h2";
+ }
+
+ @Override
+ public String getDisplayName() {
+ return "H2 Database";
+ }
+
+ @Override
+ public String buildPageSql(String sql, int offset, int limit) {
+ return sql + " LIMIT " + limit + " OFFSET " + offset;
+ }
+
+ @Override
+ public boolean supportsLimitOffset() {
+ return true;
+ }
+
+ @Override
+ public String getCurrentTimestamp() {
+ return "CURRENT_TIMESTAMP";
+ }
+
+ @Override
+ public String getDateDiff(String startColumn, String endColumn) {
+ return "DATEDIFF('DAY', " + startColumn + ", " + endColumn + ")";
+ }
+
+ @Override
+ public String getClobType() {
+ return "CLOB";
+ }
+
+ @Override
+ public IdGenerationType getIdGenerationType() {
+ return IdGenerationType.AUTO_INCREMENT;
+ }
+
+ @Override
+ public String getAutoIncrementDefinition() {
+ return "AUTO_INCREMENT";
+ }
+
+ @Override
+ public String getSequenceNextValueSql(String sequenceName) {
+ return "NEXT VALUE FOR " + sequenceName;
+ }
+
+ @Override
+ public String jsonExtractText(String column, String key) {
+ return "JSON_VALUE(" + column + " FORMAT JSON, '$." + key + "')";
+ }
+
+ @Override
+ public boolean supportsFeature(DialectFeature feature) {
+ switch (feature) {
+ case LIMIT_OFFSET:
+ case SEQUENCES:
+ case WINDOW_FUNCTIONS:
+ case COMMON_TABLE_EXPRESSIONS:
+ return true;
+ case LATERAL_JOINS:
+ case RETURNING_CLAUSE:
+ case JSON_FUNCTIONS:
+ case ARRAY_TYPE:
+ case UPSERT:
+ return true; // H2 supports MERGE
+ default:
+ return false;
+ }
+ }
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/dialect/impl/KingbaseDialect.java b/core/src/main/java/com/alibaba/smart/framework/engine/dialect/impl/KingbaseDialect.java
new file mode 100644
index 000000000..45d434936
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/dialect/impl/KingbaseDialect.java
@@ -0,0 +1,96 @@
+package com.alibaba.smart.framework.engine.dialect.impl;
+
+import com.alibaba.smart.framework.engine.dialect.IdGenerationType;
+
+/**
+ * 人大金仓 (KingbaseES) database dialect implementation.
+ * KingbaseES is a Chinese domestic database with high PostgreSQL compatibility.
+ *
+ * @author SmartEngine Team
+ */
+public class KingbaseDialect extends AbstractDialect {
+
+ @Override
+ public String getName() {
+ return "kingbase";
+ }
+
+ @Override
+ public String getDisplayName() {
+ return "人大金仓 (KingbaseES)";
+ }
+
+ @Override
+ public String buildPageSql(String sql, int offset, int limit) {
+ // KingbaseES supports LIMIT OFFSET (PostgreSQL compatible)
+ return sql + " LIMIT " + limit + " OFFSET " + offset;
+ }
+
+ @Override
+ public boolean supportsLimitOffset() {
+ return true;
+ }
+
+ @Override
+ public String getCurrentTimestamp() {
+ return "CURRENT_TIMESTAMP";
+ }
+
+ @Override
+ public String getDateDiff(String startColumn, String endColumn) {
+ // PostgreSQL compatible
+ return "(" + endColumn + " - " + startColumn + ")";
+ }
+
+ @Override
+ public String getClobType() {
+ return "TEXT";
+ }
+
+ @Override
+ public IdGenerationType getIdGenerationType() {
+ return IdGenerationType.IDENTITY;
+ }
+
+ @Override
+ public String getAutoIncrementDefinition() {
+ return "GENERATED ALWAYS AS IDENTITY";
+ }
+
+ @Override
+ public String getSequenceNextValueSql(String sequenceName) {
+ return "nextval('" + sequenceName + "')";
+ }
+
+ @Override
+ public String concat(String... expressions) {
+ // PostgreSQL compatible: use || operator
+ return "(" + String.join(" || ", expressions) + ")";
+ }
+
+ @Override
+ public String jsonExtractText(String column, String key) {
+ // PostgreSQL compatible
+ return "(" + column + "->>'" + key + "')";
+ }
+
+ @Override
+ public boolean supportsFeature(DialectFeature feature) {
+ switch (feature) {
+ case LIMIT_OFFSET:
+ case SEQUENCES:
+ case WINDOW_FUNCTIONS:
+ case COMMON_TABLE_EXPRESSIONS:
+ case LATERAL_JOINS:
+ return true;
+ case RETURNING_CLAUSE:
+ case JSON_FUNCTIONS:
+ case ARRAY_TYPE:
+ return true;
+ case UPSERT:
+ return true; // ON CONFLICT
+ default:
+ return false;
+ }
+ }
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/dialect/impl/MySqlDialect.java b/core/src/main/java/com/alibaba/smart/framework/engine/dialect/impl/MySqlDialect.java
new file mode 100644
index 000000000..a771d15ea
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/dialect/impl/MySqlDialect.java
@@ -0,0 +1,86 @@
+package com.alibaba.smart.framework.engine.dialect.impl;
+
+import com.alibaba.smart.framework.engine.dialect.IdGenerationType;
+
+/**
+ * MySQL dialect implementation.
+ *
+ * @author SmartEngine Team
+ */
+public class MySqlDialect extends AbstractDialect {
+
+ @Override
+ public String getName() {
+ return "mysql";
+ }
+
+ @Override
+ public String getDisplayName() {
+ return "MySQL";
+ }
+
+ @Override
+ public String buildPageSql(String sql, int offset, int limit) {
+ return sql + " LIMIT " + limit + " OFFSET " + offset;
+ }
+
+ @Override
+ public boolean supportsLimitOffset() {
+ return true;
+ }
+
+ @Override
+ public String getCurrentTimestamp() {
+ return "CURRENT_TIMESTAMP";
+ }
+
+ @Override
+ public String getDateDiff(String startColumn, String endColumn) {
+ return "DATEDIFF(" + endColumn + ", " + startColumn + ")";
+ }
+
+ @Override
+ public String getClobType() {
+ return "TEXT";
+ }
+
+ @Override
+ public IdGenerationType getIdGenerationType() {
+ return IdGenerationType.AUTO_INCREMENT;
+ }
+
+ @Override
+ public String getAutoIncrementDefinition() {
+ return "AUTO_INCREMENT";
+ }
+
+ @Override
+ public String quoteIdentifier(String identifier) {
+ return "`" + identifier + "`";
+ }
+
+ @Override
+ public String jsonExtractText(String column, String key) {
+ return "JSON_UNQUOTE(JSON_EXTRACT(" + column + ", '$." + key + "'))";
+ }
+
+ @Override
+ public boolean supportsFeature(DialectFeature feature) {
+ switch (feature) {
+ case LIMIT_OFFSET:
+ case WINDOW_FUNCTIONS:
+ case COMMON_TABLE_EXPRESSIONS:
+ case JSON_FUNCTIONS:
+ return true;
+ case SEQUENCES:
+ case LATERAL_JOINS:
+ case RETURNING_CLAUSE:
+ case ARRAY_TYPE:
+ return false;
+ case UPSERT:
+ return true; // ON DUPLICATE KEY UPDATE
+ default:
+ return false;
+ }
+ }
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/dialect/impl/OceanBaseDialect.java b/core/src/main/java/com/alibaba/smart/framework/engine/dialect/impl/OceanBaseDialect.java
new file mode 100644
index 000000000..4a5e95949
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/dialect/impl/OceanBaseDialect.java
@@ -0,0 +1,90 @@
+package com.alibaba.smart.framework.engine.dialect.impl;
+
+import com.alibaba.smart.framework.engine.dialect.IdGenerationType;
+
+/**
+ * OceanBase database dialect implementation.
+ * OceanBase is a distributed database with MySQL compatibility mode.
+ *
+ * @author SmartEngine Team
+ */
+public class OceanBaseDialect extends AbstractDialect {
+
+ @Override
+ public String getName() {
+ return "oceanbase";
+ }
+
+ @Override
+ public String getDisplayName() {
+ return "OceanBase";
+ }
+
+ @Override
+ public String buildPageSql(String sql, int offset, int limit) {
+ // OceanBase MySQL mode supports LIMIT OFFSET
+ return sql + " LIMIT " + limit + " OFFSET " + offset;
+ }
+
+ @Override
+ public boolean supportsLimitOffset() {
+ return true;
+ }
+
+ @Override
+ public String getCurrentTimestamp() {
+ return "CURRENT_TIMESTAMP";
+ }
+
+ @Override
+ public String getDateDiff(String startColumn, String endColumn) {
+ return "DATEDIFF(" + endColumn + ", " + startColumn + ")";
+ }
+
+ @Override
+ public String getClobType() {
+ return "LONGTEXT";
+ }
+
+ @Override
+ public IdGenerationType getIdGenerationType() {
+ return IdGenerationType.AUTO_INCREMENT;
+ }
+
+ @Override
+ public String getAutoIncrementDefinition() {
+ return "AUTO_INCREMENT";
+ }
+
+ @Override
+ public String quoteIdentifier(String identifier) {
+ return "`" + identifier + "`";
+ }
+
+ @Override
+ public String jsonExtractText(String column, String key) {
+ // MySQL compatible
+ return "JSON_UNQUOTE(JSON_EXTRACT(" + column + ", '$." + key + "'))";
+ }
+
+ @Override
+ public boolean supportsFeature(DialectFeature feature) {
+ switch (feature) {
+ case LIMIT_OFFSET:
+ case WINDOW_FUNCTIONS:
+ case COMMON_TABLE_EXPRESSIONS:
+ return true;
+ case SEQUENCES:
+ return false; // MySQL mode doesn't support sequences
+ case LATERAL_JOINS:
+ case RETURNING_CLAUSE:
+ case ARRAY_TYPE:
+ return false;
+ case JSON_FUNCTIONS:
+ case UPSERT:
+ return true;
+ default:
+ return false;
+ }
+ }
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/dialect/impl/OracleDialect.java b/core/src/main/java/com/alibaba/smart/framework/engine/dialect/impl/OracleDialect.java
new file mode 100644
index 000000000..0d5732337
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/dialect/impl/OracleDialect.java
@@ -0,0 +1,113 @@
+package com.alibaba.smart.framework.engine.dialect.impl;
+
+import com.alibaba.smart.framework.engine.dialect.IdGenerationType;
+
+/**
+ * Oracle database dialect implementation.
+ *
+ * @author SmartEngine Team
+ */
+public class OracleDialect extends AbstractDialect {
+
+ @Override
+ public String getName() {
+ return "oracle";
+ }
+
+ @Override
+ public String getDisplayName() {
+ return "Oracle Database";
+ }
+
+ @Override
+ public String buildPageSql(String sql, int offset, int limit) {
+ // Oracle 12c+ supports OFFSET FETCH
+ return sql + " OFFSET " + offset + " ROWS FETCH NEXT " + limit + " ROWS ONLY";
+ }
+
+ /**
+ * Build page SQL for older Oracle versions (before 12c).
+ *
+ * @param sql the original SQL
+ * @param offset the offset
+ * @param limit the limit
+ * @return the paginated SQL using ROWNUM
+ */
+ public String buildPageSqlLegacy(String sql, int offset, int limit) {
+ int endRow = offset + limit;
+ return "SELECT * FROM (SELECT t.*, ROWNUM rn FROM (" + sql + ") t WHERE ROWNUM <= " + endRow +
+ ") WHERE rn > " + offset;
+ }
+
+ @Override
+ public boolean supportsLimitOffset() {
+ return true; // Oracle 12c+
+ }
+
+ @Override
+ public String getCurrentTimestamp() {
+ return "SYSTIMESTAMP";
+ }
+
+ @Override
+ public String getDateDiff(String startColumn, String endColumn) {
+ return "(" + endColumn + " - " + startColumn + ")";
+ }
+
+ @Override
+ public String getVarcharType(int length) {
+ return "VARCHAR2(" + length + ")";
+ }
+
+ @Override
+ public String getClobType() {
+ return "CLOB";
+ }
+
+ @Override
+ public String getTimestampType() {
+ return "TIMESTAMP(6)";
+ }
+
+ @Override
+ public IdGenerationType getIdGenerationType() {
+ return IdGenerationType.SEQUENCE;
+ }
+
+ @Override
+ public String getSequenceNextValueSql(String sequenceName) {
+ return sequenceName + ".NEXTVAL";
+ }
+
+ @Override
+ public String concat(String... expressions) {
+ // Oracle uses || for concatenation
+ return "(" + String.join(" || ", expressions) + ")";
+ }
+
+ @Override
+ public String substring(String column, int start, int length) {
+ return "SUBSTR(" + column + ", " + start + ", " + length + ")";
+ }
+
+ @Override
+ public boolean supportsFeature(DialectFeature feature) {
+ switch (feature) {
+ case SEQUENCES:
+ case WINDOW_FUNCTIONS:
+ case COMMON_TABLE_EXPRESSIONS:
+ case LATERAL_JOINS:
+ return true;
+ case LIMIT_OFFSET:
+ return true; // Oracle 12c+
+ case RETURNING_CLAUSE:
+ case JSON_FUNCTIONS:
+ return true;
+ case ARRAY_TYPE:
+ case UPSERT:
+ return true; // MERGE statement
+ default:
+ return false;
+ }
+ }
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/dialect/impl/PostgreSqlDialect.java b/core/src/main/java/com/alibaba/smart/framework/engine/dialect/impl/PostgreSqlDialect.java
new file mode 100644
index 000000000..e8464bfdd
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/dialect/impl/PostgreSqlDialect.java
@@ -0,0 +1,92 @@
+package com.alibaba.smart.framework.engine.dialect.impl;
+
+import com.alibaba.smart.framework.engine.dialect.IdGenerationType;
+
+/**
+ * PostgreSQL dialect implementation.
+ *
+ * @author SmartEngine Team
+ */
+public class PostgreSqlDialect extends AbstractDialect {
+
+ @Override
+ public String getName() {
+ return "postgresql";
+ }
+
+ @Override
+ public String getDisplayName() {
+ return "PostgreSQL";
+ }
+
+ @Override
+ public String buildPageSql(String sql, int offset, int limit) {
+ return sql + " LIMIT " + limit + " OFFSET " + offset;
+ }
+
+ @Override
+ public boolean supportsLimitOffset() {
+ return true;
+ }
+
+ @Override
+ public String getCurrentTimestamp() {
+ return "CURRENT_TIMESTAMP";
+ }
+
+ @Override
+ public String getDateDiff(String startColumn, String endColumn) {
+ return "(" + endColumn + " - " + startColumn + ")";
+ }
+
+ @Override
+ public String getClobType() {
+ return "TEXT";
+ }
+
+ @Override
+ public IdGenerationType getIdGenerationType() {
+ return IdGenerationType.IDENTITY;
+ }
+
+ @Override
+ public String getAutoIncrementDefinition() {
+ // PostgreSQL uses SERIAL or GENERATED ... AS IDENTITY
+ return "GENERATED ALWAYS AS IDENTITY";
+ }
+
+ @Override
+ public String getSequenceNextValueSql(String sequenceName) {
+ return "nextval('" + sequenceName + "')";
+ }
+
+ @Override
+ public String concat(String... expressions) {
+ // PostgreSQL supports || operator for concatenation
+ return "(" + String.join(" || ", expressions) + ")";
+ }
+
+ @Override
+ public String jsonExtractText(String column, String key) {
+ return "(" + column + "->>'" + key + "')";
+ }
+
+ @Override
+ public boolean supportsFeature(DialectFeature feature) {
+ switch (feature) {
+ case LIMIT_OFFSET:
+ case SEQUENCES:
+ case WINDOW_FUNCTIONS:
+ case COMMON_TABLE_EXPRESSIONS:
+ case LATERAL_JOINS:
+ case RETURNING_CLAUSE:
+ case JSON_FUNCTIONS:
+ case ARRAY_TYPE:
+ return true;
+ case UPSERT:
+ return true; // ON CONFLICT DO UPDATE
+ default:
+ return false;
+ }
+ }
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/dialect/impl/SqlServerDialect.java b/core/src/main/java/com/alibaba/smart/framework/engine/dialect/impl/SqlServerDialect.java
new file mode 100644
index 000000000..f12529d7d
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/dialect/impl/SqlServerDialect.java
@@ -0,0 +1,105 @@
+package com.alibaba.smart.framework.engine.dialect.impl;
+
+import com.alibaba.smart.framework.engine.dialect.IdGenerationType;
+
+/**
+ * Microsoft SQL Server dialect implementation.
+ *
+ * @author SmartEngine Team
+ */
+public class SqlServerDialect extends AbstractDialect {
+
+ @Override
+ public String getName() {
+ return "sqlserver";
+ }
+
+ @Override
+ public String getDisplayName() {
+ return "Microsoft SQL Server";
+ }
+
+ @Override
+ public String buildPageSql(String sql, int offset, int limit) {
+ // SQL Server 2012+ supports OFFSET FETCH
+ // Requires ORDER BY clause
+ return sql + " OFFSET " + offset + " ROWS FETCH NEXT " + limit + " ROWS ONLY";
+ }
+
+ @Override
+ public boolean supportsLimitOffset() {
+ return true; // SQL Server 2012+
+ }
+
+ @Override
+ public String getCurrentTimestamp() {
+ return "GETDATE()";
+ }
+
+ @Override
+ public String getDateDiff(String startColumn, String endColumn) {
+ return "DATEDIFF(DAY, " + startColumn + ", " + endColumn + ")";
+ }
+
+ @Override
+ public String getVarcharType(int length) {
+ return "NVARCHAR(" + length + ")";
+ }
+
+ @Override
+ public String getClobType() {
+ return "NVARCHAR(MAX)";
+ }
+
+ @Override
+ public String getTimestampType() {
+ return "DATETIME2";
+ }
+
+ @Override
+ public IdGenerationType getIdGenerationType() {
+ return IdGenerationType.IDENTITY;
+ }
+
+ @Override
+ public String getAutoIncrementDefinition() {
+ return "IDENTITY(1,1)";
+ }
+
+ @Override
+ public String concat(String... expressions) {
+ return "CONCAT(" + String.join(", ", expressions) + ")";
+ }
+
+ @Override
+ public String quoteIdentifier(String identifier) {
+ return "[" + identifier + "]";
+ }
+
+ @Override
+ public String getForUpdateNoWaitClause() {
+ return "WITH (UPDLOCK, NOWAIT)";
+ }
+
+ @Override
+ public boolean supportsFeature(DialectFeature feature) {
+ switch (feature) {
+ case WINDOW_FUNCTIONS:
+ case COMMON_TABLE_EXPRESSIONS:
+ case JSON_FUNCTIONS:
+ return true;
+ case LIMIT_OFFSET:
+ return true; // SQL Server 2012+
+ case SEQUENCES:
+ return true; // SQL Server 2012+
+ case LATERAL_JOINS:
+ case RETURNING_CLAUSE:
+ case ARRAY_TYPE:
+ return false;
+ case UPSERT:
+ return true; // MERGE statement
+ default:
+ return false;
+ }
+ }
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/exception/NotificationException.java b/core/src/main/java/com/alibaba/smart/framework/engine/exception/NotificationException.java
new file mode 100644
index 000000000..b2dd94e07
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/exception/NotificationException.java
@@ -0,0 +1,21 @@
+package com.alibaba.smart.framework.engine.exception;
+
+/**
+ * 知会通知相关异常
+ *
+ * @author SmartEngine Team
+ */
+public class NotificationException extends EngineException {
+
+ private static final long serialVersionUID = 1L;
+
+ public NotificationException(String message) {
+ super(message);
+ }
+
+ public NotificationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/exception/RollbackException.java b/core/src/main/java/com/alibaba/smart/framework/engine/exception/RollbackException.java
new file mode 100644
index 000000000..40d44dbda
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/exception/RollbackException.java
@@ -0,0 +1,27 @@
+package com.alibaba.smart.framework.engine.exception;
+
+/**
+ * 流程回退相关异常
+ *
+ * @author SmartEngine Team
+ */
+public class RollbackException extends EngineException {
+
+ private static final long serialVersionUID = -2066455487370828007L;
+
+ public RollbackException(String message) {
+ super(message);
+ }
+
+ public RollbackException(Exception e) {
+ super(e);
+ }
+
+ public RollbackException(String message, Exception e) {
+ super(message, e);
+ }
+
+ public RollbackException(String message, Throwable e) {
+ super(message, e);
+ }
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/exception/ShardingKeyRequiredException.java b/core/src/main/java/com/alibaba/smart/framework/engine/exception/ShardingKeyRequiredException.java
new file mode 100644
index 000000000..b108e2a42
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/exception/ShardingKeyRequiredException.java
@@ -0,0 +1,14 @@
+package com.alibaba.smart.framework.engine.exception;
+
+/**
+ * Thrown when a query that requires a sharding key (processInstanceId) is invoked
+ * without providing one in sharding mode.
+ */
+public class ShardingKeyRequiredException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ public ShardingKeyRequiredException(String message) {
+ super(message);
+ }
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/exception/SupervisionException.java b/core/src/main/java/com/alibaba/smart/framework/engine/exception/SupervisionException.java
new file mode 100644
index 000000000..5008003b9
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/exception/SupervisionException.java
@@ -0,0 +1,21 @@
+package com.alibaba.smart.framework.engine.exception;
+
+/**
+ * 督办相关异常
+ *
+ * @author SmartEngine Team
+ */
+public class SupervisionException extends EngineException {
+
+ private static final long serialVersionUID = 1L;
+
+ public SupervisionException(String message) {
+ super(message);
+ }
+
+ public SupervisionException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/extension/constant/ExtensionConstant.java b/core/src/main/java/com/alibaba/smart/framework/engine/extension/constant/ExtensionConstant.java
index e09e89541..111b16a1d 100644
--- a/core/src/main/java/com/alibaba/smart/framework/engine/extension/constant/ExtensionConstant.java
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/extension/constant/ExtensionConstant.java
@@ -6,6 +6,8 @@
public interface ExtensionConstant {
String COMMON = "common";
+ String CUSTOM = "custom";
+
String SERVICE = "SERVICE";
String ELEMENT_PARSER = "element-parser";
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/extension/scanner/SimpleAnnotationScanner.java b/core/src/main/java/com/alibaba/smart/framework/engine/extension/scanner/SimpleAnnotationScanner.java
index bd180d8dd..257b4093d 100644
--- a/core/src/main/java/com/alibaba/smart/framework/engine/extension/scanner/SimpleAnnotationScanner.java
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/extension/scanner/SimpleAnnotationScanner.java
@@ -4,6 +4,8 @@
import java.io.FileFilter;
import java.io.IOException;
import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Proxy;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
@@ -21,9 +23,12 @@
import com.alibaba.smart.framework.engine.configuration.scanner.ExtensionBindingResult;
import com.alibaba.smart.framework.engine.exception.EngineException;
import com.alibaba.smart.framework.engine.extension.annotation.ExtensionBinding;
+import com.alibaba.smart.framework.engine.extension.constant.ExtensionConstant;
+import com.alibaba.smart.framework.engine.storage.StorageRouter;
import com.alibaba.smart.framework.engine.util.ClassUtil;
import lombok.Getter;
+import lombok.Setter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -39,6 +44,9 @@ public class SimpleAnnotationScanner implements AnnotationScanner {
@Getter
private Map scanResult = new HashMap();
+ @Setter
+ private StorageRouter storageRouter;
+
private String[] packageNameList;
public SimpleAnnotationScanner(String... packageNameList) {this.packageNameList = packageNameList;}
@@ -249,11 +257,37 @@ else if(currentBindingAnnotation.priority() == exBindingAnnotation.priority()){
@Override
public T getExtensionPoint(String group, Class clazz) {
+ // For storage types registered in StorageRouter, return a dynamic proxy
+ // that routes based on current StorageMode at invocation time.
+ // This is critical because service classes cache the storage reference during init(),
+ // so a direct reference would be locked to a single mode.
+ if (storageRouter != null && ExtensionConstant.COMMON.equals(group)
+ && storageRouter.hasStorageType(clazz)) {
+ return createStorageProxy(clazz);
+ }
+
+ // Default behavior (unchanged)
ExtensionBindingResult extensionBindingResult = this.scanResult.get(group);
Map bindingMap = extensionBindingResult.getBindingMap();
return (T)bindingMap.get(clazz);
}
+ @SuppressWarnings("unchecked")
+ private T createStorageProxy(Class storageInterface) {
+ return (T) Proxy.newProxyInstance(
+ storageInterface.getClassLoader(),
+ new Class>[]{storageInterface},
+ (proxy, method, args) -> {
+ T realStorage = storageRouter.getStorage(storageInterface);
+ try {
+ return method.invoke(realStorage, args);
+ } catch (InvocationTargetException e) {
+ throw e.getCause();
+ }
+ }
+ );
+ }
+
@Override
public Object getObject(String group, Class clazz) {
return this.scanResult.get(group).getBindingMap().get(clazz);
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/instance/impl/DefaultAssigneeOperationRecord.java b/core/src/main/java/com/alibaba/smart/framework/engine/instance/impl/DefaultAssigneeOperationRecord.java
new file mode 100644
index 000000000..2d490a81a
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/instance/impl/DefaultAssigneeOperationRecord.java
@@ -0,0 +1,32 @@
+package com.alibaba.smart.framework.engine.instance.impl;
+
+import com.alibaba.smart.framework.engine.model.instance.AssigneeOperationRecord;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+/**
+ * 默认加签减签操作记录实现
+ *
+ * @author SmartEngine Team
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class DefaultAssigneeOperationRecord extends AbstractLifeCycleInstance implements AssigneeOperationRecord {
+
+ private static final long serialVersionUID = 1L;
+
+ private String processInstanceId;
+
+ private String taskInstanceId;
+
+ private String operationType;
+
+ private String operatorUserId;
+
+ private String targetUserId;
+
+ private String operationReason;
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/instance/impl/DefaultNotificationInstance.java b/core/src/main/java/com/alibaba/smart/framework/engine/instance/impl/DefaultNotificationInstance.java
new file mode 100644
index 000000000..27e7f1784
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/instance/impl/DefaultNotificationInstance.java
@@ -0,0 +1,40 @@
+package com.alibaba.smart.framework.engine.instance.impl;
+
+import java.util.Date;
+
+import com.alibaba.smart.framework.engine.model.instance.NotificationInstance;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+/**
+ * 默认知会通知实例实现
+ *
+ * @author SmartEngine Team
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class DefaultNotificationInstance extends AbstractLifeCycleInstance implements NotificationInstance {
+
+ private static final long serialVersionUID = 1L;
+
+ private String processInstanceId;
+
+ private String taskInstanceId;
+
+ private String senderUserId;
+
+ private String receiverUserId;
+
+ private String notificationType;
+
+ private String title;
+
+ private String content;
+
+ private String readStatus;
+
+ private Date readTime;
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/instance/impl/DefaultRollbackRecord.java b/core/src/main/java/com/alibaba/smart/framework/engine/instance/impl/DefaultRollbackRecord.java
new file mode 100644
index 000000000..b30fd3d94
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/instance/impl/DefaultRollbackRecord.java
@@ -0,0 +1,34 @@
+package com.alibaba.smart.framework.engine.instance.impl;
+
+import com.alibaba.smart.framework.engine.model.instance.RollbackRecord;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+/**
+ * 默认流程回退记录实现
+ *
+ * @author SmartEngine Team
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class DefaultRollbackRecord extends AbstractLifeCycleInstance implements RollbackRecord {
+
+ private static final long serialVersionUID = 1L;
+
+ private String processInstanceId;
+
+ private String taskInstanceId;
+
+ private String rollbackType;
+
+ private String fromActivityId;
+
+ private String toActivityId;
+
+ private String operatorUserId;
+
+ private String rollbackReason;
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/instance/impl/DefaultSupervisionInstance.java b/core/src/main/java/com/alibaba/smart/framework/engine/instance/impl/DefaultSupervisionInstance.java
new file mode 100644
index 000000000..e2b855007
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/instance/impl/DefaultSupervisionInstance.java
@@ -0,0 +1,36 @@
+package com.alibaba.smart.framework.engine.instance.impl;
+
+import java.util.Date;
+
+import com.alibaba.smart.framework.engine.model.instance.SupervisionInstance;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+/**
+ * 默认督办实例实现
+ *
+ * @author SmartEngine Team
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class DefaultSupervisionInstance extends AbstractLifeCycleInstance implements SupervisionInstance {
+
+ private static final long serialVersionUID = 1L;
+
+ private String processInstanceId;
+
+ private String taskInstanceId;
+
+ private String supervisorUserId;
+
+ private String supervisionReason;
+
+ private String supervisionType;
+
+ private String status;
+
+ private Date closeTime;
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/instance/impl/DefaultTaskInstance.java b/core/src/main/java/com/alibaba/smart/framework/engine/instance/impl/DefaultTaskInstance.java
index c7c447c44..32f2ac88c 100644
--- a/core/src/main/java/com/alibaba/smart/framework/engine/instance/impl/DefaultTaskInstance.java
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/instance/impl/DefaultTaskInstance.java
@@ -53,6 +53,10 @@ public class DefaultTaskInstance extends AbstractLifeCycleInstance implements Ta
*/
private String extension;
+ private String domainCode;
+
+ private String extra;
+
/**
* 任务标题
*/
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/instance/impl/DefaultTaskTransferRecord.java b/core/src/main/java/com/alibaba/smart/framework/engine/instance/impl/DefaultTaskTransferRecord.java
new file mode 100644
index 000000000..ba07cd1e8
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/instance/impl/DefaultTaskTransferRecord.java
@@ -0,0 +1,34 @@
+package com.alibaba.smart.framework.engine.instance.impl;
+
+import java.util.Date;
+
+import com.alibaba.smart.framework.engine.model.instance.TaskTransferRecord;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+/**
+ * 默认任务移交记录实现
+ *
+ * @author SmartEngine Team
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class DefaultTaskTransferRecord extends AbstractLifeCycleInstance implements TaskTransferRecord {
+
+ private static final long serialVersionUID = 1L;
+
+ private String processInstanceId;
+
+ private String taskInstanceId;
+
+ private String fromUserId;
+
+ private String toUserId;
+
+ private String transferReason;
+
+ private Date deadline;
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/instance/storage/AssigneeOperationRecordStorage.java b/core/src/main/java/com/alibaba/smart/framework/engine/instance/storage/AssigneeOperationRecordStorage.java
new file mode 100644
index 000000000..e83f21999
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/instance/storage/AssigneeOperationRecordStorage.java
@@ -0,0 +1,44 @@
+package com.alibaba.smart.framework.engine.instance.storage;
+
+import com.alibaba.smart.framework.engine.configuration.ProcessEngineConfiguration;
+import com.alibaba.smart.framework.engine.model.instance.AssigneeOperationRecord;
+
+import java.util.List;
+
+/**
+ * 加签减签操作记录存储接口
+ *
+ * @author SmartEngine Team
+ */
+public interface AssigneeOperationRecordStorage {
+
+ /**
+ * 插入加签减签操作记录
+ */
+ AssigneeOperationRecord insert(AssigneeOperationRecord assigneeOperationRecord,
+ ProcessEngineConfiguration processEngineConfiguration);
+
+ /**
+ * 根据任务ID查询操作记录列表
+ */
+ List findByTaskId(Long taskInstanceId, String tenantId,
+ ProcessEngineConfiguration processEngineConfiguration);
+
+ /**
+ * 根据ID查询操作记录
+ */
+ AssigneeOperationRecord find(Long id, String tenantId,
+ ProcessEngineConfiguration processEngineConfiguration);
+
+ /**
+ * 更新操作记录
+ */
+ AssigneeOperationRecord update(AssigneeOperationRecord assigneeOperationRecord,
+ ProcessEngineConfiguration processEngineConfiguration);
+
+ /**
+ * 删除操作记录
+ */
+ void remove(Long id, String tenantId,
+ ProcessEngineConfiguration processEngineConfiguration);
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/instance/storage/NotificationInstanceStorage.java b/core/src/main/java/com/alibaba/smart/framework/engine/instance/storage/NotificationInstanceStorage.java
new file mode 100644
index 000000000..9cf63e9ee
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/instance/storage/NotificationInstanceStorage.java
@@ -0,0 +1,83 @@
+package com.alibaba.smart.framework.engine.instance.storage;
+
+import java.util.List;
+
+import com.alibaba.smart.framework.engine.configuration.ProcessEngineConfiguration;
+import com.alibaba.smart.framework.engine.model.instance.NotificationInstance;
+import com.alibaba.smart.framework.engine.service.param.query.NotificationQueryParam;
+
+/**
+ * 知会通知实例存储接口
+ *
+ * @author SmartEngine Team
+ */
+public interface NotificationInstanceStorage {
+
+ /**
+ * 插入知会通知实例
+ */
+ NotificationInstance insert(NotificationInstance notificationInstance,
+ ProcessEngineConfiguration processEngineConfiguration);
+
+ /**
+ * 更新知会通知实例
+ */
+ NotificationInstance update(NotificationInstance notificationInstance,
+ ProcessEngineConfiguration processEngineConfiguration);
+
+ /**
+ * 根据ID查询知会通知实例
+ */
+ NotificationInstance find(String notificationId, String tenantId,
+ ProcessEngineConfiguration processEngineConfiguration);
+
+ /**
+ * 查询知会通知实例列表
+ */
+ List findNotificationList(NotificationQueryParam param,
+ ProcessEngineConfiguration processEngineConfiguration);
+
+ /**
+ * 统计知会通知实例数量
+ */
+ Long countNotifications(NotificationQueryParam param,
+ ProcessEngineConfiguration processEngineConfiguration);
+
+ /**
+ * 统计未读通知数量
+ */
+ Long countUnreadNotifications(String receiverUserId, String tenantId,
+ ProcessEngineConfiguration processEngineConfiguration);
+
+ /**
+ * 查询接收人的知会列表
+ */
+ List findByReceiver(String receiverUserId, String readStatus,
+ String tenantId, Integer pageOffset, Integer pageSize,
+ ProcessEngineConfiguration processEngineConfiguration);
+
+ /**
+ * 查询发送人的知会列表
+ */
+ List findBySender(String senderUserId, String tenantId,
+ Integer pageOffset, Integer pageSize,
+ ProcessEngineConfiguration processEngineConfiguration);
+
+ /**
+ * 标记为已读
+ */
+ int markAsRead(String notificationId, String tenantId,
+ ProcessEngineConfiguration processEngineConfiguration);
+
+ /**
+ * 批量标记为已读
+ */
+ int batchMarkAsRead(List notificationIds, String tenantId,
+ ProcessEngineConfiguration processEngineConfiguration);
+
+ /**
+ * 删除知会通知实例
+ */
+ void remove(String notificationId, String tenantId,
+ ProcessEngineConfiguration processEngineConfiguration);
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/instance/storage/RollbackRecordStorage.java b/core/src/main/java/com/alibaba/smart/framework/engine/instance/storage/RollbackRecordStorage.java
new file mode 100644
index 000000000..3bcc330e1
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/instance/storage/RollbackRecordStorage.java
@@ -0,0 +1,44 @@
+package com.alibaba.smart.framework.engine.instance.storage;
+
+import com.alibaba.smart.framework.engine.configuration.ProcessEngineConfiguration;
+import com.alibaba.smart.framework.engine.model.instance.RollbackRecord;
+
+import java.util.List;
+
+/**
+ * 流程回退记录存储接口
+ *
+ * @author SmartEngine Team
+ */
+public interface RollbackRecordStorage {
+
+ /**
+ * 插入回退记录
+ */
+ RollbackRecord insert(RollbackRecord rollbackRecord,
+ ProcessEngineConfiguration processEngineConfiguration);
+
+ /**
+ * 根据流程实例ID查询回退记录列表
+ */
+ List findByProcessInstanceId(Long processInstanceId, String tenantId,
+ ProcessEngineConfiguration processEngineConfiguration);
+
+ /**
+ * 根据ID查询回退记录
+ */
+ RollbackRecord find(Long id, String tenantId,
+ ProcessEngineConfiguration processEngineConfiguration);
+
+ /**
+ * 更新回退记录
+ */
+ RollbackRecord update(RollbackRecord rollbackRecord,
+ ProcessEngineConfiguration processEngineConfiguration);
+
+ /**
+ * 删除回退记录
+ */
+ void remove(Long id, String tenantId,
+ ProcessEngineConfiguration processEngineConfiguration);
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/instance/storage/SupervisionInstanceStorage.java b/core/src/main/java/com/alibaba/smart/framework/engine/instance/storage/SupervisionInstanceStorage.java
new file mode 100644
index 000000000..10e75397e
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/instance/storage/SupervisionInstanceStorage.java
@@ -0,0 +1,69 @@
+package com.alibaba.smart.framework.engine.instance.storage;
+
+import java.util.List;
+
+import com.alibaba.smart.framework.engine.configuration.ProcessEngineConfiguration;
+import com.alibaba.smart.framework.engine.model.instance.SupervisionInstance;
+import com.alibaba.smart.framework.engine.service.param.query.SupervisionQueryParam;
+
+/**
+ * 督办实例存储接口
+ *
+ * @author SmartEngine Team
+ */
+public interface SupervisionInstanceStorage {
+
+ /**
+ * 插入督办实例
+ */
+ SupervisionInstance insert(SupervisionInstance supervisionInstance,
+ ProcessEngineConfiguration processEngineConfiguration);
+
+ /**
+ * 更新督办实例
+ */
+ SupervisionInstance update(SupervisionInstance supervisionInstance,
+ ProcessEngineConfiguration processEngineConfiguration);
+
+ /**
+ * 根据ID查询督办实例
+ */
+ SupervisionInstance find(String supervisionId, String tenantId,
+ ProcessEngineConfiguration processEngineConfiguration);
+
+ /**
+ * 查询督办实例列表
+ */
+ List findSupervisionList(SupervisionQueryParam param,
+ ProcessEngineConfiguration processEngineConfiguration);
+
+ /**
+ * 统计督办实例数量
+ */
+ Long countSupervision(SupervisionQueryParam param,
+ ProcessEngineConfiguration processEngineConfiguration);
+
+ /**
+ * 查询任务的活跃督办记录
+ */
+ List findActiveSupervisionByTask(String taskInstanceId, String tenantId,
+ ProcessEngineConfiguration processEngineConfiguration);
+
+ /**
+ * 关闭督办记录
+ */
+ int closeSupervision(String supervisionId, String tenantId,
+ ProcessEngineConfiguration processEngineConfiguration);
+
+ /**
+ * 根据任务ID批量关闭督办记录
+ */
+ int closeSupervisionByTask(String taskInstanceId, String tenantId,
+ ProcessEngineConfiguration processEngineConfiguration);
+
+ /**
+ * 删除督办实例
+ */
+ void remove(String supervisionId, String tenantId,
+ ProcessEngineConfiguration processEngineConfiguration);
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/instance/storage/TaskTransferRecordStorage.java b/core/src/main/java/com/alibaba/smart/framework/engine/instance/storage/TaskTransferRecordStorage.java
new file mode 100644
index 000000000..433df6c72
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/instance/storage/TaskTransferRecordStorage.java
@@ -0,0 +1,44 @@
+package com.alibaba.smart.framework.engine.instance.storage;
+
+import com.alibaba.smart.framework.engine.configuration.ProcessEngineConfiguration;
+import com.alibaba.smart.framework.engine.model.instance.TaskTransferRecord;
+
+import java.util.List;
+
+/**
+ * 任务移交记录存储接口
+ *
+ * @author SmartEngine Team
+ */
+public interface TaskTransferRecordStorage {
+
+ /**
+ * 插入任务移交记录
+ */
+ TaskTransferRecord insert(TaskTransferRecord taskTransferRecord,
+ ProcessEngineConfiguration processEngineConfiguration);
+
+ /**
+ * 根据任务ID查询移交记录列表
+ */
+ List findByTaskId(Long taskInstanceId, String tenantId,
+ ProcessEngineConfiguration processEngineConfiguration);
+
+ /**
+ * 根据ID查询移交记录
+ */
+ TaskTransferRecord find(Long id, String tenantId,
+ ProcessEngineConfiguration processEngineConfiguration);
+
+ /**
+ * 更新移交记录
+ */
+ TaskTransferRecord update(TaskTransferRecord taskTransferRecord,
+ ProcessEngineConfiguration processEngineConfiguration);
+
+ /**
+ * 删除移交记录
+ */
+ void remove(Long id, String tenantId,
+ ProcessEngineConfiguration processEngineConfiguration);
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/model/instance/AssigneeOperationRecord.java b/core/src/main/java/com/alibaba/smart/framework/engine/model/instance/AssigneeOperationRecord.java
new file mode 100644
index 000000000..829d99be2
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/model/instance/AssigneeOperationRecord.java
@@ -0,0 +1,69 @@
+package com.alibaba.smart.framework.engine.model.instance;
+
+/**
+ * 加签减签操作记录接口
+ *
+ * @author SmartEngine Team
+ */
+public interface AssigneeOperationRecord extends LifeCycleInstance {
+
+ /**
+ * 获取流程实例ID
+ */
+ String getProcessInstanceId();
+
+ /**
+ * 设置流程实例ID
+ */
+ void setProcessInstanceId(String processInstanceId);
+
+ /**
+ * 获取任务实例ID
+ */
+ String getTaskInstanceId();
+
+ /**
+ * 设置任务实例ID
+ */
+ void setTaskInstanceId(String taskInstanceId);
+
+ /**
+ * 获取操作类型(add_assignee/remove_assignee)
+ */
+ String getOperationType();
+
+ /**
+ * 设置操作类型
+ */
+ void setOperationType(String operationType);
+
+ /**
+ * 获取操作人用户ID
+ */
+ String getOperatorUserId();
+
+ /**
+ * 设置操作人用户ID
+ */
+ void setOperatorUserId(String operatorUserId);
+
+ /**
+ * 获取目标用户ID
+ */
+ String getTargetUserId();
+
+ /**
+ * 设置目标用户ID
+ */
+ void setTargetUserId(String targetUserId);
+
+ /**
+ * 获取操作原因
+ */
+ String getOperationReason();
+
+ /**
+ * 设置操作原因
+ */
+ void setOperationReason(String operationReason);
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/model/instance/NotificationInstance.java b/core/src/main/java/com/alibaba/smart/framework/engine/model/instance/NotificationInstance.java
new file mode 100644
index 000000000..3c29b27cd
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/model/instance/NotificationInstance.java
@@ -0,0 +1,101 @@
+package com.alibaba.smart.framework.engine.model.instance;
+
+import java.util.Date;
+
+/**
+ * 知会通知实例接口
+ *
+ * @author SmartEngine Team
+ */
+public interface NotificationInstance extends LifeCycleInstance {
+
+ /**
+ * 获取流程实例ID
+ */
+ String getProcessInstanceId();
+
+ /**
+ * 设置流程实例ID
+ */
+ void setProcessInstanceId(String processInstanceId);
+
+ /**
+ * 获取任务实例ID
+ */
+ String getTaskInstanceId();
+
+ /**
+ * 设置任务实例ID
+ */
+ void setTaskInstanceId(String taskInstanceId);
+
+ /**
+ * 获取发送人用户ID
+ */
+ String getSenderUserId();
+
+ /**
+ * 设置发送人用户ID
+ */
+ void setSenderUserId(String senderUserId);
+
+ /**
+ * 获取接收人用户ID
+ */
+ String getReceiverUserId();
+
+ /**
+ * 设置接收人用户ID
+ */
+ void setReceiverUserId(String receiverUserId);
+
+ /**
+ * 获取通知类型
+ */
+ String getNotificationType();
+
+ /**
+ * 设置通知类型
+ */
+ void setNotificationType(String notificationType);
+
+ /**
+ * 获取通知标题
+ */
+ String getTitle();
+
+ /**
+ * 设置通知标题
+ */
+ void setTitle(String title);
+
+ /**
+ * 获取通知内容
+ */
+ String getContent();
+
+ /**
+ * 设置通知内容
+ */
+ void setContent(String content);
+
+ /**
+ * 获取读取状态
+ */
+ String getReadStatus();
+
+ /**
+ * 设置读取状态
+ */
+ void setReadStatus(String readStatus);
+
+ /**
+ * 获取读取时间
+ */
+ Date getReadTime();
+
+ /**
+ * 设置读取时间
+ */
+ void setReadTime(Date readTime);
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/model/instance/RollbackRecord.java b/core/src/main/java/com/alibaba/smart/framework/engine/model/instance/RollbackRecord.java
new file mode 100644
index 000000000..e22748f59
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/model/instance/RollbackRecord.java
@@ -0,0 +1,79 @@
+package com.alibaba.smart.framework.engine.model.instance;
+
+/**
+ * 流程回退记录接口
+ *
+ * @author SmartEngine Team
+ */
+public interface RollbackRecord extends LifeCycleInstance {
+
+ /**
+ * 获取流程实例ID
+ */
+ String getProcessInstanceId();
+
+ /**
+ * 设置流程实例ID
+ */
+ void setProcessInstanceId(String processInstanceId);
+
+ /**
+ * 获取任务实例ID
+ */
+ String getTaskInstanceId();
+
+ /**
+ * 设置任务实例ID
+ */
+ void setTaskInstanceId(String taskInstanceId);
+
+ /**
+ * 获取回退类型(specific/previous)
+ */
+ String getRollbackType();
+
+ /**
+ * 设置回退类型
+ */
+ void setRollbackType(String rollbackType);
+
+ /**
+ * 获取源活动ID
+ */
+ String getFromActivityId();
+
+ /**
+ * 设置源活动ID
+ */
+ void setFromActivityId(String fromActivityId);
+
+ /**
+ * 获取目标活动ID
+ */
+ String getToActivityId();
+
+ /**
+ * 设置目标活动ID
+ */
+ void setToActivityId(String toActivityId);
+
+ /**
+ * 获取操作人用户ID
+ */
+ String getOperatorUserId();
+
+ /**
+ * 设置操作人用户ID
+ */
+ void setOperatorUserId(String operatorUserId);
+
+ /**
+ * 获取回退原因
+ */
+ String getRollbackReason();
+
+ /**
+ * 设置回退原因
+ */
+ void setRollbackReason(String rollbackReason);
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/model/instance/SupervisionInstance.java b/core/src/main/java/com/alibaba/smart/framework/engine/model/instance/SupervisionInstance.java
new file mode 100644
index 000000000..134411637
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/model/instance/SupervisionInstance.java
@@ -0,0 +1,81 @@
+package com.alibaba.smart.framework.engine.model.instance;
+
+import java.util.Date;
+
+/**
+ * 督办实例接口
+ *
+ * @author SmartEngine Team
+ */
+public interface SupervisionInstance extends LifeCycleInstance {
+
+ /**
+ * 获取流程实例ID
+ */
+ String getProcessInstanceId();
+
+ /**
+ * 设置流程实例ID
+ */
+ void setProcessInstanceId(String processInstanceId);
+
+ /**
+ * 获取任务实例ID
+ */
+ String getTaskInstanceId();
+
+ /**
+ * 设置任务实例ID
+ */
+ void setTaskInstanceId(String taskInstanceId);
+
+ /**
+ * 获取督办人用户ID
+ */
+ String getSupervisorUserId();
+
+ /**
+ * 设置督办人用户ID
+ */
+ void setSupervisorUserId(String supervisorUserId);
+
+ /**
+ * 获取督办原因
+ */
+ String getSupervisionReason();
+
+ /**
+ * 设置督办原因
+ */
+ void setSupervisionReason(String supervisionReason);
+
+ /**
+ * 获取督办类型
+ */
+ String getSupervisionType();
+
+ /**
+ * 设置督办类型
+ */
+ void setSupervisionType(String supervisionType);
+
+ /**
+ * 获取督办状态
+ */
+ String getStatus();
+
+ /**
+ * 设置督办状态
+ */
+ void setStatus(String status);
+
+ /**
+ * 获取关闭时间
+ */
+ Date getCloseTime();
+
+ /**
+ * 设置关闭时间
+ */
+ void setCloseTime(Date closeTime);
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/model/instance/TaskInstance.java b/core/src/main/java/com/alibaba/smart/framework/engine/model/instance/TaskInstance.java
index ad21b987c..030fbeb87 100644
--- a/core/src/main/java/com/alibaba/smart/framework/engine/model/instance/TaskInstance.java
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/model/instance/TaskInstance.java
@@ -74,6 +74,13 @@ public interface TaskInstance extends LifeCycleInstance {
void setExtension(String extension);
+ String getDomainCode();
+
+ void setDomainCode(String domainCode);
+
+ String getExtra();
+
+ void setExtra(String extra);
String getTitle();
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/model/instance/TaskTransferRecord.java b/core/src/main/java/com/alibaba/smart/framework/engine/model/instance/TaskTransferRecord.java
new file mode 100644
index 000000000..ece457ec8
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/model/instance/TaskTransferRecord.java
@@ -0,0 +1,71 @@
+package com.alibaba.smart.framework.engine.model.instance;
+
+import java.util.Date;
+
+/**
+ * 任务移交记录接口
+ *
+ * @author SmartEngine Team
+ */
+public interface TaskTransferRecord extends LifeCycleInstance {
+
+ /**
+ * 获取流程实例ID
+ */
+ String getProcessInstanceId();
+
+ /**
+ * 设置流程实例ID
+ */
+ void setProcessInstanceId(String processInstanceId);
+
+ /**
+ * 获取任务实例ID
+ */
+ String getTaskInstanceId();
+
+ /**
+ * 设置任务实例ID
+ */
+ void setTaskInstanceId(String taskInstanceId);
+
+ /**
+ * 获取移交人用户ID
+ */
+ String getFromUserId();
+
+ /**
+ * 设置移交人用户ID
+ */
+ void setFromUserId(String fromUserId);
+
+ /**
+ * 获取接收人用户ID
+ */
+ String getToUserId();
+
+ /**
+ * 设置接收人用户ID
+ */
+ void setToUserId(String toUserId);
+
+ /**
+ * 获取移交原因
+ */
+ String getTransferReason();
+
+ /**
+ * 设置移交原因
+ */
+ void setTransferReason(String transferReason);
+
+ /**
+ * 获取截止时间
+ */
+ Date getDeadline();
+
+ /**
+ * 设置截止时间
+ */
+ void setDeadline(Date deadline);
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/pvm/event/EventConstant.java b/core/src/main/java/com/alibaba/smart/framework/engine/pvm/event/EventConstant.java
index f233c56d3..d271ff585 100644
--- a/core/src/main/java/com/alibaba/smart/framework/engine/pvm/event/EventConstant.java
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/pvm/event/EventConstant.java
@@ -17,8 +17,14 @@ public enum EventConstant {
ACTIVITY_EXECUTE,
ACTIVITY_END,
-
-
+ // Task lifecycle events (fired via TaskEventPublisher SPI)
+ TASK_ASSIGNED, // Task created and assigned to candidate(s)
+ TASK_CLAIMED, // Task claimed by a specific user
+ TASK_COMPLETED, // Task completed (approved/processed)
+ TASK_CANCELED, // Task canceled (e.g. countersign decision made by others)
+ TASK_TRANSFERRED, // Task transferred to another user
+ TASK_DELEGATED, // New assignee added to task
+ TASK_REVOKED, // Assignee removed from task
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/query/DeploymentQuery.java b/core/src/main/java/com/alibaba/smart/framework/engine/query/DeploymentQuery.java
new file mode 100644
index 000000000..65808fddd
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/query/DeploymentQuery.java
@@ -0,0 +1,169 @@
+package com.alibaba.smart.framework.engine.query;
+
+import com.alibaba.smart.framework.engine.model.instance.DeploymentInstance;
+
+/**
+ * Fluent query interface for DeploymentInstance.
+ * Provides type-safe method chaining for building deployment queries.
+ *
+ * Example usage:
+ *
{@code
+ * List deployments = smartEngine.createDeploymentQuery()
+ * .processDefinitionType("approval")
+ * .deploymentStatus(DeploymentStatusConstant.ACTIVE)
+ * .orderByModifyTime().desc()
+ * .listPage(0, 10);
+ * }
+ *
+ * @author SmartEngine Team
+ */
+public interface DeploymentQuery extends Query {
+
+ // ============ Filter conditions ============
+
+ /**
+ * Filter by deployment instance ID.
+ *
+ * @param deploymentInstanceId the deployment instance ID
+ * @return this query for method chaining
+ */
+ DeploymentQuery deploymentInstanceId(String deploymentInstanceId);
+
+ /**
+ * Filter by process definition version.
+ *
+ * @param version the process definition version
+ * @return this query for method chaining
+ */
+ DeploymentQuery processDefinitionVersion(String version);
+
+ /**
+ * Filter by process definition type.
+ *
+ * @param processDefinitionType the process definition type
+ * @return this query for method chaining
+ */
+ DeploymentQuery processDefinitionType(String processDefinitionType);
+
+ /**
+ * Conditionally filter by process definition type.
+ *
+ * @param condition if true, the filter is applied
+ * @param processDefinitionType the process definition type
+ * @return this query for method chaining
+ */
+ DeploymentQuery processDefinitionType(boolean condition, String processDefinitionType);
+
+ /**
+ * Filter by process definition code (key).
+ *
+ * @param processDefinitionCode the process definition code
+ * @return this query for method chaining
+ */
+ DeploymentQuery processDefinitionCode(String processDefinitionCode);
+
+ /**
+ * Filter by process definition name (exact match).
+ *
+ * @param processDefinitionName the process definition name
+ * @return this query for method chaining
+ */
+ DeploymentQuery processDefinitionName(String processDefinitionName);
+
+ /**
+ * Filter by process definition name (fuzzy match, LIKE %nameLike%).
+ *
+ * @param processDefinitionNameLike the name pattern
+ * @return this query for method chaining
+ */
+ DeploymentQuery processDefinitionNameLike(String processDefinitionNameLike);
+
+ /**
+ * Conditionally filter by process definition name (fuzzy match).
+ *
+ * @param condition if true, the filter is applied
+ * @param processDefinitionNameLike the name pattern
+ * @return this query for method chaining
+ */
+ DeploymentQuery processDefinitionNameLike(boolean condition, String processDefinitionNameLike);
+
+ /**
+ * Filter by process definition description (fuzzy match, LIKE %descLike%).
+ *
+ * @param processDefinitionDescLike the description pattern
+ * @return this query for method chaining
+ */
+ DeploymentQuery processDefinitionDescLike(String processDefinitionDescLike);
+
+ /**
+ * Filter by deployment user ID.
+ *
+ * @param deploymentUserId the user who deployed
+ * @return this query for method chaining
+ */
+ DeploymentQuery deploymentUserId(String deploymentUserId);
+
+ /**
+ * Filter by deployment status.
+ *
+ * @param deploymentStatus the deployment status
+ * @return this query for method chaining
+ * @see com.alibaba.smart.framework.engine.constant.DeploymentStatusConstant
+ */
+ DeploymentQuery deploymentStatus(String deploymentStatus);
+
+ /**
+ * Conditionally filter by deployment status.
+ *
+ * @param condition if true, the filter is applied
+ * @param deploymentStatus the deployment status
+ * @return this query for method chaining
+ */
+ DeploymentQuery deploymentStatus(boolean condition, String deploymentStatus);
+
+ /**
+ * Filter by logic status.
+ *
+ * @param logicStatus the logic status
+ * @return this query for method chaining
+ * @see com.alibaba.smart.framework.engine.constant.LogicStatusConstant
+ */
+ DeploymentQuery logicStatus(String logicStatus);
+
+ // ============ Ordering ============
+
+ /**
+ * Order by deployment instance ID.
+ *
+ * @return this query for method chaining (call asc() or desc() next)
+ */
+ DeploymentQuery orderByDeploymentId();
+
+ /**
+ * Order by create time.
+ *
+ * @return this query for method chaining (call asc() or desc() next)
+ */
+ DeploymentQuery orderByCreateTime();
+
+ /**
+ * Order by modify time.
+ *
+ * @return this query for method chaining (call asc() or desc() next)
+ */
+ DeploymentQuery orderByModifyTime();
+
+ /**
+ * Set ascending order for the previous orderBy call.
+ *
+ * @return this query for method chaining
+ */
+ DeploymentQuery asc();
+
+ /**
+ * Set descending order for the previous orderBy call.
+ *
+ * @return this query for method chaining
+ */
+ DeploymentQuery desc();
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/query/NotificationQuery.java b/core/src/main/java/com/alibaba/smart/framework/engine/query/NotificationQuery.java
new file mode 100644
index 000000000..3c2d2e535
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/query/NotificationQuery.java
@@ -0,0 +1,141 @@
+package com.alibaba.smart.framework.engine.query;
+
+import java.util.Date;
+import java.util.List;
+
+import com.alibaba.smart.framework.engine.model.instance.NotificationInstance;
+
+/**
+ * Fluent query interface for NotificationInstance.
+ * Provides type-safe method chaining for building notification queries.
+ *
+ * Example usage:
+ *
{@code
+ * List notifications = smartEngine.createNotificationQuery()
+ * .receiverUserId("user001")
+ * .readStatus("unread")
+ * .orderByCreateTime().desc()
+ * .listPage(0, 10);
+ * }
+ *
+ * @author SmartEngine Team
+ */
+public interface NotificationQuery extends ProcessBoundQuery {
+
+ // ============ Filter conditions ============
+
+ /**
+ * Filter by notification instance ID.
+ *
+ * @param notificationId the notification instance ID
+ * @return this query for method chaining
+ */
+ NotificationQuery notificationId(String notificationId);
+
+ /**
+ * Filter by sender user ID.
+ *
+ * @param senderUserId the sender user ID
+ * @return this query for method chaining
+ */
+ NotificationQuery senderUserId(String senderUserId);
+
+ /**
+ * Filter by receiver user ID.
+ *
+ * @param receiverUserId the receiver user ID
+ * @return this query for method chaining
+ */
+ NotificationQuery receiverUserId(String receiverUserId);
+
+ /**
+ * Conditionally filter by receiver user ID.
+ *
+ * @param condition if true, the filter is applied
+ * @param receiverUserId the receiver user ID
+ * @return this query for method chaining
+ */
+ NotificationQuery receiverUserId(boolean condition, String receiverUserId);
+
+ /**
+ * Filter by task instance ID.
+ *
+ * @param taskInstanceId the task instance ID
+ * @return this query for method chaining
+ */
+ NotificationQuery taskInstanceId(String taskInstanceId);
+
+ /**
+ * Filter by multiple task instance IDs.
+ *
+ * @param taskInstanceIds the list of task instance IDs
+ * @return this query for method chaining
+ */
+ NotificationQuery taskInstanceIdIn(List taskInstanceIds);
+
+ /**
+ * Filter by notification type.
+ *
+ * @param notificationType the notification type
+ * @return this query for method chaining
+ */
+ NotificationQuery notificationType(String notificationType);
+
+ /**
+ * Conditionally filter by notification type.
+ *
+ * @param condition if true, the filter is applied
+ * @param notificationType the notification type
+ * @return this query for method chaining
+ */
+ NotificationQuery notificationType(boolean condition, String notificationType);
+
+ /**
+ * Filter by read status.
+ *
+ * @param readStatus the read status
+ * @return this query for method chaining
+ */
+ NotificationQuery readStatus(String readStatus);
+
+ /**
+ * Conditionally filter by read status.
+ *
+ * @param condition if true, the filter is applied
+ * @param readStatus the read status
+ * @return this query for method chaining
+ */
+ NotificationQuery readStatus(boolean condition, String readStatus);
+
+ /**
+ * Filter by notification time range (start).
+ *
+ * @param startTime the start of notification time range
+ * @return this query for method chaining
+ */
+ NotificationQuery notificationTimeAfter(Date startTime);
+
+ /**
+ * Filter by notification time range (end).
+ *
+ * @param endTime the end of notification time range
+ * @return this query for method chaining
+ */
+ NotificationQuery notificationTimeBefore(Date endTime);
+
+ // ============ Ordering ============
+
+ /**
+ * Order by notification instance ID.
+ *
+ * @return this query for method chaining (call asc() or desc() next)
+ */
+ NotificationQuery orderByNotificationId();
+
+ /**
+ * Order by read time.
+ *
+ * @return this query for method chaining (call asc() or desc() next)
+ */
+ NotificationQuery orderByReadTime();
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/query/OrderSpec.java b/core/src/main/java/com/alibaba/smart/framework/engine/query/OrderSpec.java
new file mode 100644
index 000000000..81ee7ae68
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/query/OrderSpec.java
@@ -0,0 +1,86 @@
+package com.alibaba.smart.framework.engine.query;
+
+import java.io.Serializable;
+
+/**
+ * Order specification for dynamic sorting in queries.
+ * Used to build ORDER BY clauses dynamically.
+ *
+ * @author SmartEngine Team
+ */
+public class OrderSpec implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Column name to sort by (database column name)
+ */
+ private final String columnName;
+
+ /**
+ * Sort direction: ASC or DESC
+ */
+ private final Direction direction;
+
+ /**
+ * Property name (Java field name for reference)
+ */
+ private final String propertyName;
+
+ public enum Direction {
+ ASC("ASC"),
+ DESC("DESC");
+
+ private final String sql;
+
+ Direction(String sql) {
+ this.sql = sql;
+ }
+
+ public String getSql() {
+ return sql;
+ }
+ }
+
+ public OrderSpec(String propertyName, String columnName, Direction direction) {
+ this.propertyName = propertyName;
+ this.columnName = columnName;
+ this.direction = direction;
+ }
+
+ public String getColumnName() {
+ return columnName;
+ }
+
+ public String getPropertyName() {
+ return propertyName;
+ }
+
+ public Direction getDirection() {
+ return direction;
+ }
+
+ /**
+ * Get the column name for SQL ORDER BY clause.
+ * This method is used in MyBatis XML.
+ *
+ * @return the column name
+ */
+ public String toColumnName() {
+ return columnName;
+ }
+
+ /**
+ * Get the SQL direction string.
+ *
+ * @return "ASC" or "DESC"
+ */
+ public String getDirectionSql() {
+ return direction.getSql();
+ }
+
+ @Override
+ public String toString() {
+ return columnName + " " + direction.getSql();
+ }
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/query/ProcessBoundQuery.java b/core/src/main/java/com/alibaba/smart/framework/engine/query/ProcessBoundQuery.java
new file mode 100644
index 000000000..ea91e907f
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/query/ProcessBoundQuery.java
@@ -0,0 +1,67 @@
+package com.alibaba.smart.framework.engine.query;
+
+import java.util.List;
+
+/**
+ * Shared query interface for entities bound to a process instance.
+ * Provides common filtering by processInstanceId and shared ordering.
+ * Inspired by Flowable's TaskInfoQuery shared interface pattern.
+ *
+ * @param the query type itself for method chaining
+ * @param the result entity type
+ */
+public interface ProcessBoundQuery, T> extends Query {
+
+ /**
+ * Filter by process instance ID.
+ *
+ * @param processInstanceId the process instance ID
+ * @return this query for method chaining
+ */
+ Q processInstanceId(String processInstanceId);
+
+ /**
+ * Conditionally filter by process instance ID.
+ *
+ * @param condition if true, the filter is applied
+ * @param processInstanceId the process instance ID
+ * @return this query for method chaining
+ */
+ Q processInstanceId(boolean condition, String processInstanceId);
+
+ /**
+ * Filter by multiple process instance IDs.
+ *
+ * @param processInstanceIds the list of process instance IDs
+ * @return this query for method chaining
+ */
+ Q processInstanceIdIn(List processInstanceIds);
+
+ /**
+ * Order by create time.
+ *
+ * @return this query for method chaining (call asc() or desc() next)
+ */
+ Q orderByCreateTime();
+
+ /**
+ * Order by modify time.
+ *
+ * @return this query for method chaining (call asc() or desc() next)
+ */
+ Q orderByModifyTime();
+
+ /**
+ * Set ascending order for the previous orderBy call.
+ *
+ * @return this query for method chaining
+ */
+ Q asc();
+
+ /**
+ * Set descending order for the previous orderBy call.
+ *
+ * @return this query for method chaining
+ */
+ Q desc();
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/query/ProcessInstanceQuery.java b/core/src/main/java/com/alibaba/smart/framework/engine/query/ProcessInstanceQuery.java
new file mode 100644
index 000000000..52f649f02
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/query/ProcessInstanceQuery.java
@@ -0,0 +1,240 @@
+package com.alibaba.smart.framework.engine.query;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+import com.alibaba.smart.framework.engine.model.instance.ProcessInstance;
+
+/**
+ * Fluent query interface for ProcessInstance.
+ * Provides type-safe method chaining for building process instance queries.
+ *
+ * Example usage:
+ *
{@code
+ * List processes = smartEngine.createProcessQuery()
+ * .processDefinitionType("approval")
+ * .startedBy("user001")
+ * .processStatus(InstanceStatus.running)
+ * .orderByStartTime().desc()
+ * .listPage(0, 10);
+ * }
+ *
+ * @author SmartEngine Team
+ */
+public interface ProcessInstanceQuery extends Query {
+
+ // ============ Filter conditions ============
+
+ /**
+ * Filter by process instance ID.
+ *
+ * @param processInstanceId the process instance ID
+ * @return this query for method chaining
+ */
+ ProcessInstanceQuery processInstanceId(String processInstanceId);
+
+ /**
+ * Conditionally filter by process instance ID.
+ *
+ * @param condition if true, the filter is applied
+ * @param processInstanceId the process instance ID
+ * @return this query for method chaining
+ */
+ ProcessInstanceQuery processInstanceId(boolean condition, String processInstanceId);
+
+ /**
+ * Filter by multiple process instance IDs.
+ *
+ * @param processInstanceIds the list of process instance IDs
+ * @return this query for method chaining
+ */
+ ProcessInstanceQuery processInstanceIdIn(List processInstanceIds);
+
+ /**
+ * Filter by start user ID.
+ *
+ * @param startUserId the user ID who started the process
+ * @return this query for method chaining
+ */
+ ProcessInstanceQuery startedBy(String startUserId);
+
+ /**
+ * Conditionally filter by start user ID.
+ *
+ * @param condition if true, the filter is applied
+ * @param startUserId the user ID who started the process
+ * @return this query for method chaining
+ */
+ ProcessInstanceQuery startedBy(boolean condition, String startUserId);
+
+ /**
+ * Filter by process status.
+ *
+ * @param status the process status (use InstanceStatus enum value name)
+ * @return this query for method chaining
+ */
+ ProcessInstanceQuery processStatus(String status);
+
+ /**
+ * Conditionally filter by process status.
+ *
+ * @param condition if true, the filter is applied
+ * @param status the process status
+ * @return this query for method chaining
+ */
+ ProcessInstanceQuery processStatus(boolean condition, String status);
+
+ /**
+ * Filter by process definition type.
+ *
+ * @param processDefinitionType the process definition type
+ * @return this query for method chaining
+ */
+ ProcessInstanceQuery processDefinitionType(String processDefinitionType);
+
+ /**
+ * Conditionally filter by process definition type.
+ *
+ * @param condition if true, the filter is applied
+ * @param processDefinitionType the process definition type
+ * @return this query for method chaining
+ */
+ ProcessInstanceQuery processDefinitionType(boolean condition, String processDefinitionType);
+
+ /**
+ * Filter by process definition ID and version.
+ *
+ * @param processDefinitionIdAndVersion the process definition ID and version
+ * @return this query for method chaining
+ */
+ ProcessInstanceQuery processDefinitionIdAndVersion(String processDefinitionIdAndVersion);
+
+ /**
+ * Filter by parent instance ID.
+ *
+ * @param parentInstanceId the parent process instance ID
+ * @return this query for method chaining
+ */
+ ProcessInstanceQuery parentInstanceId(String parentInstanceId);
+
+ /**
+ * Filter by business unique ID.
+ *
+ * @param bizUniqueId the business unique ID
+ * @return this query for method chaining
+ */
+ ProcessInstanceQuery bizUniqueId(String bizUniqueId);
+
+ /**
+ * Conditionally filter by business unique ID.
+ *
+ * @param condition if true, the filter is applied
+ * @param bizUniqueId the business unique ID
+ * @return this query for method chaining
+ */
+ ProcessInstanceQuery bizUniqueId(boolean condition, String bizUniqueId);
+
+ /**
+ * Filter by start time range (start).
+ *
+ * @param startTimeAfter the start of time range
+ * @return this query for method chaining
+ */
+ ProcessInstanceQuery startedAfter(Date startTimeAfter);
+
+ /**
+ * Filter by start time range (end).
+ *
+ * @param startTimeBefore the end of time range
+ * @return this query for method chaining
+ */
+ ProcessInstanceQuery startedBefore(Date startTimeBefore);
+
+ /**
+ * Filter by complete time range (start).
+ *
+ * @param completeTimeStart the start of complete time range
+ * @return this query for method chaining
+ */
+ ProcessInstanceQuery completedAfter(Date completeTimeStart);
+
+ /**
+ * Filter by complete time range (end).
+ *
+ * @param completeTimeEnd the end of complete time range
+ * @return this query for method chaining
+ */
+ ProcessInstanceQuery completedBefore(Date completeTimeEnd);
+
+ /**
+ * Filter by multiple process definition types.
+ *
+ * @param types the list of process definition types
+ * @return this query for method chaining
+ */
+ ProcessInstanceQuery processDefinitionTypeIn(List types);
+
+ /**
+ * Filter by multiple process definition types (varargs).
+ *
+ * @param types the process definition types
+ * @return this query for method chaining
+ */
+ default ProcessInstanceQuery processDefinitionTypeIn(String... types) {
+ return processDefinitionTypeIn(Arrays.asList(types));
+ }
+
+ /**
+ * Filter by involved user. Finds processes where the user is the starter.
+ * This is a semantic alias for {@link #startedBy(String)}.
+ *
+ * @param userId the involved user ID
+ * @return this query for method chaining
+ */
+ ProcessInstanceQuery involvedUser(String userId);
+
+ // ============ Ordering ============
+
+ /**
+ * Order by process instance ID.
+ *
+ * @return this query for method chaining (call asc() or desc() next)
+ */
+ ProcessInstanceQuery orderByProcessInstanceId();
+
+ /**
+ * Order by start time (create time).
+ *
+ * @return this query for method chaining (call asc() or desc() next)
+ */
+ ProcessInstanceQuery orderByStartTime();
+
+ /**
+ * Order by modify time.
+ *
+ * @return this query for method chaining (call asc() or desc() next)
+ */
+ ProcessInstanceQuery orderByModifyTime();
+
+ /**
+ * Order by complete time.
+ *
+ * @return this query for method chaining (call asc() or desc() next)
+ */
+ ProcessInstanceQuery orderByCompleteTime();
+
+ /**
+ * Set ascending order for the previous orderBy call.
+ *
+ * @return this query for method chaining
+ */
+ ProcessInstanceQuery asc();
+
+ /**
+ * Set descending order for the previous orderBy call.
+ *
+ * @return this query for method chaining
+ */
+ ProcessInstanceQuery desc();
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/query/Query.java b/core/src/main/java/com/alibaba/smart/framework/engine/query/Query.java
new file mode 100644
index 000000000..f2f8ba7c9
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/query/Query.java
@@ -0,0 +1,109 @@
+package com.alibaba.smart.framework.engine.query;
+
+import java.util.List;
+
+/**
+ * Base interface for fluent query API.
+ * Provides common query operations like pagination and sorting.
+ *
+ * @param the query type itself for method chaining
+ * @param the result entity type
+ * @author SmartEngine Team
+ */
+public interface Query, T> {
+
+ // ============ Pagination ============
+
+ /**
+ * Set the page offset (0-based).
+ *
+ * @param offset page offset, starting from 0
+ * @return this query for method chaining
+ */
+ Q pageOffset(int offset);
+
+ /**
+ * Set the page size.
+ *
+ * @param size maximum number of results to return
+ * @return this query for method chaining
+ */
+ Q pageSize(int size);
+
+ // ============ Tenant ============
+
+ /**
+ * Filter by tenant ID.
+ *
+ * @param tenantId the tenant ID
+ * @return this query for method chaining
+ */
+ Q tenantId(String tenantId);
+
+ /**
+ * Conditionally filter by tenant ID.
+ *
+ * @param condition if true, the tenantId filter is applied
+ * @param tenantId the tenant ID
+ * @return this query for method chaining
+ */
+ Q tenantId(boolean condition, String tenantId);
+
+ /**
+ * Conditionally set the page offset (0-based).
+ *
+ * @param condition if true, the offset is applied
+ * @param offset page offset, starting from 0
+ * @return this query for method chaining
+ */
+ Q pageOffset(boolean condition, int offset);
+
+ /**
+ * Conditionally set the page size.
+ *
+ * @param condition if true, the size is applied
+ * @param size maximum number of results to return
+ * @return this query for method chaining
+ */
+ Q pageSize(boolean condition, int size);
+
+ /**
+ * Exclude tenant filtering. Useful for cross-tenant queries.
+ *
+ * @return this query for method chaining
+ */
+ Q withoutTenantId();
+
+ // ============ Execution methods ============
+
+ /**
+ * Execute the query and return all matching results.
+ *
+ * @return list of matching entities
+ */
+ List list();
+
+ /**
+ * Execute the query with pagination and return matching results.
+ *
+ * @param offset page offset (0-based)
+ * @param limit maximum number of results
+ * @return list of matching entities
+ */
+ List listPage(int offset, int limit);
+
+ /**
+ * Execute the query and return the single result.
+ *
+ * @return the single result, or null if not found
+ * @throws IllegalStateException if more than one result is found
+ */
+ T singleResult();
+
+ /**
+ * Execute the query and return the count of matching entities.
+ *
+ * @return the count
+ */
+ long count();
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/query/SupervisionQuery.java b/core/src/main/java/com/alibaba/smart/framework/engine/query/SupervisionQuery.java
new file mode 100644
index 000000000..5ea3498b3
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/query/SupervisionQuery.java
@@ -0,0 +1,133 @@
+package com.alibaba.smart.framework.engine.query;
+
+import java.util.Date;
+import java.util.List;
+
+import com.alibaba.smart.framework.engine.model.instance.SupervisionInstance;
+
+/**
+ * Fluent query interface for SupervisionInstance.
+ * Provides type-safe method chaining for building supervision queries.
+ *
+ * Example usage:
+ *
{@code
+ * List supervisions = smartEngine.createSupervisionQuery()
+ * .supervisorUserId("supervisor001")
+ * .supervisionStatus("active")
+ * .orderByCreateTime().desc()
+ * .listPage(0, 10);
+ * }
+ *
+ * @author SmartEngine Team
+ */
+public interface SupervisionQuery extends ProcessBoundQuery {
+
+ // ============ Filter conditions ============
+
+ /**
+ * Filter by supervision instance ID.
+ *
+ * @param supervisionId the supervision instance ID
+ * @return this query for method chaining
+ */
+ SupervisionQuery supervisionId(String supervisionId);
+
+ /**
+ * Filter by supervisor user ID.
+ *
+ * @param supervisorUserId the supervisor user ID
+ * @return this query for method chaining
+ */
+ SupervisionQuery supervisorUserId(String supervisorUserId);
+
+ /**
+ * Conditionally filter by supervisor user ID.
+ *
+ * @param condition if true, the filter is applied
+ * @param supervisorUserId the supervisor user ID
+ * @return this query for method chaining
+ */
+ SupervisionQuery supervisorUserId(boolean condition, String supervisorUserId);
+
+ /**
+ * Filter by task instance ID.
+ *
+ * @param taskInstanceId the task instance ID
+ * @return this query for method chaining
+ */
+ SupervisionQuery taskInstanceId(String taskInstanceId);
+
+ /**
+ * Filter by multiple task instance IDs.
+ *
+ * @param taskInstanceIds the list of task instance IDs
+ * @return this query for method chaining
+ */
+ SupervisionQuery taskInstanceIdIn(List taskInstanceIds);
+
+ /**
+ * Filter by supervision type.
+ *
+ * @param supervisionType the supervision type
+ * @return this query for method chaining
+ */
+ SupervisionQuery supervisionType(String supervisionType);
+
+ /**
+ * Conditionally filter by supervision type.
+ *
+ * @param condition if true, the filter is applied
+ * @param supervisionType the supervision type
+ * @return this query for method chaining
+ */
+ SupervisionQuery supervisionType(boolean condition, String supervisionType);
+
+ /**
+ * Filter by supervision status.
+ *
+ * @param status the supervision status
+ * @return this query for method chaining
+ */
+ SupervisionQuery supervisionStatus(String status);
+
+ /**
+ * Conditionally filter by supervision status.
+ *
+ * @param condition if true, the filter is applied
+ * @param status the supervision status
+ * @return this query for method chaining
+ */
+ SupervisionQuery supervisionStatus(boolean condition, String status);
+
+ /**
+ * Filter by supervision time range (start).
+ *
+ * @param startTime the start of supervision time range
+ * @return this query for method chaining
+ */
+ SupervisionQuery supervisionTimeAfter(Date startTime);
+
+ /**
+ * Filter by supervision time range (end).
+ *
+ * @param endTime the end of supervision time range
+ * @return this query for method chaining
+ */
+ SupervisionQuery supervisionTimeBefore(Date endTime);
+
+ // ============ Ordering ============
+
+ /**
+ * Order by supervision instance ID.
+ *
+ * @return this query for method chaining (call asc() or desc() next)
+ */
+ SupervisionQuery orderBySupervisionId();
+
+ /**
+ * Order by close time.
+ *
+ * @return this query for method chaining (call asc() or desc() next)
+ */
+ SupervisionQuery orderByCloseTime();
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/query/TaskQuery.java b/core/src/main/java/com/alibaba/smart/framework/engine/query/TaskQuery.java
new file mode 100644
index 000000000..f3cbc6c48
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/query/TaskQuery.java
@@ -0,0 +1,450 @@
+package com.alibaba.smart.framework.engine.query;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+import com.alibaba.smart.framework.engine.model.instance.TaskInstance;
+
+/**
+ * Fluent query interface for TaskInstance.
+ * Provides type-safe method chaining for building task queries.
+ *
+ * Example usage:
+ *
{@code
+ * List tasks = smartEngine.createTaskQuery()
+ * .processInstanceId("12345")
+ * .taskAssignee("user001")
+ * .taskStatus(TaskInstanceConstant.PENDING)
+ * .orderByCreateTime().desc()
+ * .listPage(0, 10);
+ * }
+ *
+ * @author SmartEngine Team
+ */
+public interface TaskQuery extends ProcessBoundQuery {
+
+ // ============ Filter conditions ============
+
+ /**
+ * Filter by task instance ID.
+ *
+ * @param taskInstanceId the task instance ID
+ * @return this query for method chaining
+ */
+ TaskQuery taskInstanceId(String taskInstanceId);
+
+ /**
+ * Filter by activity instance ID.
+ *
+ * @param activityInstanceId the activity instance ID
+ * @return this query for method chaining
+ */
+ TaskQuery activityInstanceId(String activityInstanceId);
+
+ /**
+ * Filter by process definition type.
+ *
+ * @param processDefinitionType the process definition type
+ * @return this query for method chaining
+ */
+ TaskQuery processDefinitionType(String processDefinitionType);
+
+ /**
+ * Conditionally filter by process definition type.
+ *
+ * @param condition if true, the filter is applied
+ * @param processDefinitionType the process definition type
+ * @return this query for method chaining
+ */
+ TaskQuery processDefinitionType(boolean condition, String processDefinitionType);
+
+ /**
+ * Filter by process definition activity ID.
+ *
+ * @param processDefinitionActivityId the process definition activity ID
+ * @return this query for method chaining
+ */
+ TaskQuery processDefinitionActivityId(String processDefinitionActivityId);
+
+ /**
+ * Filter by task status.
+ *
+ * @param status the task status
+ * @return this query for method chaining
+ * @see com.alibaba.smart.framework.engine.constant.TaskInstanceConstant
+ */
+ TaskQuery taskStatus(String status);
+
+ /**
+ * Conditionally filter by task status.
+ *
+ * @param condition if true, the filter is applied
+ * @param status the task status
+ * @return this query for method chaining
+ */
+ TaskQuery taskStatus(boolean condition, String status);
+
+ /**
+ * Filter by multiple task statuses.
+ *
+ * @param statuses the list of task statuses
+ * @return this query for method chaining
+ */
+ TaskQuery taskStatusIn(List statuses);
+
+ /**
+ * Filter by multiple task statuses (varargs).
+ *
+ * @param statuses the task statuses
+ * @return this query for method chaining
+ */
+ TaskQuery taskStatusIn(String... statuses);
+
+ /**
+ * Filter by claim user ID (task assignee).
+ *
+ * @param claimUserId the user ID who claimed the task
+ * @return this query for method chaining
+ */
+ TaskQuery taskAssignee(String claimUserId);
+
+ /**
+ * Conditionally filter by claim user ID (task assignee).
+ *
+ * @param condition if true, the filter is applied
+ * @param claimUserId the user ID who claimed the task
+ * @return this query for method chaining
+ */
+ TaskQuery taskAssignee(boolean condition, String claimUserId);
+
+ /**
+ * Filter by candidate user ID (via assignee table JOIN).
+ * Finds tasks where the specified user is a candidate assignee (assignee_type = 'user').
+ *
+ * This differs from {@link #taskAssignee(String)} which filters by the already-claimed user.
+ * This method queries the task assignee candidate table to find tasks assigned to the user.
+ *
+ * @param userId the candidate user ID
+ * @return this query for method chaining
+ */
+ TaskQuery taskCandidateUser(String userId);
+
+ /**
+ * Filter by candidate group ID (via assignee table JOIN).
+ * Finds tasks where the specified group is a candidate assignee (assignee_type = 'group').
+ *
+ * @param groupId the candidate group ID
+ * @return this query for method chaining
+ */
+ TaskQuery taskCandidateGroup(String groupId);
+
+ /**
+ * Filter by multiple candidate group IDs (via assignee table JOIN).
+ * Finds tasks where any of the specified groups is a candidate assignee.
+ *
+ * @param groupIds the candidate group ID list
+ * @return this query for method chaining
+ */
+ TaskQuery taskCandidateGroupIn(List groupIds);
+
+ /**
+ * Filter by multiple candidate group IDs (varargs).
+ *
+ * @param groupIds the candidate group IDs
+ * @return this query for method chaining
+ */
+ default TaskQuery taskCandidateGroupIn(String... groupIds) {
+ return taskCandidateGroupIn(Arrays.asList(groupIds));
+ }
+
+ /**
+ * Filter by candidate user OR candidate groups (via assignee table JOIN).
+ * Finds tasks where the user is a candidate (assignee_type = 'user')
+ * OR any of the groups is a candidate (assignee_type = 'group').
+ *
+ * This is the most common pattern for pending task queries:
+ *
{@code
+ * List myTasks = smartEngine.createTaskQuery()
+ * .taskCandidateOrGroup("user001", Arrays.asList("dept-A", "role-manager"))
+ * .taskStatus(TaskInstanceConstant.PENDING)
+ * .listPage(0, 10);
+ * }
+ *
+ * @param userId the candidate user ID
+ * @param groupIds the candidate group ID list
+ * @return this query for method chaining
+ */
+ TaskQuery taskCandidateOrGroup(String userId, List groupIds);
+
+ /**
+ * Filter by tag.
+ *
+ * @param tag the task tag
+ * @return this query for method chaining
+ */
+ TaskQuery taskTag(String tag);
+
+ /**
+ * Conditionally filter by tag.
+ *
+ * @param condition if true, the filter is applied
+ * @param tag the task tag
+ * @return this query for method chaining
+ */
+ TaskQuery taskTag(boolean condition, String tag);
+
+ /**
+ * Filter by extension field.
+ *
+ * @param extension the extension value
+ * @return this query for method chaining
+ */
+ TaskQuery taskExtension(String extension);
+
+ /**
+ * Filter by priority.
+ *
+ * @param priority the task priority
+ * @return this query for method chaining
+ */
+ TaskQuery taskPriority(Integer priority);
+
+ /**
+ * Filter by comment.
+ *
+ * @param comment the task comment
+ * @return this query for method chaining
+ */
+ TaskQuery taskComment(String comment);
+
+ /**
+ * Filter by title.
+ *
+ * @param title the task title
+ * @return this query for method chaining
+ */
+ TaskQuery taskTitle(String title);
+
+ /**
+ * Conditionally filter by title.
+ *
+ * @param condition if true, the filter is applied
+ * @param title the task title
+ * @return this query for method chaining
+ */
+ TaskQuery taskTitle(boolean condition, String title);
+
+ /**
+ * Filter by complete time range (start).
+ *
+ * @param completeTimeStart the start of complete time range
+ * @return this query for method chaining
+ */
+ TaskQuery completeTimeAfter(Date completeTimeStart);
+
+ /**
+ * Filter by complete time range (end).
+ *
+ * @param completeTimeEnd the end of complete time range
+ * @return this query for method chaining
+ */
+ TaskQuery completeTimeBefore(Date completeTimeEnd);
+
+ /**
+ * Filter by create time range (start, inclusive).
+ *
+ * @param createTimeStart tasks created at or after this time
+ * @return this query for method chaining
+ */
+ TaskQuery createdAfter(Date createTimeStart);
+
+ /**
+ * Filter by create time range (end, exclusive).
+ *
+ * @param createTimeEnd tasks created before this time
+ * @return this query for method chaining
+ */
+ TaskQuery createdBefore(Date createTimeEnd);
+
+ /**
+ * Filter by title using fuzzy match (LIKE %titleLike%).
+ *
+ * @param titleLike the title pattern to match
+ * @return this query for method chaining
+ */
+ TaskQuery taskTitleLike(String titleLike);
+
+ /**
+ * Conditionally filter by title using fuzzy match.
+ *
+ * @param condition if true, the filter is applied
+ * @param titleLike the title pattern to match
+ * @return this query for method chaining
+ */
+ TaskQuery taskTitleLike(boolean condition, String titleLike);
+
+ /**
+ * Filter for unassigned tasks (no claim user).
+ *
+ * @return this query for method chaining
+ */
+ TaskQuery taskUnassigned();
+
+ /**
+ * Filter by multiple process definition types.
+ *
+ * @param types the list of process definition types
+ * @return this query for method chaining
+ */
+ TaskQuery processDefinitionTypeIn(List types);
+
+ /**
+ * Filter by multiple process definition types (varargs).
+ *
+ * @param types the process definition types
+ * @return this query for method chaining
+ */
+ default TaskQuery processDefinitionTypeIn(String... types) {
+ return processDefinitionTypeIn(Arrays.asList(types));
+ }
+
+ /**
+ * Filter by assignee using fuzzy match (LIKE %claimUserIdLike%).
+ *
+ * @param claimUserIdLike the assignee pattern to match
+ * @return this query for method chaining
+ */
+ TaskQuery taskAssigneeLike(String claimUserIdLike);
+
+ /**
+ * Filter by minimum priority (inclusive).
+ *
+ * @param minPriority the minimum priority value
+ * @return this query for method chaining
+ */
+ TaskQuery taskMinPriority(Integer minPriority);
+
+ /**
+ * Filter by maximum priority (inclusive).
+ *
+ * @param maxPriority the maximum priority value
+ * @return this query for method chaining
+ */
+ TaskQuery taskMaxPriority(Integer maxPriority);
+
+ // ============ Domain code filters ============
+
+ /**
+ * Filter by domain code (exact match).
+ *
+ * @param domainCode the domain code
+ * @return this query for method chaining
+ */
+ TaskQuery domainCode(String domainCode);
+
+ /**
+ * Conditionally filter by domain code.
+ *
+ * @param condition if true, the filter is applied
+ * @param domainCode the domain code
+ * @return this query for method chaining
+ */
+ TaskQuery domainCode(boolean condition, String domainCode);
+
+ /**
+ * Filter by multiple domain codes (IN clause).
+ *
+ * @param domainCodes the list of domain codes
+ * @return this query for method chaining
+ */
+ TaskQuery domainCodeIn(List domainCodes);
+
+ /**
+ * Filter by multiple domain codes (varargs).
+ *
+ * @param domainCodes the domain codes
+ * @return this query for method chaining
+ */
+ default TaskQuery domainCodeIn(String... domainCodes) {
+ return domainCodeIn(Arrays.asList(domainCodes));
+ }
+
+ /**
+ * Filter by domain code using fuzzy match (LIKE %domainCodeLike%).
+ *
+ * @param domainCodeLike the domain code pattern to match
+ * @return this query for method chaining
+ */
+ TaskQuery domainCodeLike(String domainCodeLike);
+
+ // ============ Extra JSON filters ============
+
+ /**
+ * Filter by a JSON key-value in the extra field (exact match).
+ *
+ * @param key JSON key (single-level, e.g., "category")
+ * @param value expected value
+ * @return this query for method chaining
+ */
+ TaskQuery extraJson(String key, String value);
+
+ /**
+ * Filter by JSON key matching any of the given values (IN clause).
+ *
+ * @param key JSON key (single-level)
+ * @param values the list of expected values
+ * @return this query for method chaining
+ */
+ TaskQuery extraJsonIn(String key, List values);
+
+ /**
+ * Filter by JSON key matching any of the given values (varargs).
+ *
+ * @param key JSON key (single-level)
+ * @param values the expected values
+ * @return this query for method chaining
+ */
+ default TaskQuery extraJsonIn(String key, String... values) {
+ return extraJsonIn(key, Arrays.asList(values));
+ }
+
+ /**
+ * Filter by JSON key using LIKE match.
+ *
+ * @param key JSON key (single-level)
+ * @param pattern the LIKE pattern
+ * @return this query for method chaining
+ */
+ TaskQuery extraJsonLike(String key, String pattern);
+
+ // ============ Ordering ============
+
+ /**
+ * Order by task instance ID.
+ *
+ * @return this query for method chaining (call asc() or desc() next)
+ */
+ TaskQuery orderByTaskId();
+
+ /**
+ * Order by claim time.
+ *
+ * @return this query for method chaining (call asc() or desc() next)
+ */
+ TaskQuery orderByClaimTime();
+
+ /**
+ * Order by complete time.
+ *
+ * @return this query for method chaining (call asc() or desc() next)
+ */
+ TaskQuery orderByCompleteTime();
+
+ /**
+ * Order by priority.
+ *
+ * @return this query for method chaining (call asc() or desc() next)
+ */
+ TaskQuery orderByPriority();
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/query/impl/AbstractProcessBoundQuery.java b/core/src/main/java/com/alibaba/smart/framework/engine/query/impl/AbstractProcessBoundQuery.java
new file mode 100644
index 000000000..964f5fa21
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/query/impl/AbstractProcessBoundQuery.java
@@ -0,0 +1,90 @@
+package com.alibaba.smart.framework.engine.query.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.smart.framework.engine.configuration.ProcessEngineConfiguration;
+import com.alibaba.smart.framework.engine.query.ProcessBoundQuery;
+
+/**
+ * Abstract base for queries bound to process instances.
+ * Provides common processInstanceId filtering and ordering.
+ *
+ * @param the query type itself for method chaining
+ * @param the result entity type
+ */
+public abstract class AbstractProcessBoundQuery, T>
+ extends AbstractQuery implements ProcessBoundQuery {
+
+ protected String processInstanceId;
+ protected List processInstanceIds;
+
+ protected AbstractProcessBoundQuery(ProcessEngineConfiguration processEngineConfiguration) {
+ super(processEngineConfiguration);
+ }
+
+ @Override
+ public Q processInstanceId(String processInstanceId) {
+ this.processInstanceId = processInstanceId;
+ return self();
+ }
+
+ @Override
+ public Q processInstanceId(boolean condition, String processInstanceId) {
+ if (condition) {
+ this.processInstanceId = processInstanceId;
+ }
+ return self();
+ }
+
+ @Override
+ public Q processInstanceIdIn(List processInstanceIds) {
+ this.processInstanceIds = processInstanceIds;
+ return self();
+ }
+
+ @Override
+ public Q orderByCreateTime() {
+ return orderBy("gmtCreate", "gmt_create");
+ }
+
+ @Override
+ public Q orderByModifyTime() {
+ return orderBy("gmtModified", "gmt_modified");
+ }
+
+ @Override
+ public Q asc() {
+ return applyAsc();
+ }
+
+ @Override
+ public Q desc() {
+ return applyDesc();
+ }
+
+ /**
+ * Build process instance ID list for query param.
+ * Helper for subclasses' buildQueryParam() methods.
+ *
+ * @return the process instance ID list, or null if not set
+ */
+ protected List buildProcessInstanceIdList() {
+ if (processInstanceId != null) {
+ List ids = new ArrayList<>();
+ ids.add(processInstanceId);
+ return ids;
+ } else if (processInstanceIds != null && !processInstanceIds.isEmpty()) {
+ return processInstanceIds;
+ }
+ return null;
+ }
+
+ public String getProcessInstanceId() {
+ return processInstanceId;
+ }
+
+ public List getProcessInstanceIds() {
+ return processInstanceIds;
+ }
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/query/impl/AbstractQuery.java b/core/src/main/java/com/alibaba/smart/framework/engine/query/impl/AbstractQuery.java
new file mode 100644
index 000000000..dfd28ccab
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/query/impl/AbstractQuery.java
@@ -0,0 +1,206 @@
+package com.alibaba.smart.framework.engine.query.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.smart.framework.engine.configuration.ProcessEngineConfiguration;
+import com.alibaba.smart.framework.engine.query.OrderSpec;
+import com.alibaba.smart.framework.engine.query.Query;
+
+/**
+ * Abstract base implementation for fluent query API.
+ * Provides common functionality for pagination, ordering, and execution.
+ *
+ * @param the query type itself for method chaining
+ * @param the result entity type
+ * @author SmartEngine Team
+ */
+public abstract class AbstractQuery, T> implements Query {
+
+ protected ProcessEngineConfiguration processEngineConfiguration;
+
+ protected Integer pageOffset;
+ protected Integer pageSize;
+ protected String tenantId;
+ protected boolean excludeTenant = false;
+
+ protected List orderBySpecs = new ArrayList<>();
+ protected String currentOrderByProperty;
+ protected String currentOrderByColumn;
+
+ protected AbstractQuery(ProcessEngineConfiguration processEngineConfiguration) {
+ this.processEngineConfiguration = processEngineConfiguration;
+ }
+
+ @SuppressWarnings("unchecked")
+ protected Q self() {
+ return (Q) this;
+ }
+
+ // ============ Pagination ============
+
+ @Override
+ public Q pageOffset(int offset) {
+ this.pageOffset = offset;
+ return self();
+ }
+
+ @Override
+ public Q pageSize(int size) {
+ this.pageSize = size;
+ return self();
+ }
+
+ // ============ Tenant ============
+
+ @Override
+ public Q tenantId(String tenantId) {
+ this.tenantId = tenantId;
+ return self();
+ }
+
+ @Override
+ public Q tenantId(boolean condition, String tenantId) {
+ if (condition) {
+ this.tenantId = tenantId;
+ }
+ return self();
+ }
+
+ @Override
+ public Q pageOffset(boolean condition, int offset) {
+ if (condition) {
+ this.pageOffset = offset;
+ }
+ return self();
+ }
+
+ @Override
+ public Q pageSize(boolean condition, int size) {
+ if (condition) {
+ this.pageSize = size;
+ }
+ return self();
+ }
+
+ @Override
+ public Q withoutTenantId() {
+ this.excludeTenant = true;
+ return self();
+ }
+
+ // ============ Ordering helpers ============
+
+ /**
+ * Set up an order by with the given property and column names.
+ * The actual direction (asc/desc) will be set by a subsequent call.
+ *
+ * @param propertyName the Java property name
+ * @param columnName the database column name
+ * @return this query for method chaining
+ */
+ protected Q orderBy(String propertyName, String columnName) {
+ this.currentOrderByProperty = propertyName;
+ this.currentOrderByColumn = columnName;
+ return self();
+ }
+
+ /**
+ * Apply ascending order to the current order by specification.
+ *
+ * @return this query for method chaining
+ */
+ protected Q applyAsc() {
+ if (currentOrderByColumn != null) {
+ orderBySpecs.add(new OrderSpec(currentOrderByProperty, currentOrderByColumn, OrderSpec.Direction.ASC));
+ currentOrderByProperty = null;
+ currentOrderByColumn = null;
+ }
+ return self();
+ }
+
+ /**
+ * Apply descending order to the current order by specification.
+ *
+ * @return this query for method chaining
+ */
+ protected Q applyDesc() {
+ if (currentOrderByColumn != null) {
+ orderBySpecs.add(new OrderSpec(currentOrderByProperty, currentOrderByColumn, OrderSpec.Direction.DESC));
+ currentOrderByProperty = null;
+ currentOrderByColumn = null;
+ }
+ return self();
+ }
+
+ // ============ Execution methods ============
+
+ @Override
+ public List list() {
+ List results = executeList();
+ return results != null ? results : new ArrayList<>();
+ }
+
+ @Override
+ public List listPage(int offset, int limit) {
+ this.pageOffset = offset;
+ this.pageSize = limit;
+ List results = executeList();
+ return results != null ? results : new ArrayList<>();
+ }
+
+ @Override
+ public T singleResult() {
+ List results = list();
+ if (results.isEmpty()) {
+ return null;
+ }
+ if (results.size() > 1) {
+ throw new IllegalStateException("Query returned " + results.size() + " results instead of max 1");
+ }
+ return results.get(0);
+ }
+
+ @Override
+ public long count() {
+ return executeCount();
+ }
+
+ // ============ Abstract methods to be implemented by subclasses ============
+
+ /**
+ * Execute the list query.
+ *
+ * @return the list of results
+ */
+ protected abstract List executeList();
+
+ /**
+ * Execute the count query.
+ *
+ * @return the count of results
+ */
+ protected abstract long executeCount();
+
+ // ============ Getters for subclasses ============
+
+ public List getOrderBySpecs() {
+ return orderBySpecs;
+ }
+
+ public Integer getPageOffset() {
+ return pageOffset;
+ }
+
+ public Integer getPageSize() {
+ return pageSize;
+ }
+
+ public String getTenantId() {
+ return tenantId;
+ }
+
+ protected ProcessEngineConfiguration getProcessEngineConfiguration() {
+ return processEngineConfiguration;
+ }
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/query/impl/DeploymentQueryImpl.java b/core/src/main/java/com/alibaba/smart/framework/engine/query/impl/DeploymentQueryImpl.java
new file mode 100644
index 000000000..ca60c6fc2
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/query/impl/DeploymentQueryImpl.java
@@ -0,0 +1,199 @@
+package com.alibaba.smart.framework.engine.query.impl;
+
+import java.util.List;
+
+import com.alibaba.smart.framework.engine.configuration.ProcessEngineConfiguration;
+import com.alibaba.smart.framework.engine.extension.constant.ExtensionConstant;
+import com.alibaba.smart.framework.engine.instance.storage.DeploymentInstanceStorage;
+import com.alibaba.smart.framework.engine.model.instance.DeploymentInstance;
+import com.alibaba.smart.framework.engine.query.DeploymentQuery;
+import com.alibaba.smart.framework.engine.service.param.query.DeploymentInstanceQueryParam;
+
+/**
+ * Implementation of DeploymentQuery fluent API.
+ *
+ * @author SmartEngine Team
+ */
+public class DeploymentQueryImpl extends AbstractQuery
+ implements DeploymentQuery {
+
+ private DeploymentInstanceStorage deploymentInstanceStorage;
+
+ // Filter conditions
+ private String deploymentInstanceId;
+ private String processDefinitionVersion;
+ private String processDefinitionType;
+ private String processDefinitionCode;
+ private String processDefinitionName;
+ private String processDefinitionNameLike;
+ private String processDefinitionDescLike;
+ private String deploymentUserId;
+ private String deploymentStatus;
+ private String logicStatus;
+
+ public DeploymentQueryImpl(ProcessEngineConfiguration processEngineConfiguration) {
+ super(processEngineConfiguration);
+ this.deploymentInstanceStorage = processEngineConfiguration.getAnnotationScanner()
+ .getExtensionPoint(ExtensionConstant.COMMON, DeploymentInstanceStorage.class);
+ }
+
+ // ============ Filter conditions ============
+
+ @Override
+ public DeploymentQuery deploymentInstanceId(String deploymentInstanceId) {
+ this.deploymentInstanceId = deploymentInstanceId;
+ return this;
+ }
+
+ @Override
+ public DeploymentQuery processDefinitionVersion(String version) {
+ this.processDefinitionVersion = version;
+ return this;
+ }
+
+ @Override
+ public DeploymentQuery processDefinitionType(String processDefinitionType) {
+ this.processDefinitionType = processDefinitionType;
+ return this;
+ }
+
+ @Override
+ public DeploymentQuery processDefinitionType(boolean condition, String processDefinitionType) {
+ if (condition) {
+ this.processDefinitionType = processDefinitionType;
+ }
+ return this;
+ }
+
+ @Override
+ public DeploymentQuery processDefinitionCode(String processDefinitionCode) {
+ this.processDefinitionCode = processDefinitionCode;
+ return this;
+ }
+
+ @Override
+ public DeploymentQuery processDefinitionName(String processDefinitionName) {
+ this.processDefinitionName = processDefinitionName;
+ return this;
+ }
+
+ @Override
+ public DeploymentQuery processDefinitionNameLike(String processDefinitionNameLike) {
+ this.processDefinitionNameLike = processDefinitionNameLike;
+ return this;
+ }
+
+ @Override
+ public DeploymentQuery processDefinitionNameLike(boolean condition, String processDefinitionNameLike) {
+ if (condition) {
+ this.processDefinitionNameLike = processDefinitionNameLike;
+ }
+ return this;
+ }
+
+ @Override
+ public DeploymentQuery processDefinitionDescLike(String processDefinitionDescLike) {
+ this.processDefinitionDescLike = processDefinitionDescLike;
+ return this;
+ }
+
+ @Override
+ public DeploymentQuery deploymentUserId(String deploymentUserId) {
+ this.deploymentUserId = deploymentUserId;
+ return this;
+ }
+
+ @Override
+ public DeploymentQuery deploymentStatus(String deploymentStatus) {
+ this.deploymentStatus = deploymentStatus;
+ return this;
+ }
+
+ @Override
+ public DeploymentQuery deploymentStatus(boolean condition, String deploymentStatus) {
+ if (condition) {
+ this.deploymentStatus = deploymentStatus;
+ }
+ return this;
+ }
+
+ @Override
+ public DeploymentQuery logicStatus(String logicStatus) {
+ this.logicStatus = logicStatus;
+ return this;
+ }
+
+ // ============ Ordering ============
+
+ @Override
+ public DeploymentQuery orderByDeploymentId() {
+ return orderBy("id", "id");
+ }
+
+ @Override
+ public DeploymentQuery orderByCreateTime() {
+ return orderBy("gmtCreate", "gmt_create");
+ }
+
+ @Override
+ public DeploymentQuery orderByModifyTime() {
+ return orderBy("gmtModified", "gmt_modified");
+ }
+
+ @Override
+ public DeploymentQuery asc() {
+ return applyAsc();
+ }
+
+ @Override
+ public DeploymentQuery desc() {
+ return applyDesc();
+ }
+
+ // ============ Execution ============
+
+ @Override
+ protected List executeList() {
+ DeploymentInstanceQueryParam param = buildQueryParam();
+ return deploymentInstanceStorage.findByPage(param, processEngineConfiguration);
+ }
+
+ @Override
+ protected long executeCount() {
+ DeploymentInstanceQueryParam param = buildQueryParam();
+ return deploymentInstanceStorage.count(param, processEngineConfiguration);
+ }
+
+ /**
+ * Build DeploymentInstanceQueryParam from the fluent query settings.
+ */
+ private DeploymentInstanceQueryParam buildQueryParam() {
+ DeploymentInstanceQueryParam param = new DeploymentInstanceQueryParam();
+
+ if (deploymentInstanceId != null) {
+ param.setId(Long.parseLong(deploymentInstanceId));
+ }
+
+ param.setProcessDefinitionVersion(processDefinitionVersion);
+ param.setProcessDefinitionType(processDefinitionType);
+ param.setProcessDefinitionCode(processDefinitionCode);
+ param.setProcessDefinitionName(processDefinitionName);
+ param.setProcessDefinitionNameLike(processDefinitionNameLike);
+ param.setProcessDefinitionDescLike(processDefinitionDescLike);
+ param.setDeploymentUserId(deploymentUserId);
+ param.setDeploymentStatus(deploymentStatus);
+ param.setLogicStatus(logicStatus);
+
+ // Set pagination
+ param.setPageOffset(pageOffset);
+ param.setPageSize(pageSize);
+ if (!excludeTenant) {
+ param.setTenantId(tenantId);
+ }
+
+ // Set order by specs
+ param.setOrderBySpecs(orderBySpecs);
+
+ return param;
+ }
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/query/impl/NotificationQueryImpl.java b/core/src/main/java/com/alibaba/smart/framework/engine/query/impl/NotificationQueryImpl.java
new file mode 100644
index 000000000..f68fe8ab8
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/query/impl/NotificationQueryImpl.java
@@ -0,0 +1,194 @@
+package com.alibaba.smart.framework.engine.query.impl;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import com.alibaba.smart.framework.engine.configuration.ProcessEngineConfiguration;
+import com.alibaba.smart.framework.engine.extension.constant.ExtensionConstant;
+import com.alibaba.smart.framework.engine.instance.storage.NotificationInstanceStorage;
+import com.alibaba.smart.framework.engine.model.instance.NotificationInstance;
+import com.alibaba.smart.framework.engine.query.NotificationQuery;
+import com.alibaba.smart.framework.engine.service.param.query.NotificationQueryParam;
+
+/**
+ * Implementation of NotificationQuery fluent API.
+ *
+ * @author SmartEngine Team
+ */
+public class NotificationQueryImpl extends AbstractProcessBoundQuery
+ implements NotificationQuery {
+
+ private NotificationInstanceStorage notificationInstanceStorage;
+
+ // Filter conditions
+ private String notificationId;
+ private String senderUserId;
+ private String receiverUserId;
+ private String taskInstanceId;
+ private List taskInstanceIds;
+ private String notificationType;
+ private String readStatus;
+ private Date notificationStartTime;
+ private Date notificationEndTime;
+
+ public NotificationQueryImpl(ProcessEngineConfiguration processEngineConfiguration) {
+ super(processEngineConfiguration);
+ this.notificationInstanceStorage = processEngineConfiguration.getAnnotationScanner()
+ .getExtensionPoint(ExtensionConstant.COMMON, NotificationInstanceStorage.class);
+ }
+
+ // ============ Filter conditions ============
+
+ @Override
+ public NotificationQuery notificationId(String notificationId) {
+ this.notificationId = notificationId;
+ return this;
+ }
+
+ @Override
+ public NotificationQuery senderUserId(String senderUserId) {
+ this.senderUserId = senderUserId;
+ return this;
+ }
+
+ @Override
+ public NotificationQuery receiverUserId(String receiverUserId) {
+ this.receiverUserId = receiverUserId;
+ return this;
+ }
+
+ @Override
+ public NotificationQuery receiverUserId(boolean condition, String receiverUserId) {
+ if (condition) {
+ this.receiverUserId = receiverUserId;
+ }
+ return this;
+ }
+
+ @Override
+ public NotificationQuery taskInstanceId(String taskInstanceId) {
+ this.taskInstanceId = taskInstanceId;
+ return this;
+ }
+
+ @Override
+ public NotificationQuery taskInstanceIdIn(List taskInstanceIds) {
+ this.taskInstanceIds = taskInstanceIds;
+ return this;
+ }
+
+ @Override
+ public NotificationQuery notificationType(String notificationType) {
+ this.notificationType = notificationType;
+ return this;
+ }
+
+ @Override
+ public NotificationQuery notificationType(boolean condition, String notificationType) {
+ if (condition) {
+ this.notificationType = notificationType;
+ }
+ return this;
+ }
+
+ @Override
+ public NotificationQuery readStatus(String readStatus) {
+ this.readStatus = readStatus;
+ return this;
+ }
+
+ @Override
+ public NotificationQuery readStatus(boolean condition, String readStatus) {
+ if (condition) {
+ this.readStatus = readStatus;
+ }
+ return this;
+ }
+
+ @Override
+ public NotificationQuery notificationTimeAfter(Date startTime) {
+ this.notificationStartTime = startTime;
+ return this;
+ }
+
+ @Override
+ public NotificationQuery notificationTimeBefore(Date endTime) {
+ this.notificationEndTime = endTime;
+ return this;
+ }
+
+ // ============ Ordering ============
+
+ @Override
+ public NotificationQuery orderByNotificationId() {
+ return orderBy("id", "id");
+ }
+
+ @Override
+ public NotificationQuery orderByReadTime() {
+ return orderBy("readTime", "read_time");
+ }
+
+ // ============ Execution ============
+
+ @Override
+ protected List executeList() {
+ NotificationQueryParam param = buildQueryParam();
+ return notificationInstanceStorage.findNotificationList(param, processEngineConfiguration);
+ }
+
+ @Override
+ protected long executeCount() {
+ NotificationQueryParam param = buildQueryParam();
+ Long count = notificationInstanceStorage.countNotifications(param, processEngineConfiguration);
+ return count != null ? count : 0L;
+ }
+
+ /**
+ * Build NotificationQueryParam from the fluent query settings.
+ */
+ private NotificationQueryParam buildQueryParam() {
+ NotificationQueryParam param = new NotificationQueryParam();
+
+ // Set ID filter
+ if (notificationId != null) {
+ param.setId(Long.parseLong(notificationId));
+ }
+
+ // Set user filters
+ param.setSenderUserId(senderUserId);
+ param.setReceiverUserId(receiverUserId);
+
+ // Set task instance filter
+ if (taskInstanceId != null) {
+ List ids = new ArrayList<>();
+ ids.add(taskInstanceId);
+ param.setTaskInstanceIdList(ids);
+ } else if (taskInstanceIds != null && !taskInstanceIds.isEmpty()) {
+ param.setTaskInstanceIdList(taskInstanceIds);
+ }
+
+ // Set process instance filter (from base class)
+ List processInstanceIdList = buildProcessInstanceIdList();
+ if (processInstanceIdList != null) {
+ param.setProcessInstanceIdList(processInstanceIdList);
+ }
+
+ // Set other filters
+ param.setNotificationType(notificationType);
+ param.setReadStatus(readStatus);
+ param.setNotificationStartTime(notificationStartTime);
+ param.setNotificationEndTime(notificationEndTime);
+
+ // Set pagination
+ param.setPageOffset(pageOffset);
+ param.setPageSize(pageSize);
+ param.setTenantId(tenantId);
+
+ // Set order by specs
+ param.setOrderBySpecs(orderBySpecs);
+
+ return param;
+ }
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/query/impl/ProcessInstanceQueryImpl.java b/core/src/main/java/com/alibaba/smart/framework/engine/query/impl/ProcessInstanceQueryImpl.java
new file mode 100644
index 000000000..9c67eea36
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/query/impl/ProcessInstanceQueryImpl.java
@@ -0,0 +1,261 @@
+package com.alibaba.smart.framework.engine.query.impl;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import com.alibaba.smart.framework.engine.configuration.ProcessEngineConfiguration;
+import com.alibaba.smart.framework.engine.extension.constant.ExtensionConstant;
+import com.alibaba.smart.framework.engine.instance.storage.ProcessInstanceStorage;
+import com.alibaba.smart.framework.engine.model.instance.ProcessInstance;
+import com.alibaba.smart.framework.engine.query.ProcessInstanceQuery;
+import com.alibaba.smart.framework.engine.service.param.query.ProcessInstanceQueryParam;
+
+/**
+ * Implementation of ProcessInstanceQuery fluent API.
+ *
+ * @author SmartEngine Team
+ */
+public class ProcessInstanceQueryImpl extends AbstractQuery
+ implements ProcessInstanceQuery {
+
+ private ProcessInstanceStorage processInstanceStorage;
+
+ // Filter conditions
+ private String processInstanceId;
+ private List processInstanceIds;
+ private String startUserId;
+ private String status;
+ private String processDefinitionType;
+ private String processDefinitionIdAndVersion;
+ private String parentInstanceId;
+ private String bizUniqueId;
+ private Date startTimeAfter;
+ private Date startTimeBefore;
+ private Date completeTimeStart;
+ private Date completeTimeEnd;
+ private List processDefinitionTypeList;
+
+ public ProcessInstanceQueryImpl(ProcessEngineConfiguration processEngineConfiguration) {
+ super(processEngineConfiguration);
+ this.processInstanceStorage = processEngineConfiguration.getAnnotationScanner()
+ .getExtensionPoint(ExtensionConstant.COMMON, ProcessInstanceStorage.class);
+ }
+
+ // ============ Filter conditions ============
+
+ @Override
+ public ProcessInstanceQuery processInstanceId(String processInstanceId) {
+ this.processInstanceId = processInstanceId;
+ return this;
+ }
+
+ @Override
+ public ProcessInstanceQuery processInstanceId(boolean condition, String processInstanceId) {
+ if (condition) {
+ this.processInstanceId = processInstanceId;
+ }
+ return this;
+ }
+
+ @Override
+ public ProcessInstanceQuery processInstanceIdIn(List processInstanceIds) {
+ this.processInstanceIds = processInstanceIds;
+ return this;
+ }
+
+ @Override
+ public ProcessInstanceQuery startedBy(String startUserId) {
+ this.startUserId = startUserId;
+ return this;
+ }
+
+ @Override
+ public ProcessInstanceQuery startedBy(boolean condition, String startUserId) {
+ if (condition) {
+ this.startUserId = startUserId;
+ }
+ return this;
+ }
+
+ @Override
+ public ProcessInstanceQuery processStatus(String status) {
+ this.status = status;
+ return this;
+ }
+
+ @Override
+ public ProcessInstanceQuery processStatus(boolean condition, String status) {
+ if (condition) {
+ this.status = status;
+ }
+ return this;
+ }
+
+ @Override
+ public ProcessInstanceQuery processDefinitionType(String processDefinitionType) {
+ this.processDefinitionType = processDefinitionType;
+ return this;
+ }
+
+ @Override
+ public ProcessInstanceQuery processDefinitionType(boolean condition, String processDefinitionType) {
+ if (condition) {
+ this.processDefinitionType = processDefinitionType;
+ }
+ return this;
+ }
+
+ @Override
+ public ProcessInstanceQuery processDefinitionIdAndVersion(String processDefinitionIdAndVersion) {
+ this.processDefinitionIdAndVersion = processDefinitionIdAndVersion;
+ return this;
+ }
+
+ @Override
+ public ProcessInstanceQuery parentInstanceId(String parentInstanceId) {
+ this.parentInstanceId = parentInstanceId;
+ return this;
+ }
+
+ @Override
+ public ProcessInstanceQuery bizUniqueId(String bizUniqueId) {
+ this.bizUniqueId = bizUniqueId;
+ return this;
+ }
+
+ @Override
+ public ProcessInstanceQuery bizUniqueId(boolean condition, String bizUniqueId) {
+ if (condition) {
+ this.bizUniqueId = bizUniqueId;
+ }
+ return this;
+ }
+
+ @Override
+ public ProcessInstanceQuery startedAfter(Date startTimeAfter) {
+ this.startTimeAfter = startTimeAfter;
+ return this;
+ }
+
+ @Override
+ public ProcessInstanceQuery startedBefore(Date startTimeBefore) {
+ this.startTimeBefore = startTimeBefore;
+ return this;
+ }
+
+ @Override
+ public ProcessInstanceQuery completedAfter(Date completeTimeStart) {
+ this.completeTimeStart = completeTimeStart;
+ return this;
+ }
+
+ @Override
+ public ProcessInstanceQuery completedBefore(Date completeTimeEnd) {
+ this.completeTimeEnd = completeTimeEnd;
+ return this;
+ }
+
+ @Override
+ public ProcessInstanceQuery processDefinitionTypeIn(List types) {
+ this.processDefinitionTypeList = types != null ? new ArrayList(types) : null;
+ return this;
+ }
+
+ @Override
+ public ProcessInstanceQuery involvedUser(String userId) {
+ this.startUserId = userId;
+ return this;
+ }
+
+ // ============ Ordering ============
+
+ @Override
+ public ProcessInstanceQuery orderByProcessInstanceId() {
+ return orderBy("id", "id");
+ }
+
+ @Override
+ public ProcessInstanceQuery orderByStartTime() {
+ return orderBy("gmtCreate", "gmt_create");
+ }
+
+ @Override
+ public ProcessInstanceQuery orderByModifyTime() {
+ return orderBy("gmtModified", "gmt_modified");
+ }
+
+ @Override
+ public ProcessInstanceQuery orderByCompleteTime() {
+ return orderBy("completeTime", "complete_time");
+ }
+
+ @Override
+ public ProcessInstanceQuery asc() {
+ return applyAsc();
+ }
+
+ @Override
+ public ProcessInstanceQuery desc() {
+ return applyDesc();
+ }
+
+ // ============ Execution ============
+
+ @Override
+ protected List executeList() {
+ ProcessInstanceQueryParam param = buildQueryParam();
+ return processInstanceStorage.queryProcessInstanceList(param, processEngineConfiguration);
+ }
+
+ @Override
+ protected long executeCount() {
+ ProcessInstanceQueryParam param = buildQueryParam();
+ Long count = processInstanceStorage.count(param, processEngineConfiguration);
+ return count != null ? count : 0L;
+ }
+
+ /**
+ * Build ProcessInstanceQueryParam from the fluent query settings.
+ */
+ private ProcessInstanceQueryParam buildQueryParam() {
+ ProcessInstanceQueryParam param = new ProcessInstanceQueryParam();
+
+ // Set ID filter
+ if (processInstanceId != null) {
+ List ids = new ArrayList<>();
+ ids.add(processInstanceId);
+ param.setProcessInstanceIdList(ids);
+ } else if (processInstanceIds != null && !processInstanceIds.isEmpty()) {
+ param.setProcessInstanceIdList(processInstanceIds);
+ }
+
+ // Set other filters
+ param.setStartUserId(startUserId);
+ param.setStatus(status);
+ param.setProcessDefinitionType(processDefinitionType);
+ param.setProcessDefinitionIdAndVersion(processDefinitionIdAndVersion);
+ param.setParentInstanceId(parentInstanceId);
+ param.setBizUniqueId(bizUniqueId);
+ param.setProcessStartTime(startTimeAfter);
+ param.setProcessEndTime(startTimeBefore);
+ param.setCompleteTimeStart(completeTimeStart);
+ param.setCompleteTimeEnd(completeTimeEnd);
+ // processDefinitionTypeList takes precedence over single type
+ if (processDefinitionTypeList != null && !processDefinitionTypeList.isEmpty()) {
+ param.setProcessDefinitionTypeList(processDefinitionTypeList);
+ }
+
+ // Set pagination
+ param.setPageOffset(pageOffset);
+ param.setPageSize(pageSize);
+ if (!excludeTenant) {
+ param.setTenantId(tenantId);
+ }
+
+ // Set order by specs
+ param.setOrderBySpecs(orderBySpecs);
+
+ return param;
+ }
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/query/impl/SupervisionQueryImpl.java b/core/src/main/java/com/alibaba/smart/framework/engine/query/impl/SupervisionQueryImpl.java
new file mode 100644
index 000000000..48ad63250
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/query/impl/SupervisionQueryImpl.java
@@ -0,0 +1,186 @@
+package com.alibaba.smart.framework.engine.query.impl;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import com.alibaba.smart.framework.engine.configuration.ProcessEngineConfiguration;
+import com.alibaba.smart.framework.engine.extension.constant.ExtensionConstant;
+import com.alibaba.smart.framework.engine.instance.storage.SupervisionInstanceStorage;
+import com.alibaba.smart.framework.engine.model.instance.SupervisionInstance;
+import com.alibaba.smart.framework.engine.query.SupervisionQuery;
+import com.alibaba.smart.framework.engine.service.param.query.SupervisionQueryParam;
+
+/**
+ * Implementation of SupervisionQuery fluent API.
+ *
+ * @author SmartEngine Team
+ */
+public class SupervisionQueryImpl extends AbstractProcessBoundQuery
+ implements SupervisionQuery {
+
+ private SupervisionInstanceStorage supervisionInstanceStorage;
+
+ // Filter conditions
+ private String supervisionId;
+ private String supervisorUserId;
+ private String taskInstanceId;
+ private List taskInstanceIds;
+ private String supervisionType;
+ private String status;
+ private Date supervisionStartTime;
+ private Date supervisionEndTime;
+
+ public SupervisionQueryImpl(ProcessEngineConfiguration processEngineConfiguration) {
+ super(processEngineConfiguration);
+ this.supervisionInstanceStorage = processEngineConfiguration.getAnnotationScanner()
+ .getExtensionPoint(ExtensionConstant.COMMON, SupervisionInstanceStorage.class);
+ }
+
+ // ============ Filter conditions ============
+
+ @Override
+ public SupervisionQuery supervisionId(String supervisionId) {
+ this.supervisionId = supervisionId;
+ return this;
+ }
+
+ @Override
+ public SupervisionQuery supervisorUserId(String supervisorUserId) {
+ this.supervisorUserId = supervisorUserId;
+ return this;
+ }
+
+ @Override
+ public SupervisionQuery supervisorUserId(boolean condition, String supervisorUserId) {
+ if (condition) {
+ this.supervisorUserId = supervisorUserId;
+ }
+ return this;
+ }
+
+ @Override
+ public SupervisionQuery taskInstanceId(String taskInstanceId) {
+ this.taskInstanceId = taskInstanceId;
+ return this;
+ }
+
+ @Override
+ public SupervisionQuery taskInstanceIdIn(List taskInstanceIds) {
+ this.taskInstanceIds = taskInstanceIds;
+ return this;
+ }
+
+ @Override
+ public SupervisionQuery supervisionType(String supervisionType) {
+ this.supervisionType = supervisionType;
+ return this;
+ }
+
+ @Override
+ public SupervisionQuery supervisionType(boolean condition, String supervisionType) {
+ if (condition) {
+ this.supervisionType = supervisionType;
+ }
+ return this;
+ }
+
+ @Override
+ public SupervisionQuery supervisionStatus(String status) {
+ this.status = status;
+ return this;
+ }
+
+ @Override
+ public SupervisionQuery supervisionStatus(boolean condition, String status) {
+ if (condition) {
+ this.status = status;
+ }
+ return this;
+ }
+
+ @Override
+ public SupervisionQuery supervisionTimeAfter(Date startTime) {
+ this.supervisionStartTime = startTime;
+ return this;
+ }
+
+ @Override
+ public SupervisionQuery supervisionTimeBefore(Date endTime) {
+ this.supervisionEndTime = endTime;
+ return this;
+ }
+
+ // ============ Ordering ============
+
+ @Override
+ public SupervisionQuery orderBySupervisionId() {
+ return orderBy("id", "id");
+ }
+
+ @Override
+ public SupervisionQuery orderByCloseTime() {
+ return orderBy("closeTime", "close_time");
+ }
+
+ // ============ Execution ============
+
+ @Override
+ protected List executeList() {
+ SupervisionQueryParam param = buildQueryParam();
+ return supervisionInstanceStorage.findSupervisionList(param, processEngineConfiguration);
+ }
+
+ @Override
+ protected long executeCount() {
+ SupervisionQueryParam param = buildQueryParam();
+ Long count = supervisionInstanceStorage.countSupervision(param, processEngineConfiguration);
+ return count != null ? count : 0L;
+ }
+
+ /**
+ * Build SupervisionQueryParam from the fluent query settings.
+ */
+ private SupervisionQueryParam buildQueryParam() {
+ SupervisionQueryParam param = new SupervisionQueryParam();
+
+ // Set ID filter
+ if (supervisionId != null) {
+ param.setId(Long.parseLong(supervisionId));
+ }
+
+ // Set supervisor filter
+ param.setSupervisorUserId(supervisorUserId);
+
+ // Set task instance filter
+ if (taskInstanceId != null) {
+ List ids = new ArrayList<>();
+ ids.add(taskInstanceId);
+ param.setTaskInstanceIdList(ids);
+ } else if (taskInstanceIds != null && !taskInstanceIds.isEmpty()) {
+ param.setTaskInstanceIdList(taskInstanceIds);
+ }
+
+ // Set process instance filter (from base class)
+ List processInstanceIdList = buildProcessInstanceIdList();
+ if (processInstanceIdList != null) {
+ param.setProcessInstanceIdList(processInstanceIdList);
+ }
+
+ // Set other filters
+ param.setSupervisionType(supervisionType);
+ param.setStatus(status);
+ param.setSupervisionStartTime(supervisionStartTime);
+ param.setSupervisionEndTime(supervisionEndTime);
+
+ // Set pagination
+ param.setPageOffset(pageOffset);
+ param.setPageSize(pageSize);
+ param.setTenantId(tenantId);
+
+ // Set order by specs
+ param.setOrderBySpecs(orderBySpecs);
+
+ return param;
+ }
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/query/impl/TaskQueryImpl.java b/core/src/main/java/com/alibaba/smart/framework/engine/query/impl/TaskQueryImpl.java
new file mode 100644
index 000000000..a090aec0d
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/query/impl/TaskQueryImpl.java
@@ -0,0 +1,589 @@
+package com.alibaba.smart.framework.engine.query.impl;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import com.alibaba.smart.framework.engine.configuration.ProcessEngineConfiguration;
+import com.alibaba.smart.framework.engine.dialect.Dialect;
+import com.alibaba.smart.framework.engine.dialect.DialectRegistry;
+import com.alibaba.smart.framework.engine.extension.constant.ExtensionConstant;
+import com.alibaba.smart.framework.engine.instance.storage.TaskInstanceStorage;
+import com.alibaba.smart.framework.engine.model.instance.TaskInstance;
+import com.alibaba.smart.framework.engine.query.TaskQuery;
+import com.alibaba.smart.framework.engine.service.param.query.JsonCondition;
+import com.alibaba.smart.framework.engine.service.param.query.JsonInCondition;
+import com.alibaba.smart.framework.engine.service.param.query.TaskInstanceQueryByAssigneeParam;
+import com.alibaba.smart.framework.engine.service.param.query.TaskInstanceQueryParam;
+
+/**
+ * Implementation of TaskQuery fluent API.
+ *
+ * @author SmartEngine Team
+ */
+public class TaskQueryImpl extends AbstractProcessBoundQuery implements TaskQuery {
+
+ private TaskInstanceStorage taskInstanceStorage;
+
+ // Filter conditions
+ private String taskInstanceId;
+ private String activityInstanceId;
+ private String processDefinitionType;
+ private String processDefinitionActivityId;
+ private String status;
+ private List statusList;
+ private String claimUserId;
+ private String tag;
+ private String extension;
+ private Integer priority;
+ private String comment;
+ private String title;
+ private Date completeTimeStart;
+ private Date completeTimeEnd;
+ private Date createTimeStart;
+ private Date createTimeEnd;
+ private String titleLike;
+ private boolean unassigned;
+ private List processDefinitionTypeList;
+ private String claimUserIdLike;
+ private Integer minPriority;
+ private Integer maxPriority;
+ private String domainCode;
+ private List domainCodeList;
+ private String domainCodeLike;
+ private Map jsonExactFilters = new LinkedHashMap();
+ private Map> jsonInFilters = new LinkedHashMap>();
+ private Map jsonLikeFilters = new LinkedHashMap();
+
+ private static final Pattern VALID_JSON_KEY = Pattern.compile("^[a-zA-Z_][a-zA-Z0-9_.]*$");
+
+ // Candidate assignee filters (triggers assignee JOIN query path)
+ private String candidateUserId;
+ private List candidateGroupIds;
+
+ public TaskQueryImpl(ProcessEngineConfiguration processEngineConfiguration) {
+ super(processEngineConfiguration);
+ this.taskInstanceStorage = processEngineConfiguration.getAnnotationScanner()
+ .getExtensionPoint(ExtensionConstant.COMMON, TaskInstanceStorage.class);
+ }
+
+ // ============ Filter conditions ============
+
+ @Override
+ public TaskQuery taskInstanceId(String taskInstanceId) {
+ this.taskInstanceId = taskInstanceId;
+ return this;
+ }
+
+ @Override
+ public TaskQuery activityInstanceId(String activityInstanceId) {
+ this.activityInstanceId = activityInstanceId;
+ return this;
+ }
+
+ @Override
+ public TaskQuery processDefinitionType(String processDefinitionType) {
+ this.processDefinitionType = processDefinitionType;
+ return this;
+ }
+
+ @Override
+ public TaskQuery processDefinitionType(boolean condition, String processDefinitionType) {
+ if (condition) {
+ this.processDefinitionType = processDefinitionType;
+ }
+ return this;
+ }
+
+ @Override
+ public TaskQuery processDefinitionActivityId(String processDefinitionActivityId) {
+ this.processDefinitionActivityId = processDefinitionActivityId;
+ return this;
+ }
+
+ @Override
+ public TaskQuery taskStatus(String status) {
+ this.status = status;
+ return this;
+ }
+
+ @Override
+ public TaskQuery taskStatus(boolean condition, String status) {
+ if (condition) {
+ this.status = status;
+ }
+ return this;
+ }
+
+ @Override
+ public TaskQuery taskStatusIn(List statuses) {
+ this.statusList = statuses;
+ return this;
+ }
+
+ @Override
+ public TaskQuery taskStatusIn(String... statuses) {
+ this.statusList = Arrays.asList(statuses);
+ return this;
+ }
+
+ @Override
+ public TaskQuery taskAssignee(String claimUserId) {
+ this.claimUserId = claimUserId;
+ return this;
+ }
+
+ @Override
+ public TaskQuery taskAssignee(boolean condition, String claimUserId) {
+ if (condition) {
+ this.claimUserId = claimUserId;
+ }
+ return this;
+ }
+
+ @Override
+ public TaskQuery taskCandidateUser(String userId) {
+ this.candidateUserId = userId;
+ return this;
+ }
+
+ @Override
+ public TaskQuery taskCandidateGroup(String groupId) {
+ this.candidateGroupIds = Collections.singletonList(groupId);
+ return this;
+ }
+
+ @Override
+ public TaskQuery taskCandidateGroupIn(List groupIds) {
+ this.candidateGroupIds = groupIds;
+ return this;
+ }
+
+ @Override
+ public TaskQuery taskCandidateOrGroup(String userId, List groupIds) {
+ this.candidateUserId = userId;
+ this.candidateGroupIds = groupIds;
+ return this;
+ }
+
+ @Override
+ public TaskQuery taskTag(String tag) {
+ this.tag = tag;
+ return this;
+ }
+
+ @Override
+ public TaskQuery taskTag(boolean condition, String tag) {
+ if (condition) {
+ this.tag = tag;
+ }
+ return this;
+ }
+
+ @Override
+ public TaskQuery taskExtension(String extension) {
+ this.extension = extension;
+ return this;
+ }
+
+ @Override
+ public TaskQuery taskPriority(Integer priority) {
+ this.priority = priority;
+ return this;
+ }
+
+ @Override
+ public TaskQuery taskComment(String comment) {
+ this.comment = comment;
+ return this;
+ }
+
+ @Override
+ public TaskQuery taskTitle(String title) {
+ this.title = title;
+ return this;
+ }
+
+ @Override
+ public TaskQuery taskTitle(boolean condition, String title) {
+ if (condition) {
+ this.title = title;
+ }
+ return this;
+ }
+
+ @Override
+ public TaskQuery completeTimeAfter(Date completeTimeStart) {
+ this.completeTimeStart = completeTimeStart;
+ return this;
+ }
+
+ @Override
+ public TaskQuery completeTimeBefore(Date completeTimeEnd) {
+ this.completeTimeEnd = completeTimeEnd;
+ return this;
+ }
+
+ @Override
+ public TaskQuery createdAfter(Date createTimeStart) {
+ this.createTimeStart = createTimeStart;
+ return this;
+ }
+
+ @Override
+ public TaskQuery createdBefore(Date createTimeEnd) {
+ this.createTimeEnd = createTimeEnd;
+ return this;
+ }
+
+ @Override
+ public TaskQuery taskTitleLike(String titleLike) {
+ this.titleLike = titleLike;
+ return this;
+ }
+
+ @Override
+ public TaskQuery taskTitleLike(boolean condition, String titleLike) {
+ if (condition) {
+ this.titleLike = titleLike;
+ }
+ return this;
+ }
+
+ @Override
+ public TaskQuery taskUnassigned() {
+ this.unassigned = true;
+ return this;
+ }
+
+ @Override
+ public TaskQuery processDefinitionTypeIn(List types) {
+ this.processDefinitionTypeList = types != null ? new ArrayList(types) : null;
+ return this;
+ }
+
+ @Override
+ public TaskQuery taskAssigneeLike(String claimUserIdLike) {
+ this.claimUserIdLike = claimUserIdLike;
+ return this;
+ }
+
+ @Override
+ public TaskQuery taskMinPriority(Integer minPriority) {
+ this.minPriority = minPriority;
+ return this;
+ }
+
+ @Override
+ public TaskQuery taskMaxPriority(Integer maxPriority) {
+ this.maxPriority = maxPriority;
+ return this;
+ }
+
+ // ============ Domain code filters ============
+
+ @Override
+ public TaskQuery domainCode(String domainCode) {
+ this.domainCode = domainCode;
+ return this;
+ }
+
+ @Override
+ public TaskQuery domainCode(boolean condition, String domainCode) {
+ if (condition) {
+ this.domainCode = domainCode;
+ }
+ return this;
+ }
+
+ @Override
+ public TaskQuery domainCodeIn(List domainCodes) {
+ this.domainCodeList = domainCodes != null ? new ArrayList(domainCodes) : null;
+ return this;
+ }
+
+ @Override
+ public TaskQuery domainCodeLike(String domainCodeLike) {
+ this.domainCodeLike = domainCodeLike;
+ return this;
+ }
+
+ // ============ Extra JSON filters ============
+
+ @Override
+ public TaskQuery extraJson(String key, String value) {
+ validateJsonKey(key);
+ jsonExactFilters.put(key, value);
+ return this;
+ }
+
+ @Override
+ public TaskQuery extraJsonIn(String key, List values) {
+ validateJsonKey(key);
+ jsonInFilters.put(key, values);
+ return this;
+ }
+
+ @Override
+ public TaskQuery extraJsonLike(String key, String pattern) {
+ validateJsonKey(key);
+ jsonLikeFilters.put(key, pattern);
+ return this;
+ }
+
+ private void validateJsonKey(String key) {
+ if (key == null || !VALID_JSON_KEY.matcher(key).matches()) {
+ throw new IllegalArgumentException("Invalid JSON key: " + key
+ + ". Only [a-zA-Z_][a-zA-Z0-9_.]* is allowed.");
+ }
+ }
+
+ // ============ Ordering ============
+
+ @Override
+ public TaskQuery orderByTaskId() {
+ return orderBy("id", "id");
+ }
+
+ @Override
+ public TaskQuery orderByClaimTime() {
+ return orderBy("claimTime", "claim_time");
+ }
+
+ @Override
+ public TaskQuery orderByCompleteTime() {
+ return orderBy("completeTime", "complete_time");
+ }
+
+ @Override
+ public TaskQuery orderByPriority() {
+ return orderBy("priority", "priority");
+ }
+
+ // ============ Execution ============
+
+ /**
+ * Check if candidate assignee filters are set, which requires the assignee JOIN query path.
+ */
+ private boolean isCandidateQuery() {
+ return candidateUserId != null || (candidateGroupIds != null && !candidateGroupIds.isEmpty());
+ }
+
+ @Override
+ protected List executeList() {
+ if (isCandidateQuery()) {
+ TaskInstanceQueryByAssigneeParam param = buildAssigneeQueryParam();
+ return taskInstanceStorage.findTaskListByAssignee(param, processEngineConfiguration);
+ }
+ TaskInstanceQueryParam param = buildQueryParam();
+ return taskInstanceStorage.findTaskList(param, processEngineConfiguration);
+ }
+
+ @Override
+ protected long executeCount() {
+ if (isCandidateQuery()) {
+ TaskInstanceQueryByAssigneeParam param = buildAssigneeQueryParam();
+ Long count = taskInstanceStorage.countTaskListByAssignee(param, processEngineConfiguration);
+ return count != null ? count : 0L;
+ }
+ TaskInstanceQueryParam param = buildQueryParam();
+ Long count = taskInstanceStorage.count(param, processEngineConfiguration);
+ return count != null ? count : 0L;
+ }
+
+ /**
+ * Build TaskInstanceQueryByAssigneeParam for the assignee JOIN query path.
+ */
+ private TaskInstanceQueryByAssigneeParam buildAssigneeQueryParam() {
+ TaskInstanceQueryByAssigneeParam param = new TaskInstanceQueryByAssigneeParam();
+
+ param.setAssigneeUserId(candidateUserId);
+ // Treat empty group list as null to avoid empty IN() SQL syntax error
+ param.setAssigneeGroupIdList(
+ candidateGroupIds != null && !candidateGroupIds.isEmpty() ? candidateGroupIds : null);
+ param.setProcessDefinitionType(processDefinitionType);
+
+ // Set process instance ID list
+ List processInstanceIdList = buildProcessInstanceIdList();
+ if (processInstanceIdList != null) {
+ List longIds = new ArrayList(processInstanceIdList.size());
+ for (String id : processInstanceIdList) {
+ longIds.add(Long.parseLong(id));
+ }
+ param.setProcessInstanceIdList(longIds);
+ }
+
+ // statusList takes precedence, else single status
+ if (statusList != null && !statusList.isEmpty()) {
+ param.setStatus(statusList.get(0));
+ } else {
+ param.setStatus(status);
+ }
+
+ // domain_code filters
+ param.setDomainCode(domainCode);
+ param.setDomainCodeList(domainCodeList);
+ param.setDomainCodeLike(domainCodeLike);
+
+ // Build JSON conditions
+ buildJsonConditionsForAssignee(param);
+
+ // Set pagination and tenant
+ param.setPageOffset(pageOffset);
+ param.setPageSize(pageSize);
+ if (!excludeTenant) {
+ param.setTenantId(tenantId);
+ }
+
+ return param;
+ }
+
+ /**
+ * Build TaskInstanceQueryParam from the fluent query settings.
+ */
+ private TaskInstanceQueryParam buildQueryParam() {
+ TaskInstanceQueryParam param = new TaskInstanceQueryParam();
+
+ // Set ID filter
+ if (taskInstanceId != null) {
+ param.setId(Long.parseLong(taskInstanceId));
+ }
+
+ // Set process instance filter (from base class)
+ List processInstanceIdList = buildProcessInstanceIdList();
+ if (processInstanceIdList != null) {
+ param.setProcessInstanceIdList(processInstanceIdList);
+ }
+
+ // Set other filters
+ param.setActivityInstanceId(activityInstanceId);
+ param.setProcessDefinitionType(processDefinitionType);
+ param.setProcessDefinitionActivityId(processDefinitionActivityId);
+ // statusList takes precedence over single status
+ if (statusList != null && !statusList.isEmpty()) {
+ param.setStatusList(statusList);
+ } else {
+ param.setStatus(status);
+ }
+ param.setClaimUserId(claimUserId);
+ param.setTag(tag);
+ param.setExtension(extension);
+ param.setPriority(priority);
+ param.setComment(comment);
+ param.setTitle(title);
+ param.setTitleLike(titleLike);
+ param.setCompleteTimeStart(completeTimeStart);
+ param.setCompleteTimeEnd(completeTimeEnd);
+ param.setCreateTimeStart(createTimeStart);
+ param.setCreateTimeEnd(createTimeEnd);
+ param.setUnassigned(unassigned ? Boolean.TRUE : null);
+ param.setClaimUserIdLike(claimUserIdLike);
+ param.setMinPriority(minPriority);
+ param.setMaxPriority(maxPriority);
+ // processDefinitionTypeList takes precedence over single type
+ if (processDefinitionTypeList != null && !processDefinitionTypeList.isEmpty()) {
+ param.setProcessDefinitionTypeList(processDefinitionTypeList);
+ }
+
+ // domain_code filters
+ param.setDomainCode(domainCode);
+ param.setDomainCodeList(domainCodeList);
+ param.setDomainCodeLike(domainCodeLike);
+
+ // Build JSON conditions
+ buildJsonConditions(param);
+
+ // Set pagination
+ param.setPageOffset(pageOffset);
+ param.setPageSize(pageSize);
+ if (!excludeTenant) {
+ param.setTenantId(tenantId);
+ }
+
+ // Set order by specs
+ param.setOrderBySpecs(orderBySpecs);
+
+ return param;
+ }
+
+ private Dialect resolveDialect() {
+ Dialect dialect = processEngineConfiguration.getDialect();
+ if (dialect == null) {
+ dialect = DialectRegistry.getInstance().getDefaultDialect();
+ }
+ return dialect;
+ }
+
+ private void buildJsonConditions(TaskInstanceQueryParam param) {
+ if (jsonExactFilters.isEmpty() && jsonInFilters.isEmpty() && jsonLikeFilters.isEmpty()) {
+ return;
+ }
+ Dialect dialect = resolveDialect();
+
+ if (!jsonExactFilters.isEmpty()) {
+ List conditions = new ArrayList();
+ for (Map.Entry e : jsonExactFilters.entrySet()) {
+ String expr = dialect.jsonExtractText("task.extra", e.getKey());
+ conditions.add(new JsonCondition(expr, e.getValue()));
+ }
+ param.setJsonConditions(conditions);
+ }
+
+ if (!jsonInFilters.isEmpty()) {
+ List inConditions = new ArrayList();
+ for (Map.Entry> e : jsonInFilters.entrySet()) {
+ String expr = dialect.jsonExtractText("task.extra", e.getKey());
+ inConditions.add(new JsonInCondition(expr, e.getValue()));
+ }
+ param.setJsonInConditions(inConditions);
+ }
+
+ if (!jsonLikeFilters.isEmpty()) {
+ List likeConditions = new ArrayList();
+ for (Map.Entry e : jsonLikeFilters.entrySet()) {
+ String expr = dialect.jsonExtractText("task.extra", e.getKey());
+ likeConditions.add(new JsonCondition(expr, e.getValue()));
+ }
+ param.setJsonLikeConditions(likeConditions);
+ }
+ }
+
+ private void buildJsonConditionsForAssignee(TaskInstanceQueryByAssigneeParam param) {
+ if (jsonExactFilters.isEmpty() && jsonInFilters.isEmpty() && jsonLikeFilters.isEmpty()) {
+ return;
+ }
+ Dialect dialect = resolveDialect();
+
+ if (!jsonExactFilters.isEmpty()) {
+ List conditions = new ArrayList();
+ for (Map.Entry e : jsonExactFilters.entrySet()) {
+ String expr = dialect.jsonExtractText("task.extra", e.getKey());
+ conditions.add(new JsonCondition(expr, e.getValue()));
+ }
+ param.setJsonConditions(conditions);
+ }
+
+ if (!jsonInFilters.isEmpty()) {
+ List inConditions = new ArrayList();
+ for (Map.Entry> e : jsonInFilters.entrySet()) {
+ String expr = dialect.jsonExtractText("task.extra", e.getKey());
+ inConditions.add(new JsonInCondition(expr, e.getValue()));
+ }
+ param.setJsonInConditions(inConditions);
+ }
+
+ if (!jsonLikeFilters.isEmpty()) {
+ List likeConditions = new ArrayList();
+ for (Map.Entry e : jsonLikeFilters.entrySet()) {
+ String expr = dialect.jsonExtractText("task.extra", e.getKey());
+ likeConditions.add(new JsonCondition(expr, e.getValue()));
+ }
+ param.setJsonLikeConditions(likeConditions);
+ }
+ }
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/service/command/NotificationCommandService.java b/core/src/main/java/com/alibaba/smart/framework/engine/service/command/NotificationCommandService.java
new file mode 100644
index 000000000..b909207ce
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/service/command/NotificationCommandService.java
@@ -0,0 +1,63 @@
+package com.alibaba.smart.framework.engine.service.command;
+
+import java.util.List;
+
+import com.alibaba.smart.framework.engine.model.instance.NotificationInstance;
+
+/**
+ * 知会抄送命令服务接口
+ *
+ * @author SmartEngine Team
+ */
+public interface NotificationCommandService {
+
+ /**
+ * 发送知会通知
+ *
+ * @param processInstanceId 流程实例ID
+ * @param taskInstanceId 任务实例ID(可选)
+ * @param senderUserId 发送人用户ID
+ * @param receiverUserIds 接收人用户ID列表
+ * @param title 通知标题
+ * @param content 通知内容
+ * @param tenantId 租户ID
+ * @return 知会通知实例列表
+ */
+ List sendNotification(String processInstanceId, String taskInstanceId,
+ String senderUserId, List receiverUserIds,
+ String title, String content, String tenantId);
+
+ /**
+ * 发送单个知会通知
+ *
+ * @param processInstanceId 流程实例ID
+ * @param taskInstanceId 任务实例ID(可选)
+ * @param senderUserId 发送人用户ID
+ * @param receiverUserId 接收人用户ID
+ * @param title 通知标题
+ * @param content 通知内容
+ * @param notificationType 通知类型
+ * @param tenantId 租户ID
+ * @return 知会通知实例
+ */
+ NotificationInstance sendSingleNotification(String processInstanceId, String taskInstanceId,
+ String senderUserId, String receiverUserId,
+ String title, String content, String notificationType,
+ String tenantId);
+
+ /**
+ * 标记为已读
+ *
+ * @param notificationId 通知ID
+ * @param tenantId 租户ID
+ */
+ void markAsRead(String notificationId, String tenantId);
+
+ /**
+ * 批量标记为已读
+ *
+ * @param notificationIds 通知ID列表
+ * @param tenantId 租户ID
+ */
+ void batchMarkAsRead(List notificationIds, String tenantId);
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/service/command/ProcessCommandService.java b/core/src/main/java/com/alibaba/smart/framework/engine/service/command/ProcessCommandService.java
index 0c322596a..9cb298be0 100644
--- a/core/src/main/java/com/alibaba/smart/framework/engine/service/command/ProcessCommandService.java
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/service/command/ProcessCommandService.java
@@ -45,4 +45,21 @@ public interface ProcessCommandService {
void abort(String processInstanceId, Map request);
+ /**
+ * Suspend a running process instance. Sets status to suspended without terminating
+ * active executions or tasks.
+ */
+ void suspend(String processInstanceId, String tenantId);
+
+ /**
+ * Resume a suspended process instance. Restores status to running.
+ */
+ void resume(String processInstanceId, String tenantId);
+
+ /**
+ * Jump to a specific node in the process instance.
+ * Aborts current active executions/tasks and creates a new execution at the target node.
+ */
+ void jump(String processInstanceId, String targetNodeId, Map variables);
+
}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/service/command/SupervisionCommandService.java b/core/src/main/java/com/alibaba/smart/framework/engine/service/command/SupervisionCommandService.java
new file mode 100644
index 000000000..3388566f7
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/service/command/SupervisionCommandService.java
@@ -0,0 +1,56 @@
+package com.alibaba.smart.framework.engine.service.command;
+
+import java.util.List;
+
+import com.alibaba.smart.framework.engine.model.instance.SupervisionInstance;
+
+/**
+ * 督办管理命令服务接口
+ *
+ * @author SmartEngine Team
+ */
+public interface SupervisionCommandService {
+
+ /**
+ * 发起督办
+ *
+ * @param taskInstanceId 任务实例ID
+ * @param supervisorUserId 督办人用户ID
+ * @param reason 督办原因
+ * @param supervisionType 督办类型
+ * @param tenantId 租户ID
+ * @return 督办实例
+ */
+ SupervisionInstance createSupervision(String taskInstanceId, String supervisorUserId,
+ String reason, String supervisionType, String tenantId);
+
+ /**
+ * 关闭督办
+ *
+ * @param supervisionId 督办ID
+ * @param tenantId 租户ID
+ */
+ void closeSupervision(String supervisionId, String tenantId);
+
+ /**
+ * 批量督办
+ *
+ * @param taskInstanceIds 任务实例ID列表
+ * @param supervisorUserId 督办人用户ID
+ * @param reason 督办原因
+ * @param supervisionType 督办类型
+ * @param tenantId 租户ID
+ * @return 督办实例列表
+ */
+ List batchCreateSupervision(List taskInstanceIds,
+ String supervisorUserId, String reason,
+ String supervisionType, String tenantId);
+
+ /**
+ * 根据任务完成自动关闭督办
+ *
+ * @param taskInstanceId 任务实例ID
+ * @param tenantId 租户ID
+ */
+ void autoCloseSupervisionByTask(String taskInstanceId, String tenantId);
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/service/command/TaskCommandService.java b/core/src/main/java/com/alibaba/smart/framework/engine/service/command/TaskCommandService.java
index 90244136d..a3f767c98 100644
--- a/core/src/main/java/com/alibaba/smart/framework/engine/service/command/TaskCommandService.java
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/service/command/TaskCommandService.java
@@ -17,6 +17,11 @@ public interface TaskCommandService {
ProcessInstance complete(String taskId, Map request);
+ /**
+ * Claim a task by setting the claimUserId. Only unclaimed tasks can be claimed.
+ */
+ void claim(String taskId, String userId, String tenantId);
+
ProcessInstance complete(String taskId, String userId, Map request);
ProcessInstance complete(String taskId, Map request, Map response);
@@ -47,4 +52,24 @@ public interface TaskCommandService {
*/
void addTaskAssigneeCandidate(String taskId,String tenantId, TaskAssigneeCandidateInstance taskAssigneeCandidateInstance);
+ /**
+ * 增强的任务移交,支持原因和时限
+ */
+ void transferWithReason(String taskId, String fromUserId, String toUserId, String reason, String tenantId);
+
+ /**
+ * 任务回退到指定节点
+ */
+ ProcessInstance rollbackTask(String taskId, String targetActivityId, String reason, String tenantId);
+
+ /**
+ * 增强的加签操作,支持操作记录
+ */
+ void addTaskAssigneeCandidateWithReason(String taskId, String tenantId, TaskAssigneeCandidateInstance taskAssigneeCandidateInstance, String reason);
+
+ /**
+ * 增强的减签操作,支持操作记录
+ */
+ void removeTaskAssigneeCandidateWithReason(String taskId, String tenantId, TaskAssigneeCandidateInstance taskAssigneeCandidateInstance, String reason);
+
}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/service/command/impl/DefaultNotificationCommandService.java b/core/src/main/java/com/alibaba/smart/framework/engine/service/command/impl/DefaultNotificationCommandService.java
new file mode 100644
index 000000000..06a2bb282
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/service/command/impl/DefaultNotificationCommandService.java
@@ -0,0 +1,128 @@
+package com.alibaba.smart.framework.engine.service.command.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.smart.framework.engine.configuration.ProcessEngineConfiguration;
+import com.alibaba.smart.framework.engine.configuration.aware.ProcessEngineConfigurationAware;
+import com.alibaba.smart.framework.engine.configuration.scanner.AnnotationScanner;
+import com.alibaba.smart.framework.engine.constant.NotificationConstant;
+import com.alibaba.smart.framework.engine.exception.NotificationException;
+import com.alibaba.smart.framework.engine.exception.ValidationException;
+import com.alibaba.smart.framework.engine.extension.annotation.ExtensionBinding;
+import com.alibaba.smart.framework.engine.extension.constant.ExtensionConstant;
+import com.alibaba.smart.framework.engine.hook.LifeCycleHook;
+import com.alibaba.smart.framework.engine.instance.impl.DefaultNotificationInstance;
+import com.alibaba.smart.framework.engine.instance.storage.NotificationInstanceStorage;
+import com.alibaba.smart.framework.engine.model.instance.NotificationInstance;
+import com.alibaba.smart.framework.engine.service.command.NotificationCommandService;
+
+/**
+ * 知会通知命令服务默认实现
+ *
+ * @author SmartEngine Team
+ */
+@ExtensionBinding(group = ExtensionConstant.SERVICE, bindKey = NotificationCommandService.class)
+public class DefaultNotificationCommandService implements NotificationCommandService, LifeCycleHook, ProcessEngineConfigurationAware {
+
+ private ProcessEngineConfiguration processEngineConfiguration;
+ private NotificationInstanceStorage notificationInstanceStorage;
+
+ @Override
+ public void start() {
+ AnnotationScanner annotationScanner = this.processEngineConfiguration.getAnnotationScanner();
+ this.notificationInstanceStorage = annotationScanner.getExtensionPoint(ExtensionConstant.COMMON, NotificationInstanceStorage.class);
+ }
+
+ @Override
+ public void stop() {
+ // 清理资源
+ }
+
+ @Override
+ public List sendNotification(String processInstanceId, String taskInstanceId,
+ String senderUserId, List receiverUserIds,
+ String title, String content, String tenantId) {
+ if (processInstanceId == null || senderUserId == null || receiverUserIds == null || receiverUserIds.isEmpty()) {
+ throw new ValidationException("ProcessInstanceId, SenderUserId and ReceiverUserIds cannot be null or empty");
+ }
+
+ List results = new ArrayList<>();
+ for (String receiverUserId : receiverUserIds) {
+ try {
+ NotificationInstance notification = sendSingleNotification(processInstanceId, taskInstanceId,
+ senderUserId, receiverUserId, title, content, NotificationConstant.NotificationType.CC, tenantId);
+ results.add(notification);
+ } catch (Exception e) {
+ throw new NotificationException("Failed to send notification to user: " + receiverUserId, e);
+ }
+ }
+ return results;
+ }
+
+ @Override
+ public NotificationInstance sendSingleNotification(String processInstanceId, String taskInstanceId,
+ String senderUserId, String receiverUserId,
+ String title, String content, String notificationType,
+ String tenantId) {
+ if (processInstanceId == null || senderUserId == null || receiverUserId == null) {
+ throw new ValidationException("ProcessInstanceId, SenderUserId and ReceiverUserId cannot be null");
+ }
+
+ if (notificationType == null || (!NotificationConstant.NotificationType.CC.equals(notificationType)
+ && !NotificationConstant.NotificationType.INFORM.equals(notificationType))) {
+ throw new ValidationException("Invalid notification type: " + notificationType);
+ }
+
+ try {
+ NotificationInstance notificationInstance = new DefaultNotificationInstance();
+ notificationInstance.setProcessInstanceId(processInstanceId);
+ notificationInstance.setTaskInstanceId(taskInstanceId);
+ notificationInstance.setSenderUserId(senderUserId);
+ notificationInstance.setReceiverUserId(receiverUserId);
+ notificationInstance.setNotificationType(notificationType);
+ notificationInstance.setTitle(title);
+ notificationInstance.setContent(content);
+ notificationInstance.setReadStatus(NotificationConstant.ReadStatus.UNREAD);
+ notificationInstance.setTenantId(tenantId);
+
+ // 设置ID生成器
+ processEngineConfiguration.getIdGenerator().generate(notificationInstance);
+
+ return notificationInstanceStorage.insert(notificationInstance, processEngineConfiguration);
+ } catch (Exception e) {
+ throw new NotificationException("Failed to send notification", e);
+ }
+ }
+
+ @Override
+ public void markAsRead(String notificationId, String tenantId) {
+ if (notificationId == null) {
+ throw new ValidationException("NotificationId cannot be null");
+ }
+
+ try {
+ notificationInstanceStorage.markAsRead(notificationId, tenantId, processEngineConfiguration);
+ } catch (Exception e) {
+ throw new NotificationException("Failed to mark notification as read: " + notificationId, e);
+ }
+ }
+
+ @Override
+ public void batchMarkAsRead(List notificationIds, String tenantId) {
+ if (notificationIds == null || notificationIds.isEmpty()) {
+ throw new ValidationException("NotificationIds cannot be null or empty");
+ }
+
+ try {
+ notificationInstanceStorage.batchMarkAsRead(notificationIds, tenantId, processEngineConfiguration);
+ } catch (Exception e) {
+ throw new NotificationException("Failed to batch mark notifications as read", e);
+ }
+ }
+
+ @Override
+ public void setProcessEngineConfiguration(ProcessEngineConfiguration processEngineConfiguration) {
+ this.processEngineConfiguration = processEngineConfiguration;
+ }
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/service/command/impl/DefaultProcessCommandService.java b/core/src/main/java/com/alibaba/smart/framework/engine/service/command/impl/DefaultProcessCommandService.java
index 44ef5789f..02c2fc9a1 100644
--- a/core/src/main/java/com/alibaba/smart/framework/engine/service/command/impl/DefaultProcessCommandService.java
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/service/command/impl/DefaultProcessCommandService.java
@@ -27,6 +27,8 @@
import com.alibaba.smart.framework.engine.model.instance.InstanceStatus;
import com.alibaba.smart.framework.engine.model.instance.ProcessInstance;
import com.alibaba.smart.framework.engine.model.instance.TaskInstance;
+import com.alibaba.smart.framework.engine.pvm.PvmActivity;
+import com.alibaba.smart.framework.engine.pvm.PvmProcessDefinition;
import com.alibaba.smart.framework.engine.pvm.PvmProcessInstance;
import com.alibaba.smart.framework.engine.pvm.impl.DefaultPvmProcessInstance;
import com.alibaba.smart.framework.engine.service.command.ProcessCommandService;
@@ -229,6 +231,96 @@ public void abort(String processInstanceId, Map request) {
}
+ @Override
+ public void suspend(String processInstanceId, String tenantId) {
+ ProcessInstance processInstance = processInstanceStorage.findOne(processInstanceId, tenantId, processEngineConfiguration);
+ if (processInstance == null) {
+ throw new IllegalArgumentException("ProcessInstance not found: " + processInstanceId);
+ }
+ if (processInstance.getStatus() != InstanceStatus.running) {
+ throw new IllegalStateException("Can only suspend a running process instance, current status: " + processInstance.getStatus());
+ }
+ processInstance.setStatus(InstanceStatus.suspended);
+ processInstanceStorage.update(processInstance, processEngineConfiguration);
+ }
+
+ @Override
+ public void resume(String processInstanceId, String tenantId) {
+ ProcessInstance processInstance = processInstanceStorage.findOne(processInstanceId, tenantId, processEngineConfiguration);
+ if (processInstance == null) {
+ throw new IllegalArgumentException("ProcessInstance not found: " + processInstanceId);
+ }
+ if (processInstance.getStatus() != InstanceStatus.suspended) {
+ throw new IllegalStateException("Can only resume a suspended process instance, current status: " + processInstance.getStatus());
+ }
+ processInstance.setStatus(InstanceStatus.running);
+ processInstanceStorage.update(processInstance, processEngineConfiguration);
+ }
+
+ @Override
+ public void jump(String processInstanceId, String targetNodeId, Map variables) {
+ String tenantId = null;
+ if (variables != null) {
+ tenantId = ObjectUtil.obj2Str(variables.get(RequestMapSpecialKeyConstant.TENANT_ID));
+ }
+
+ ProcessInstance processInstance = processInstanceStorage.findOne(processInstanceId, tenantId, processEngineConfiguration);
+ if (processInstance == null) {
+ throw new IllegalArgumentException("ProcessInstance not found: " + processInstanceId);
+ }
+
+ // 1. Cancel all active executions
+ List activeExecutions = executionInstanceStorage.findActiveExecution(
+ processInstanceId, tenantId, processEngineConfiguration);
+ if (activeExecutions != null) {
+ for (ExecutionInstance exec : activeExecutions) {
+ MarkDoneUtil.markDoneExecutionInstance(exec, executionInstanceStorage, processEngineConfiguration);
+ }
+ }
+
+ // 2. Cancel all pending tasks
+ TaskInstanceQueryParam taskQueryParam = new TaskInstanceQueryParam();
+ List processInstanceIdList = new ArrayList<>(2);
+ processInstanceIdList.add(processInstanceId);
+ taskQueryParam.setProcessInstanceIdList(processInstanceIdList);
+ List taskInstances = taskInstanceStorage.findTaskList(taskQueryParam, processEngineConfiguration);
+ if (taskInstances != null) {
+ for (TaskInstance task : taskInstances) {
+ if (TaskInstanceConstant.COMPLETED.equals(task.getStatus())
+ || TaskInstanceConstant.CANCELED.equals(task.getStatus())
+ || TaskInstanceConstant.ABORTED.equals(task.getStatus())) {
+ continue;
+ }
+ MarkDoneUtil.markDoneTaskInstance(task, TaskInstanceConstant.CANCELED, TaskInstanceConstant.PENDING,
+ variables, taskInstanceStorage, processEngineConfiguration);
+ }
+ }
+
+ // 3. Resolve target PvmActivity and jump to it
+ PvmProcessDefinition pvmProcessDefinition = processDefinitionContainer.getPvmProcessDefinition(
+ processInstance.getProcessDefinitionId(), processInstance.getProcessDefinitionVersion(), tenantId);
+ if (pvmProcessDefinition == null) {
+ throw new IllegalArgumentException("PvmProcessDefinition not found for process: "
+ + processInstance.getProcessDefinitionId());
+ }
+
+ Map activities = pvmProcessDefinition.getActivities();
+ PvmActivity targetActivity = activities.get(targetNodeId);
+ if (targetActivity == null) {
+ throw new IllegalArgumentException("Target node not found in process definition: " + targetNodeId);
+ }
+
+ if (variables == null) {
+ variables = new HashMap<>();
+ }
+
+ ExecutionContext executionContext = instanceContextFactory.createProcessContext(
+ processEngineConfiguration, processInstance, variables, null, null);
+
+ PvmProcessInstance pvmProcessInstance = new DefaultPvmProcessInstance();
+ pvmProcessInstance.jump(targetActivity, executionContext);
+ }
+
private ProcessEngineConfiguration processEngineConfiguration;
private ProcessInstanceStorage processInstanceStorage;
private TaskInstanceStorage taskInstanceStorage;
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/service/command/impl/DefaultSupervisionCommandService.java b/core/src/main/java/com/alibaba/smart/framework/engine/service/command/impl/DefaultSupervisionCommandService.java
new file mode 100644
index 000000000..341077458
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/service/command/impl/DefaultSupervisionCommandService.java
@@ -0,0 +1,223 @@
+package com.alibaba.smart.framework.engine.service.command.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.alibaba.smart.framework.engine.configuration.ProcessEngineConfiguration;
+import com.alibaba.smart.framework.engine.configuration.aware.ProcessEngineConfigurationAware;
+import com.alibaba.smart.framework.engine.configuration.scanner.AnnotationScanner;
+import com.alibaba.smart.framework.engine.constant.SupervisionConstant;
+import com.alibaba.smart.framework.engine.exception.SupervisionException;
+import com.alibaba.smart.framework.engine.exception.ValidationException;
+import com.alibaba.smart.framework.engine.extension.annotation.ExtensionBinding;
+import com.alibaba.smart.framework.engine.extension.constant.ExtensionConstant;
+import com.alibaba.smart.framework.engine.hook.LifeCycleHook;
+import com.alibaba.smart.framework.engine.instance.impl.DefaultSupervisionInstance;
+import com.alibaba.smart.framework.engine.instance.storage.SupervisionInstanceStorage;
+import com.alibaba.smart.framework.engine.instance.storage.TaskInstanceStorage;
+import com.alibaba.smart.framework.engine.model.instance.SupervisionInstance;
+import com.alibaba.smart.framework.engine.model.instance.TaskInstance;
+import com.alibaba.smart.framework.engine.service.command.NotificationCommandService;
+import com.alibaba.smart.framework.engine.service.command.SupervisionCommandService;
+
+/**
+ * Default implementation of SupervisionCommandService.
+ *
+ * @author SmartEngine Team
+ */
+@ExtensionBinding(group = ExtensionConstant.SERVICE, bindKey = SupervisionCommandService.class)
+public class DefaultSupervisionCommandService implements SupervisionCommandService, LifeCycleHook,
+ ProcessEngineConfigurationAware {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(DefaultSupervisionCommandService.class);
+
+ private ProcessEngineConfiguration processEngineConfiguration;
+ private SupervisionInstanceStorage supervisionInstanceStorage;
+ private TaskInstanceStorage taskInstanceStorage;
+ private NotificationCommandService notificationCommandService;
+
+ @Override
+ public void start() {
+ AnnotationScanner annotationScanner = this.processEngineConfiguration.getAnnotationScanner();
+ this.supervisionInstanceStorage = annotationScanner.getExtensionPoint(ExtensionConstant.COMMON,
+ SupervisionInstanceStorage.class);
+ this.taskInstanceStorage = annotationScanner.getExtensionPoint(ExtensionConstant.COMMON,
+ TaskInstanceStorage.class);
+ this.notificationCommandService = annotationScanner.getExtensionPoint(ExtensionConstant.SERVICE,
+ NotificationCommandService.class);
+ }
+
+ @Override
+ public void stop() {
+ // Clean up resources if needed
+ }
+
+ @Override
+ public SupervisionInstance createSupervision(String taskInstanceId, String supervisorUserId,
+ String reason, String supervisionType, String tenantId) {
+ // Validate required parameters
+ if (taskInstanceId == null || supervisorUserId == null) {
+ throw new ValidationException("TaskInstanceId and SupervisorUserId cannot be null");
+ }
+
+ if (!isValidSupervisionType(supervisionType)) {
+ throw new ValidationException("Invalid supervision type: " + supervisionType);
+ }
+
+ LOGGER.info("Creating supervision for task: {}, supervisor: {}, type: {}",
+ taskInstanceId, supervisorUserId, supervisionType);
+
+ try {
+ // First, get task instance to retrieve processInstanceId (required for NOT NULL constraint)
+ TaskInstance taskInstance = taskInstanceStorage.find(taskInstanceId, tenantId, processEngineConfiguration);
+ if (taskInstance == null) {
+ throw new ValidationException("Task not found: " + taskInstanceId);
+ }
+
+ SupervisionInstance supervisionInstance = new DefaultSupervisionInstance();
+ supervisionInstance.setTaskInstanceId(taskInstanceId);
+ supervisionInstance.setProcessInstanceId(taskInstance.getProcessInstanceId()); // Set before insert
+ supervisionInstance.setSupervisorUserId(supervisorUserId);
+ supervisionInstance.setSupervisionReason(reason);
+ supervisionInstance.setSupervisionType(supervisionType);
+ supervisionInstance.setStatus(SupervisionConstant.SupervisionStatus.ACTIVE);
+ supervisionInstance.setTenantId(tenantId);
+
+ // Generate ID
+ processEngineConfiguration.getIdGenerator().generate(supervisionInstance);
+
+ // Save supervision record
+ SupervisionInstance result = supervisionInstanceStorage.insert(supervisionInstance,
+ processEngineConfiguration);
+
+ // Update task priority and send notification
+ processTaskAfterSupervision(taskInstance, supervisorUserId, reason);
+
+ LOGGER.info("Supervision created successfully: {}", result.getInstanceId());
+ return result;
+
+ } catch (ValidationException e) {
+ throw e;
+ } catch (Exception e) {
+ LOGGER.error("Failed to create supervision for task: {}", taskInstanceId, e);
+ throw new SupervisionException("Failed to create supervision", e);
+ }
+ }
+
+ /**
+ * Process task after supervision is created: update priority and send notification.
+ */
+ private void processTaskAfterSupervision(TaskInstance taskInstance,
+ String supervisorUserId, String reason) {
+ if (taskInstance == null) {
+ return;
+ }
+
+ String tenantId = taskInstance.getTenantId();
+
+ // Increase task priority
+ int newPriority = (taskInstance.getPriority() != null ? taskInstance.getPriority() : 0) + 1;
+ taskInstance.setPriority(newPriority);
+ taskInstanceStorage.update(taskInstance, processEngineConfiguration);
+ LOGGER.debug("Task priority updated to {} for task: {}", newPriority, taskInstance.getInstanceId());
+
+ // Send supervision notification to task assignee
+ if (taskInstance.getClaimUserId() != null && notificationCommandService != null) {
+ try {
+ notificationCommandService.sendSingleNotification(
+ taskInstance.getProcessInstanceId(),
+ taskInstance.getInstanceId(),
+ supervisorUserId,
+ taskInstance.getClaimUserId(),
+ "任务督办通知",
+ "您的任务被督办,原因:" + reason,
+ "督办提醒",
+ tenantId
+ );
+ LOGGER.debug("Supervision notification sent to user: {}", taskInstance.getClaimUserId());
+ } catch (Exception e) {
+ LOGGER.warn("Failed to send supervision notification to user: {}", taskInstance.getClaimUserId(), e);
+ // Don't throw - notification failure should not fail the supervision creation
+ }
+ }
+ }
+
+ @Override
+ public void closeSupervision(String supervisionId, String tenantId) {
+ if (supervisionId == null) {
+ throw new ValidationException("SupervisionId cannot be null");
+ }
+
+ LOGGER.info("Closing supervision: {}", supervisionId);
+
+ try {
+ supervisionInstanceStorage.closeSupervision(supervisionId, tenantId, processEngineConfiguration);
+ LOGGER.info("Supervision closed successfully: {}", supervisionId);
+ } catch (Exception e) {
+ LOGGER.error("Failed to close supervision: {}", supervisionId, e);
+ throw new SupervisionException("Failed to close supervision: " + supervisionId, e);
+ }
+ }
+
+ @Override
+ public List batchCreateSupervision(List taskInstanceIds,
+ String supervisorUserId, String reason,
+ String supervisionType, String tenantId) {
+ if (taskInstanceIds == null || taskInstanceIds.isEmpty()) {
+ throw new ValidationException("TaskInstanceIds cannot be null or empty");
+ }
+
+ LOGGER.info("Batch creating {} supervisions for supervisor: {}", taskInstanceIds.size(), supervisorUserId);
+
+ List results = new ArrayList<>();
+ for (String taskInstanceId : taskInstanceIds) {
+ try {
+ SupervisionInstance supervision = createSupervision(taskInstanceId, supervisorUserId,
+ reason, supervisionType, tenantId);
+ results.add(supervision);
+ } catch (Exception e) {
+ LOGGER.error("Failed to create supervision for task: {}", taskInstanceId, e);
+ throw new SupervisionException("Failed to create supervision for task: " + taskInstanceId, e);
+ }
+ }
+
+ LOGGER.info("Batch supervision creation completed: {} created", results.size());
+ return results;
+ }
+
+ @Override
+ public void autoCloseSupervisionByTask(String taskInstanceId, String tenantId) {
+ if (taskInstanceId == null) {
+ throw new ValidationException("TaskInstanceId cannot be null");
+ }
+
+ LOGGER.info("Auto closing supervisions for task: {}", taskInstanceId);
+
+ try {
+ supervisionInstanceStorage.closeSupervisionByTask(taskInstanceId, tenantId, processEngineConfiguration);
+ LOGGER.info("Supervisions auto closed for task: {}", taskInstanceId);
+ } catch (Exception e) {
+ LOGGER.error("Failed to auto close supervisions for task: {}", taskInstanceId, e);
+ throw new SupervisionException("Failed to auto close supervision by task: " + taskInstanceId, e);
+ }
+ }
+
+ /**
+ * Check if supervision type is valid.
+ */
+ private boolean isValidSupervisionType(String supervisionType) {
+ return supervisionType != null && (
+ SupervisionConstant.SupervisionType.URGE.equals(supervisionType)
+ || SupervisionConstant.SupervisionType.TRACK.equals(supervisionType)
+ || SupervisionConstant.SupervisionType.REMIND.equals(supervisionType)
+ );
+ }
+
+ @Override
+ public void setProcessEngineConfiguration(ProcessEngineConfiguration processEngineConfiguration) {
+ this.processEngineConfiguration = processEngineConfiguration;
+ }
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/service/command/impl/DefaultTaskCommandService.java b/core/src/main/java/com/alibaba/smart/framework/engine/service/command/impl/DefaultTaskCommandService.java
index 7c567ae76..5256d00e6 100644
--- a/core/src/main/java/com/alibaba/smart/framework/engine/service/command/impl/DefaultTaskCommandService.java
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/service/command/impl/DefaultTaskCommandService.java
@@ -10,6 +10,8 @@
import com.alibaba.smart.framework.engine.common.util.InstanceUtil;
import com.alibaba.smart.framework.engine.common.util.MarkDoneUtil;
import com.alibaba.smart.framework.engine.configuration.ConfigurationOption;
+import com.alibaba.smart.framework.engine.configuration.TaskEventPublisher;
+import com.alibaba.smart.framework.engine.pvm.event.EventConstant;
import com.alibaba.smart.framework.engine.configuration.IdGenerator;
import com.alibaba.smart.framework.engine.configuration.ProcessEngineConfiguration;
import com.alibaba.smart.framework.engine.configuration.aware.ProcessEngineConfigurationAware;
@@ -23,15 +25,24 @@
import com.alibaba.smart.framework.engine.hook.LifeCycleHook;
import com.alibaba.smart.framework.engine.instance.impl.DefaultTaskAssigneeInstance;
import com.alibaba.smart.framework.engine.instance.impl.DefaultTaskInstance;
+import com.alibaba.smart.framework.engine.instance.impl.DefaultTaskTransferRecord;
+import com.alibaba.smart.framework.engine.instance.impl.DefaultAssigneeOperationRecord;
+import com.alibaba.smart.framework.engine.instance.impl.DefaultRollbackRecord;
import com.alibaba.smart.framework.engine.instance.storage.ActivityInstanceStorage;
import com.alibaba.smart.framework.engine.instance.storage.ExecutionInstanceStorage;
import com.alibaba.smart.framework.engine.instance.storage.ProcessInstanceStorage;
import com.alibaba.smart.framework.engine.instance.storage.TaskAssigneeStorage;
import com.alibaba.smart.framework.engine.instance.storage.TaskInstanceStorage;
+import com.alibaba.smart.framework.engine.instance.storage.TaskTransferRecordStorage;
+import com.alibaba.smart.framework.engine.instance.storage.AssigneeOperationRecordStorage;
+import com.alibaba.smart.framework.engine.instance.storage.RollbackRecordStorage;
+import com.alibaba.smart.framework.engine.instance.storage.SupervisionInstanceStorage;
import com.alibaba.smart.framework.engine.model.instance.*;
import com.alibaba.smart.framework.engine.service.command.ExecutionCommandService;
import com.alibaba.smart.framework.engine.service.command.TaskCommandService;
import com.alibaba.smart.framework.engine.util.ObjectUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* @author 高海军 帝奇 2016.11.11
@@ -41,6 +52,8 @@
public class DefaultTaskCommandService implements TaskCommandService, LifeCycleHook ,
ProcessEngineConfigurationAware {
+ private static final Logger logger = LoggerFactory.getLogger(DefaultTaskCommandService.class);
+
private ProcessInstanceStorage processInstanceStorage;
private ActivityInstanceStorage activityInstanceStorage;
private ExecutionInstanceStorage executionInstanceStorage;
@@ -48,6 +61,9 @@ public class DefaultTaskCommandService implements TaskCommandService, LifeCycleH
private ProcessEngineConfiguration processEngineConfiguration;
private TaskInstanceStorage taskInstanceStorage;
private TaskAssigneeStorage taskAssigneeStorage;
+ private TaskTransferRecordStorage taskTransferRecordStorage;
+ private AssigneeOperationRecordStorage assigneeOperationRecordStorage;
+ private RollbackRecordStorage rollbackRecordStorage;
@Override
public void start() {
@@ -60,6 +76,11 @@ public void start() {
this.executionInstanceStorage = annotationScanner.getExtensionPoint(ExtensionConstant.COMMON,ExecutionInstanceStorage.class);
this.taskInstanceStorage = annotationScanner.getExtensionPoint(ExtensionConstant.COMMON,TaskInstanceStorage.class);
+ // Initialize Storage for operation records
+ this.taskTransferRecordStorage = annotationScanner.getExtensionPoint(ExtensionConstant.COMMON, TaskTransferRecordStorage.class);
+ this.assigneeOperationRecordStorage = annotationScanner.getExtensionPoint(ExtensionConstant.COMMON, AssigneeOperationRecordStorage.class);
+ this.rollbackRecordStorage = annotationScanner.getExtensionPoint(ExtensionConstant.COMMON, RollbackRecordStorage.class);
+
}
@Override
@@ -68,6 +89,22 @@ public void stop() {
}
+ @Override
+ public void claim(String taskId, String userId, String tenantId) {
+ TaskInstance taskInstance = taskInstanceStorage.find(taskId, tenantId, processEngineConfiguration);
+ if (taskInstance == null) {
+ throw new ValidationException("Task instance not found for taskId: " + taskId);
+ }
+ if (taskInstance.getClaimUserId() != null && !taskInstance.getClaimUserId().isEmpty()) {
+ throw new ValidationException("Task already claimed by user: " + taskInstance.getClaimUserId());
+ }
+ taskInstance.setClaimUserId(userId);
+ taskInstance.setClaimTime(DateUtil.getCurrentDate());
+ taskInstanceStorage.update(taskInstance, processEngineConfiguration);
+ fireTaskEvent(EventConstant.TASK_CLAIMED, taskInstance, tenantId,
+ Map.of("claimUserId", userId));
+ }
+
@Override
public ProcessInstance complete(String taskId, Map request, Map response) {
@@ -79,6 +116,20 @@ public ProcessInstance complete(String taskId, Map request, Map<
MarkDoneUtil.markDoneTaskInstance(taskInstance, TaskInstanceConstant.COMPLETED, TaskInstanceConstant.PENDING,
request, taskInstanceStorage, processEngineConfiguration);
+ fireTaskEvent(EventConstant.TASK_COMPLETED, taskInstance, tenantId, Map.of());
+
+ // 自动关闭该任务的所有督办
+ try {
+ SupervisionInstanceStorage supervisionStorage = (SupervisionInstanceStorage)
+ processEngineConfiguration.getInstanceAccessor().access("supervisionInstanceStorage");
+ if (supervisionStorage != null) {
+ supervisionStorage.closeSupervisionByTask(taskInstance.getInstanceId(), tenantId, processEngineConfiguration);
+ }
+ } catch (Exception e) {
+ // 记录日志但不影响任务完成
+ logger.warn("Failed to close supervision for task: " + taskId, e);
+ }
+
return executionCommandService.signal(taskInstance.getExecutionInstanceId(), request,response);
}
@@ -103,6 +154,9 @@ public ProcessInstance complete(String taskId, String userId, Map request) {
public void setProcessEngineConfiguration(ProcessEngineConfiguration processEngineConfiguration) {
this.processEngineConfiguration = processEngineConfiguration;
}
+
+ @Override
+ public void transferWithReason(String taskId, String fromUserId, String toUserId, String reason, String tenantId) {
+ // 执行原有的转交逻辑
+ transfer(taskId, fromUserId, toUserId, tenantId);
+
+ // 记录转交操作
+ IdGenerator idGenerator = processEngineConfiguration.getIdGenerator();
+ DefaultTaskTransferRecord record = new DefaultTaskTransferRecord();
+ idGenerator.generate(record);
+ record.setTaskInstanceId(taskId);
+ record.setFromUserId(fromUserId);
+ record.setToUserId(toUserId);
+ record.setTransferReason(reason);
+ record.setTenantId(tenantId);
+
+ taskTransferRecordStorage.insert(record, processEngineConfiguration);
+ TaskInstance taskInstance = taskInstanceStorage.find(taskId, tenantId, processEngineConfiguration);
+ fireTaskEvent(EventConstant.TASK_TRANSFERRED, taskInstance, tenantId,
+ Map.of("fromUserId", fromUserId, "toUserId", toUserId, "reason", reason != null ? reason : ""));
+ }
+
+ @Override
+ public ProcessInstance rollbackTask(String taskId, String targetActivityId, String reason, String tenantId) {
+ TaskInstance taskInstance = taskInstanceStorage.find(taskId, tenantId, processEngineConfiguration);
+
+ if (taskInstance == null) {
+ throw new ValidationException("Task instance not found for taskId: " + taskId);
+ }
+
+ // 获取流程实例
+ ProcessInstance processInstance = processInstanceStorage.findOne(
+ taskInstance.getProcessInstanceId(), tenantId, processEngineConfiguration);
+
+ String currentActivityId = taskInstance.getProcessDefinitionActivityId();
+
+ // 记录回退操作(在执行回退之前)
+ IdGenerator idGenerator = processEngineConfiguration.getIdGenerator();
+ DefaultRollbackRecord record = new DefaultRollbackRecord();
+ idGenerator.generate(record);
+ record.setProcessInstanceId(processInstance.getInstanceId());
+ record.setTaskInstanceId(taskInstance.getInstanceId());
+ record.setRollbackType("specific");
+ record.setFromActivityId(currentActivityId);
+ record.setToActivityId(targetActivityId);
+ record.setOperatorUserId(taskInstance.getClaimUserId()); // 使用当前任务处理人作为操作人
+ record.setRollbackReason(reason);
+ record.setTenantId(tenantId);
+
+ rollbackRecordStorage.insert(record, processEngineConfiguration);
+
+ // 执行回退
+ return executionCommandService.jumpTo(
+ taskInstance.getProcessInstanceId(),
+ processInstance.getProcessDefinitionId(),
+ processInstance.getProcessDefinitionVersion(),
+ processInstance.getStatus(),
+ targetActivityId,
+ tenantId
+ );
+ }
+
+ @Override
+ public void addTaskAssigneeCandidateWithReason(String taskId, String tenantId, TaskAssigneeCandidateInstance taskAssigneeCandidateInstance, String reason) {
+ // 执行原有的加签逻辑
+ addTaskAssigneeCandidate(taskId, tenantId, taskAssigneeCandidateInstance);
+
+ // 记录加签操作
+ IdGenerator idGenerator = processEngineConfiguration.getIdGenerator();
+ DefaultAssigneeOperationRecord record = new DefaultAssigneeOperationRecord();
+ idGenerator.generate(record);
+ record.setTaskInstanceId(taskId);
+
+ // 获取任务实例以获取操作人
+ TaskInstance taskInstance = taskInstanceStorage.find(taskId, tenantId, processEngineConfiguration);
+ if (taskInstance != null) {
+ record.setOperatorUserId(taskInstance.getClaimUserId());
+ }
+
+ record.setOperationType("add_assignee");
+ record.setTargetUserId(taskAssigneeCandidateInstance.getAssigneeId());
+ record.setOperationReason(reason);
+ record.setTenantId(tenantId);
+
+ assigneeOperationRecordStorage.insert(record, processEngineConfiguration);
+ if (taskInstance != null) {
+ fireTaskEvent(EventConstant.TASK_DELEGATED, taskInstance, tenantId,
+ Map.of("newAssigneeId", taskAssigneeCandidateInstance.getAssigneeId(),
+ "reason", reason != null ? reason : ""));
+ }
+ }
+
+ @Override
+ public void removeTaskAssigneeCandidateWithReason(String taskId, String tenantId, TaskAssigneeCandidateInstance taskAssigneeCandidateInstance, String reason) {
+ // 执行原有的减签逻辑
+ removeTaskAssigneeCandidate(taskId, tenantId, taskAssigneeCandidateInstance);
+
+ // 记录减签操作
+ IdGenerator idGenerator = processEngineConfiguration.getIdGenerator();
+ DefaultAssigneeOperationRecord record = new DefaultAssigneeOperationRecord();
+ idGenerator.generate(record);
+ record.setTaskInstanceId(taskId);
+
+ // 获取任务实例以获取操作人
+ TaskInstance taskInstance = taskInstanceStorage.find(taskId, tenantId, processEngineConfiguration);
+ if (taskInstance != null) {
+ record.setOperatorUserId(taskInstance.getClaimUserId());
+ }
+
+ record.setOperationType("remove_assignee");
+ record.setTargetUserId(taskAssigneeCandidateInstance.getAssigneeId());
+ record.setOperationReason(reason);
+ record.setTenantId(tenantId);
+
+ assigneeOperationRecordStorage.insert(record, processEngineConfiguration);
+ if (taskInstance != null) {
+ fireTaskEvent(EventConstant.TASK_REVOKED, taskInstance, tenantId,
+ Map.of("removedAssigneeId", taskAssigneeCandidateInstance.getAssigneeId(),
+ "reason", reason != null ? reason : ""));
+ }
+ }
+
+ private void fireTaskEvent(EventConstant event, TaskInstance taskInstance,
+ String tenantId, Map extra) {
+ TaskEventPublisher publisher = processEngineConfiguration.getTaskEventPublisher();
+ if (publisher != null) {
+ publisher.publish(event, taskInstance, tenantId, extra != null ? extra : Map.of());
+ }
+ }
}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/service/param/query/CompletedProcessQueryParam.java b/core/src/main/java/com/alibaba/smart/framework/engine/service/param/query/CompletedProcessQueryParam.java
new file mode 100644
index 000000000..730f9045d
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/service/param/query/CompletedProcessQueryParam.java
@@ -0,0 +1,62 @@
+package com.alibaba.smart.framework.engine.service.param.query;
+
+import java.util.Date;
+import java.util.List;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 办结流程查询参数
+ *
+ * @author SmartEngine Team
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class CompletedProcessQueryParam extends BaseQueryParam {
+
+ /**
+ * 参与用户ID(发起人或处理过任务的用户)
+ */
+ private String participantUserId;
+
+ /**
+ * 流程定义类型列表
+ */
+ private List processDefinitionTypes;
+
+ /**
+ * 完成时间开始
+ */
+ private Date completeTimeStart;
+
+ /**
+ * 完成时间结束
+ */
+ private Date completeTimeEnd;
+
+ /**
+ * 流程实例ID列表
+ */
+ private List processInstanceIdList;
+
+ /**
+ * 流程标题(模糊查询)
+ */
+ private String title;
+
+ /**
+ * 流程标签
+ */
+ private String tag;
+
+ /**
+ * 业务唯一ID
+ */
+ private String bizUniqueId;
+
+ /**
+ * 发起人用户ID
+ */
+ private String startUserId;
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/service/param/query/CompletedTaskQueryParam.java b/core/src/main/java/com/alibaba/smart/framework/engine/service/param/query/CompletedTaskQueryParam.java
new file mode 100644
index 000000000..e189a6f38
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/service/param/query/CompletedTaskQueryParam.java
@@ -0,0 +1,57 @@
+package com.alibaba.smart.framework.engine.service.param.query;
+
+import java.util.Date;
+import java.util.List;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 已办任务查询参数
+ *
+ * @author SmartEngine Team
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class CompletedTaskQueryParam extends BaseQueryParam {
+
+ /**
+ * 任务处理人用户ID
+ */
+ private String claimUserId;
+
+ /**
+ * 流程定义类型列表
+ */
+ private List processDefinitionTypes;
+
+ /**
+ * 完成时间开始
+ */
+ private Date completeTimeStart;
+
+ /**
+ * 完成时间结束
+ */
+ private Date completeTimeEnd;
+
+ /**
+ * 流程实例ID列表
+ */
+ private List processInstanceIdList;
+
+ /**
+ * 任务标题(模糊查询)
+ */
+ private String title;
+
+ /**
+ * 任务标签
+ */
+ private String tag;
+
+ /**
+ * 处理意见(模糊查询)
+ */
+ private String comment;
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/service/param/query/JsonCondition.java b/core/src/main/java/com/alibaba/smart/framework/engine/service/param/query/JsonCondition.java
new file mode 100644
index 000000000..7c843e18d
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/service/param/query/JsonCondition.java
@@ -0,0 +1,23 @@
+package com.alibaba.smart.framework.engine.service.param.query;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+/**
+ * Represents a JSON key-value condition for SQL WHERE clause.
+ * The sqlExpression is generated by Dialect.jsonExtractText() and is safe from injection.
+ */
+@Data
+@AllArgsConstructor
+public class JsonCondition {
+
+ /**
+ * Dialect-generated SQL expression, e.g. "JSON_UNQUOTE(JSON_EXTRACT(task.extra, '$.category'))"
+ */
+ private String sqlExpression;
+
+ /**
+ * The comparison value (parameterized via #{}).
+ */
+ private String value;
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/service/param/query/JsonInCondition.java b/core/src/main/java/com/alibaba/smart/framework/engine/service/param/query/JsonInCondition.java
new file mode 100644
index 000000000..926cc492a
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/service/param/query/JsonInCondition.java
@@ -0,0 +1,25 @@
+package com.alibaba.smart.framework.engine.service.param.query;
+
+import java.util.List;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+/**
+ * Represents a JSON key IN (...) condition for SQL WHERE clause.
+ * The sqlExpression is generated by Dialect.jsonExtractText() and is safe from injection.
+ */
+@Data
+@AllArgsConstructor
+public class JsonInCondition {
+
+ /**
+ * Dialect-generated SQL expression, e.g. "JSON_UNQUOTE(JSON_EXTRACT(task.extra, '$.category'))"
+ */
+ private String sqlExpression;
+
+ /**
+ * The list of values to match against.
+ */
+ private List values;
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/service/param/query/NotificationQueryParam.java b/core/src/main/java/com/alibaba/smart/framework/engine/service/param/query/NotificationQueryParam.java
new file mode 100644
index 000000000..1383b5740
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/service/param/query/NotificationQueryParam.java
@@ -0,0 +1,59 @@
+package com.alibaba.smart.framework.engine.service.param.query;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * Notification query parameter.
+ *
+ * @author SmartEngine Team
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class NotificationQueryParam extends BaseQueryParam {
+
+ /**
+ * Sender user ID
+ */
+ private String senderUserId;
+
+ /**
+ * Receiver user ID
+ */
+ private String receiverUserId;
+
+ /**
+ * Process instance ID list
+ */
+ private List extends Serializable> processInstanceIdList;
+
+ /**
+ * Task instance ID list
+ */
+ private List extends Serializable> taskInstanceIdList;
+
+ /**
+ * Notification type
+ */
+ private String notificationType;
+
+ /**
+ * Read status
+ */
+ private String readStatus;
+
+ /**
+ * Notification start time
+ */
+ private Date notificationStartTime;
+
+ /**
+ * Notification end time
+ */
+ private Date notificationEndTime;
+
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/service/param/query/PaginateQueryParam.java b/core/src/main/java/com/alibaba/smart/framework/engine/service/param/query/PaginateQueryParam.java
index 90e6c0ca1..088fbb530 100644
--- a/core/src/main/java/com/alibaba/smart/framework/engine/service/param/query/PaginateQueryParam.java
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/service/param/query/PaginateQueryParam.java
@@ -1,5 +1,9 @@
package com.alibaba.smart.framework.engine.service.param.query;
+import java.util.List;
+
+import com.alibaba.smart.framework.engine.query.OrderSpec;
+
import lombok.Data;
/**
@@ -15,4 +19,10 @@ public class PaginateQueryParam {
protected Integer pageOffset;
protected Integer pageSize;
+ /**
+ * Dynamic order by specifications for fluent query API.
+ * When set, this will be used instead of default ordering in SQL.
+ */
+ protected List orderBySpecs;
+
}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/service/param/query/ProcessInstanceQueryParam.java b/core/src/main/java/com/alibaba/smart/framework/engine/service/param/query/ProcessInstanceQueryParam.java
index 75738548b..21aeef64a 100644
--- a/core/src/main/java/com/alibaba/smart/framework/engine/service/param/query/ProcessInstanceQueryParam.java
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/service/param/query/ProcessInstanceQueryParam.java
@@ -1,5 +1,6 @@
package com.alibaba.smart.framework.engine.service.param.query;
+import java.io.Serializable;
import java.util.Date;
import java.util.List;
@@ -19,13 +20,13 @@ public class ProcessInstanceQueryParam extends BaseQueryParam {
private String startUserId;
private String status ;
private String processDefinitionType;
- private String parentInstanceId;
+ private Serializable parentInstanceId;
private String bizUniqueId;
private String processDefinitionIdAndVersion;
/**
* 流程引擎实例id列表
*/
- private List processInstanceIdList;
+ private List extends Serializable> processInstanceIdList;
/**
* 查询启动时间在processStartTime之后的流程实例
@@ -36,4 +37,20 @@ public class ProcessInstanceQueryParam extends BaseQueryParam {
* 查询启动时间在processEndTime之前的流程实例
*/
private Date processEndTime;
+
+ /**
+ * 完成时间开始
+ */
+ private Date completeTimeStart;
+
+ /**
+ * 完成时间结束
+ */
+ private Date completeTimeEnd;
+
+ /**
+ * Filter by multiple process definition types (IN clause).
+ */
+ private List processDefinitionTypeList;
+
}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/service/param/query/SupervisionQueryParam.java b/core/src/main/java/com/alibaba/smart/framework/engine/service/param/query/SupervisionQueryParam.java
new file mode 100644
index 000000000..416ba375c
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/service/param/query/SupervisionQueryParam.java
@@ -0,0 +1,54 @@
+package com.alibaba.smart.framework.engine.service.param.query;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * Supervision query parameter.
+ *
+ * @author SmartEngine Team
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class SupervisionQueryParam extends BaseQueryParam {
+
+ /**
+ * Supervisor user ID
+ */
+ private String supervisorUserId;
+
+ /**
+ * Task instance ID list
+ */
+ private List extends Serializable> taskInstanceIdList;
+
+ /**
+ * Process instance ID list
+ */
+ private List extends Serializable> processInstanceIdList;
+
+ /**
+ * Supervision type
+ */
+ private String supervisionType;
+
+ /**
+ * Supervision status
+ */
+ private String status;
+
+ /**
+ * Supervision start time
+ */
+ private Date supervisionStartTime;
+
+ /**
+ * Supervision end time
+ */
+ private Date supervisionEndTime;
+
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/service/param/query/TaskInstanceQueryByAssigneeParam.java b/core/src/main/java/com/alibaba/smart/framework/engine/service/param/query/TaskInstanceQueryByAssigneeParam.java
index fd7a427b4..6f37431c1 100644
--- a/core/src/main/java/com/alibaba/smart/framework/engine/service/param/query/TaskInstanceQueryByAssigneeParam.java
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/service/param/query/TaskInstanceQueryByAssigneeParam.java
@@ -4,6 +4,8 @@
import lombok.Data;
import lombok.EqualsAndHashCode;
+import com.alibaba.smart.framework.engine.service.param.query.JsonCondition;
+import com.alibaba.smart.framework.engine.service.param.query.JsonInCondition;
/**
* Created by jerry.zzy on 2017/11/16.
@@ -22,4 +24,20 @@ public class TaskInstanceQueryByAssigneeParam extends BaseQueryParam {
private String status;
+ // ============ domain_code filters ============
+
+ private String domainCode;
+
+ private List domainCodeList;
+
+ private String domainCodeLike;
+
+ // ============ extra JSON conditions (built by Dialect) ============
+
+ private List jsonConditions;
+
+ private List jsonInConditions;
+
+ private List jsonLikeConditions;
+
}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/service/param/query/TaskInstanceQueryParam.java b/core/src/main/java/com/alibaba/smart/framework/engine/service/param/query/TaskInstanceQueryParam.java
index 9716dcb66..0202daff1 100644
--- a/core/src/main/java/com/alibaba/smart/framework/engine/service/param/query/TaskInstanceQueryParam.java
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/service/param/query/TaskInstanceQueryParam.java
@@ -1,19 +1,22 @@
package com.alibaba.smart.framework.engine.service.param.query;
+import java.io.Serializable;
import java.util.Date;
import java.util.List;
import lombok.Data;
import lombok.EqualsAndHashCode;
+import com.alibaba.smart.framework.engine.service.param.query.JsonCondition;
+import com.alibaba.smart.framework.engine.service.param.query.JsonInCondition;
@Data
@EqualsAndHashCode(callSuper = true)
public class TaskInstanceQueryParam extends BaseQueryParam {
- private List processInstanceIdList;
+ private List extends Serializable> processInstanceIdList;
- private String activityInstanceId;
+ private Serializable activityInstanceId;
private String processDefinitionType;
@@ -23,6 +26,11 @@ public class TaskInstanceQueryParam extends BaseQueryParam {
*/
private String status;
+ /**
+ * Filter by multiple task statuses.
+ */
+ private List statusList;
+
private String claimUserId;
private String tag;
@@ -35,4 +43,70 @@ public class TaskInstanceQueryParam extends BaseQueryParam {
private String title;
-}
\ No newline at end of file
+ /**
+ * Fuzzy search on title (LIKE %titleLike%).
+ */
+ private String titleLike;
+
+ /**
+ * 完成时间开始
+ */
+ private Date completeTimeStart;
+
+ /**
+ * 完成时间结束
+ */
+ private Date completeTimeEnd;
+
+ /**
+ * Filter by create time range (start, inclusive).
+ */
+ private Date createTimeStart;
+
+ /**
+ * Filter by create time range (end, exclusive).
+ */
+ private Date createTimeEnd;
+
+ /**
+ * Filter by multiple process definition types (IN clause).
+ */
+ private List processDefinitionTypeList;
+
+ /**
+ * Filter for unassigned tasks (claim_user_id IS NULL).
+ */
+ private Boolean unassigned;
+
+ /**
+ * Fuzzy search on claim user ID (LIKE %claimUserIdLike%).
+ */
+ private String claimUserIdLike;
+
+ /**
+ * Filter by minimum priority (inclusive).
+ */
+ private Integer minPriority;
+
+ /**
+ * Filter by maximum priority (inclusive).
+ */
+ private Integer maxPriority;
+
+ // ============ domain_code filters ============
+
+ private String domainCode;
+
+ private List domainCodeList;
+
+ private String domainCodeLike;
+
+ // ============ extra JSON conditions (built by Dialect) ============
+
+ private List jsonConditions;
+
+ private List jsonInConditions;
+
+ private List jsonLikeConditions;
+
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/service/query/DeploymentQueryService.java b/core/src/main/java/com/alibaba/smart/framework/engine/service/query/DeploymentQueryService.java
index 3ba13fa76..0b427382a 100644
--- a/core/src/main/java/com/alibaba/smart/framework/engine/service/query/DeploymentQueryService.java
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/service/query/DeploymentQueryService.java
@@ -12,10 +12,27 @@
*/
public interface DeploymentQueryService {
+ /**
+ * @deprecated Use {@code smartEngine.createDeploymentQuery().deploymentInstanceId(id).singleResult()} instead
+ */
+ @Deprecated
DeploymentInstance findById(String deploymentInstanceId);
+
+ /**
+ * @deprecated Use {@code smartEngine.createDeploymentQuery().deploymentInstanceId(id).tenantId(t).singleResult()} instead
+ */
+ @Deprecated
DeploymentInstance findById(String deploymentInstanceId,String tenantId);
+ /**
+ * @deprecated Use {@code smartEngine.createDeploymentQuery()...list()} instead
+ */
+ @Deprecated
List findList(DeploymentInstanceQueryParam deploymentInstanceQueryParam) ;
+ /**
+ * @deprecated Use {@code smartEngine.createDeploymentQuery()...count()} instead
+ */
+ @Deprecated
Integer count(DeploymentInstanceQueryParam deploymentInstanceQueryParam);
}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/service/query/NotificationQueryService.java b/core/src/main/java/com/alibaba/smart/framework/engine/service/query/NotificationQueryService.java
new file mode 100644
index 000000000..bd7d2b3e8
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/service/query/NotificationQueryService.java
@@ -0,0 +1,85 @@
+package com.alibaba.smart.framework.engine.service.query;
+
+import java.util.List;
+
+import com.alibaba.smart.framework.engine.model.instance.NotificationInstance;
+import com.alibaba.smart.framework.engine.service.param.query.NotificationQueryParam;
+
+/**
+ * 知会抄送查询服务接口
+ *
+ * @author SmartEngine Team
+ */
+public interface NotificationQueryService {
+
+ /**
+ * 查询知会通知列表
+ *
+ * @param param 查询参数
+ * @return 知会通知列表
+ * @deprecated Use {@code smartEngine.createNotificationQuery()} fluent API instead
+ */
+ @Deprecated
+ List findNotificationList(NotificationQueryParam param);
+
+ /**
+ * 统计知会通知数量
+ *
+ * @param param 查询参数
+ * @return 知会通知数量
+ * @deprecated Use {@code smartEngine.createNotificationQuery()...count()} instead
+ */
+ @Deprecated
+ Long countNotifications(NotificationQueryParam param);
+
+ /**
+ * 统计未读通知数量
+ *
+ * @param receiverUserId 接收人用户ID
+ * @param tenantId 租户ID
+ * @return 未读通知数量
+ * @deprecated Use {@code smartEngine.createNotificationQuery().receiverUserId(u).readStatus("unread").tenantId(t).count()} instead
+ */
+ @Deprecated
+ Long countUnreadNotifications(String receiverUserId, String tenantId);
+
+ /**
+ * 根据ID查询知会通知
+ *
+ * @param notificationId 通知ID
+ * @param tenantId 租户ID
+ * @return 知会通知
+ * @deprecated Use {@code smartEngine.createNotificationQuery().notificationId(id).tenantId(t).singleResult()} instead
+ */
+ @Deprecated
+ NotificationInstance findOne(String notificationId, String tenantId);
+
+ /**
+ * 查询接收人的知会列表
+ *
+ * @param receiverUserId 接收人用户ID
+ * @param readStatus 读取状态(可选)
+ * @param tenantId 租户ID
+ * @param pageOffset 分页偏移
+ * @param pageSize 分页大小
+ * @return 知会通知列表
+ * @deprecated Use {@code smartEngine.createNotificationQuery().receiverUserId(u).readStatus(s).tenantId(t).listPage(offset, size)} instead
+ */
+ @Deprecated
+ List findByReceiver(String receiverUserId, String readStatus,
+ String tenantId, Integer pageOffset, Integer pageSize);
+
+ /**
+ * 查询发送人的知会列表
+ *
+ * @param senderUserId 发送人用户ID
+ * @param tenantId 租户ID
+ * @param pageOffset 分页偏移
+ * @param pageSize 分页大小
+ * @return 知会通知列表
+ * @deprecated Use {@code smartEngine.createNotificationQuery().senderUserId(u).tenantId(t).listPage(offset, size)} instead
+ */
+ @Deprecated
+ List findBySender(String senderUserId, String tenantId,
+ Integer pageOffset, Integer pageSize);
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/service/query/ProcessQueryService.java b/core/src/main/java/com/alibaba/smart/framework/engine/service/query/ProcessQueryService.java
index f00d2b8a6..0817e6cd4 100644
--- a/core/src/main/java/com/alibaba/smart/framework/engine/service/query/ProcessQueryService.java
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/service/query/ProcessQueryService.java
@@ -4,6 +4,7 @@
import com.alibaba.smart.framework.engine.model.instance.ProcessInstance;
import com.alibaba.smart.framework.engine.service.param.query.ProcessInstanceQueryParam;
+import com.alibaba.smart.framework.engine.service.param.query.CompletedProcessQueryParam;
/**
* 查询流程实例。
@@ -12,12 +13,48 @@
*/
public interface ProcessQueryService {
+ /**
+ * @deprecated Use {@code smartEngine.createProcessQuery().processInstanceId(id).singleResult()} instead
+ */
+ @Deprecated
ProcessInstance findById(String processInstanceId);
+ /**
+ * @deprecated Use {@code smartEngine.createProcessQuery().processInstanceId(id).tenantId(t).singleResult()} instead
+ */
+ @Deprecated
ProcessInstance findById(String processInstanceId,String tenantId);
+ /**
+ * @deprecated Use {@code smartEngine.createProcessQuery()} fluent API instead
+ */
+ @Deprecated
List findList(ProcessInstanceQueryParam processInstanceQueryParam);
+ /**
+ * @deprecated Use {@code smartEngine.createProcessQuery()...count()} instead
+ */
+ @Deprecated
Long count(ProcessInstanceQueryParam processInstanceQueryParam);
+ /**
+ * 查询办结流程列表 - 基于现有findList方法扩展
+ *
+ * @param param 办结流程查询参数
+ * @return 办结流程列表
+ * @deprecated Use {@code smartEngine.createProcessQuery().involvedUser(u).processDefinitionTypeIn(types).completedAfter(s).completedBefore(e).processStatus("completed").list()} instead
+ */
+ @Deprecated
+ List findCompletedProcessList(CompletedProcessQueryParam param);
+
+ /**
+ * 统计办结流程数量
+ *
+ * @param param 办结流程查询参数
+ * @return 办结流程数量
+ * @deprecated Use {@code smartEngine.createProcessQuery().involvedUser(u).processDefinitionTypeIn(types).completedAfter(s).completedBefore(e).processStatus("completed").count()} instead
+ */
+ @Deprecated
+ Long countCompletedProcessList(CompletedProcessQueryParam param);
+
}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/service/query/SupervisionQueryService.java b/core/src/main/java/com/alibaba/smart/framework/engine/service/query/SupervisionQueryService.java
new file mode 100644
index 000000000..2e92b9a2a
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/service/query/SupervisionQueryService.java
@@ -0,0 +1,56 @@
+package com.alibaba.smart.framework.engine.service.query;
+
+import java.util.List;
+
+import com.alibaba.smart.framework.engine.model.instance.SupervisionInstance;
+import com.alibaba.smart.framework.engine.service.param.query.SupervisionQueryParam;
+
+/**
+ * 督办管理查询服务接口
+ *
+ * @author SmartEngine Team
+ */
+public interface SupervisionQueryService {
+
+ /**
+ * 查询督办记录列表
+ *
+ * @param param 查询参数
+ * @return 督办记录列表
+ * @deprecated Use {@code smartEngine.createSupervisionQuery()} fluent API instead
+ */
+ @Deprecated
+ List findSupervisionList(SupervisionQueryParam param);
+
+ /**
+ * 统计督办记录数量
+ *
+ * @param param 查询参数
+ * @return 督办记录数量
+ * @deprecated Use {@code smartEngine.createSupervisionQuery()...count()} instead
+ */
+ @Deprecated
+ Long countSupervision(SupervisionQueryParam param);
+
+ /**
+ * 查询任务的活跃督办记录
+ *
+ * @param taskInstanceId 任务实例ID
+ * @param tenantId 租户ID
+ * @return 活跃督办记录列表
+ * @deprecated Use {@code smartEngine.createSupervisionQuery().taskInstanceId(id).supervisionStatus("active").tenantId(t).list()} instead
+ */
+ @Deprecated
+ List findActiveSupervisionByTask(String taskInstanceId, String tenantId);
+
+ /**
+ * 根据ID查询督办记录
+ *
+ * @param supervisionId 督办ID
+ * @param tenantId 租户ID
+ * @return 督办记录
+ * @deprecated Use {@code smartEngine.createSupervisionQuery().supervisionId(id).tenantId(t).singleResult()} instead
+ */
+ @Deprecated
+ SupervisionInstance findOne(String supervisionId, String tenantId);
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/service/query/TaskQueryService.java b/core/src/main/java/com/alibaba/smart/framework/engine/service/query/TaskQueryService.java
index 0fda4daa3..4f0a11c19 100644
--- a/core/src/main/java/com/alibaba/smart/framework/engine/service/query/TaskQueryService.java
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/service/query/TaskQueryService.java
@@ -6,6 +6,7 @@
import com.alibaba.smart.framework.engine.service.param.query.PendingTaskQueryParam;
import com.alibaba.smart.framework.engine.service.param.query.TaskInstanceQueryByAssigneeParam;
import com.alibaba.smart.framework.engine.service.param.query.TaskInstanceQueryParam;
+import com.alibaba.smart.framework.engine.service.param.query.CompletedTaskQueryParam;
/**
* 用户任务查询服务。
@@ -16,13 +17,28 @@ public interface TaskQueryService {
/**
* 待办任务列表
+ *
+ * @deprecated Use {@code smartEngine.createTaskQuery().taskCandidateOrGroup(userId, groupIds).taskStatus("pending").list()} instead
*/
+ @Deprecated
List findPendingTaskList(PendingTaskQueryParam pendingTaskQueryParam);
+ /**
+ * @deprecated Use {@code smartEngine.createTaskQuery().taskCandidateOrGroup(userId, groupIds).taskStatus("pending").count()} instead
+ */
+ @Deprecated
Long countPendingTaskList(PendingTaskQueryParam pendingTaskQueryParam);
+ /**
+ * @deprecated Use {@code smartEngine.createTaskQuery().taskCandidateOrGroup(userId, groupIds).taskStatus(s).list()} instead
+ */
+ @Deprecated
List findTaskListByAssignee(TaskInstanceQueryByAssigneeParam param);
+ /**
+ * @deprecated Use {@code smartEngine.createTaskQuery().taskCandidateOrGroup(userId, groupIds).taskStatus(s).count()} instead
+ */
+ @Deprecated
Long countTaskListByAssignee(TaskInstanceQueryByAssigneeParam param);
/**
@@ -30,19 +46,61 @@ public interface TaskQueryService {
*
* @param processInstanceId
* @return
+ * @deprecated Use {@code smartEngine.createTaskQuery().processInstanceId(id).taskStatus("pending").list()} instead
*/
+ @Deprecated
List findAllPendingTaskList(String processInstanceId);
+
+ /**
+ * @deprecated Use {@code smartEngine.createTaskQuery().processInstanceId(id).taskStatus("pending").tenantId(t).list()} instead
+ */
+ @Deprecated
List findAllPendingTaskList(String processInstanceId,String tenantId);
+ /**
+ * @deprecated Use {@code smartEngine.createTaskQuery().taskInstanceId(id).singleResult()} instead
+ */
+ @Deprecated
TaskInstance findOne(String taskInstanceId);
+
+ /**
+ * @deprecated Use {@code smartEngine.createTaskQuery().taskInstanceId(id).tenantId(t).singleResult()} instead
+ */
+ @Deprecated
TaskInstance findOne(String taskInstanceId,String tenantId);
/**
* 扩展方法,可用于典型的审批场景等等,取决于tag的值是什么。tag 任意非null值,可以为 appproved,rejected 等等。
*
+ * @deprecated Use {@code smartEngine.createTaskQuery()} fluent API instead
*/
+ @Deprecated
List findList(TaskInstanceQueryParam taskInstanceQueryParam);
+ /**
+ * @deprecated Use {@code smartEngine.createTaskQuery()...count()} instead
+ */
+ @Deprecated
Long count(TaskInstanceQueryParam taskInstanceQueryParam);
+ /**
+ * 查询已办任务列表 - 基于现有findList方法扩展
+ *
+ * @param param 已办任务查询参数
+ * @return 已办任务列表
+ * @deprecated Use {@code smartEngine.createTaskQuery().taskAssignee(u).processDefinitionTypeIn(types).completeTimeAfter(s).completeTimeBefore(e).taskStatus("completed").list()} instead
+ */
+ @Deprecated
+ List findCompletedTaskList(CompletedTaskQueryParam param);
+
+ /**
+ * 统计已办任务数量
+ *
+ * @param param 已办任务查询参数
+ * @return 已办任务数量
+ * @deprecated Use {@code smartEngine.createTaskQuery().taskAssignee(u).processDefinitionTypeIn(types).completeTimeAfter(s).completeTimeBefore(e).taskStatus("completed").count()} instead
+ */
+ @Deprecated
+ Long countCompletedTaskList(CompletedTaskQueryParam param);
+
}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/service/query/impl/DefaultNotificationQueryService.java b/core/src/main/java/com/alibaba/smart/framework/engine/service/query/impl/DefaultNotificationQueryService.java
new file mode 100644
index 000000000..e299e6cd3
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/service/query/impl/DefaultNotificationQueryService.java
@@ -0,0 +1,131 @@
+package com.alibaba.smart.framework.engine.service.query.impl;
+
+import java.util.List;
+
+import com.alibaba.smart.framework.engine.configuration.ProcessEngineConfiguration;
+import com.alibaba.smart.framework.engine.configuration.aware.ProcessEngineConfigurationAware;
+import com.alibaba.smart.framework.engine.configuration.scanner.AnnotationScanner;
+import com.alibaba.smart.framework.engine.constant.NotificationConstant;
+import com.alibaba.smart.framework.engine.exception.NotificationException;
+import com.alibaba.smart.framework.engine.exception.ValidationException;
+import com.alibaba.smart.framework.engine.extension.annotation.ExtensionBinding;
+import com.alibaba.smart.framework.engine.extension.constant.ExtensionConstant;
+import com.alibaba.smart.framework.engine.hook.LifeCycleHook;
+import com.alibaba.smart.framework.engine.instance.storage.NotificationInstanceStorage;
+import com.alibaba.smart.framework.engine.model.instance.NotificationInstance;
+import com.alibaba.smart.framework.engine.service.param.query.NotificationQueryParam;
+import com.alibaba.smart.framework.engine.service.query.NotificationQueryService;
+
+/**
+ * 知会通知查询服务默认实现
+ *
+ * @author SmartEngine Team
+ */
+@ExtensionBinding(group = ExtensionConstant.SERVICE, bindKey = NotificationQueryService.class)
+public class DefaultNotificationQueryService implements NotificationQueryService, LifeCycleHook, ProcessEngineConfigurationAware {
+
+ private ProcessEngineConfiguration processEngineConfiguration;
+ private NotificationInstanceStorage notificationInstanceStorage;
+
+ @Override
+ public void start() {
+ AnnotationScanner annotationScanner = this.processEngineConfiguration.getAnnotationScanner();
+ this.notificationInstanceStorage = annotationScanner.getExtensionPoint(ExtensionConstant.COMMON, NotificationInstanceStorage.class);
+ }
+
+ @Override
+ public void stop() {
+ // 清理资源
+ }
+
+ @Override
+ public NotificationInstance findOne(String notificationId, String tenantId) {
+ if (notificationId == null) {
+ throw new ValidationException("NotificationId cannot be null");
+ }
+
+ try {
+ return notificationInstanceStorage.find(notificationId, tenantId, processEngineConfiguration);
+ } catch (Exception e) {
+ throw new NotificationException("Failed to find notification: " + notificationId, e);
+ }
+ }
+
+ @Override
+ public List findNotificationList(NotificationQueryParam param) {
+ if (param == null) {
+ throw new ValidationException("NotificationQueryParam cannot be null");
+ }
+
+ try {
+ return notificationInstanceStorage.findNotificationList(param, processEngineConfiguration);
+ } catch (Exception e) {
+ throw new NotificationException("Failed to find notification list", e);
+ }
+ }
+
+ @Override
+ public Long countNotifications(NotificationQueryParam param) {
+ if (param == null) {
+ throw new ValidationException("NotificationQueryParam cannot be null");
+ }
+
+ try {
+ return notificationInstanceStorage.countNotifications(param, processEngineConfiguration);
+ } catch (Exception e) {
+ throw new NotificationException("Failed to count notifications", e);
+ }
+ }
+
+ @Override
+ public Long countUnreadNotifications(String receiverUserId, String tenantId) {
+ if (receiverUserId == null) {
+ throw new ValidationException("ReceiverUserId cannot be null");
+ }
+
+ try {
+ return notificationInstanceStorage.countUnreadNotifications(receiverUserId, tenantId, processEngineConfiguration);
+ } catch (Exception e) {
+ throw new NotificationException("Failed to count unread notifications for user: " + receiverUserId, e);
+ }
+ }
+
+ @Override
+ public List findByReceiver(String receiverUserId, String readStatus,
+ String tenantId, Integer pageOffset, Integer pageSize) {
+ if (receiverUserId == null) {
+ throw new ValidationException("ReceiverUserId cannot be null");
+ }
+
+ try {
+ return notificationInstanceStorage.findByReceiver(receiverUserId, readStatus, tenantId,
+ pageOffset != null ? pageOffset : 0,
+ pageSize != null ? pageSize : 20,
+ processEngineConfiguration);
+ } catch (Exception e) {
+ throw new NotificationException("Failed to find notifications by receiver: " + receiverUserId, e);
+ }
+ }
+
+ @Override
+ public List findBySender(String senderUserId, String tenantId,
+ Integer pageOffset, Integer pageSize) {
+ if (senderUserId == null) {
+ throw new ValidationException("SenderUserId cannot be null");
+ }
+
+ try {
+ return notificationInstanceStorage.findBySender(senderUserId, tenantId,
+ pageOffset != null ? pageOffset : 0,
+ pageSize != null ? pageSize : 20,
+ processEngineConfiguration);
+ } catch (Exception e) {
+ throw new NotificationException("Failed to find notifications by sender: " + senderUserId, e);
+ }
+ }
+
+ @Override
+ public void setProcessEngineConfiguration(ProcessEngineConfiguration processEngineConfiguration) {
+ this.processEngineConfiguration = processEngineConfiguration;
+ }
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/service/query/impl/DefaultProcessQueryService.java b/core/src/main/java/com/alibaba/smart/framework/engine/service/query/impl/DefaultProcessQueryService.java
index df3823ab6..3a7b58021 100644
--- a/core/src/main/java/com/alibaba/smart/framework/engine/service/query/impl/DefaultProcessQueryService.java
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/service/query/impl/DefaultProcessQueryService.java
@@ -1,5 +1,6 @@
package com.alibaba.smart.framework.engine.service.query.impl;
+import java.util.Collections;
import java.util.List;
import com.alibaba.smart.framework.engine.configuration.ProcessEngineConfiguration;
@@ -8,61 +9,108 @@
import com.alibaba.smart.framework.engine.extension.constant.ExtensionConstant;
import com.alibaba.smart.framework.engine.hook.LifeCycleHook;
import com.alibaba.smart.framework.engine.instance.storage.ProcessInstanceStorage;
+import com.alibaba.smart.framework.engine.model.instance.InstanceStatus;
import com.alibaba.smart.framework.engine.model.instance.ProcessInstance;
+import com.alibaba.smart.framework.engine.service.param.query.CompletedProcessQueryParam;
import com.alibaba.smart.framework.engine.service.param.query.ProcessInstanceQueryParam;
import com.alibaba.smart.framework.engine.service.query.ProcessQueryService;
/**
- * Created by 高海军 帝奇 74394 on 2016 November 22:10.
+ * Default implementation of ProcessQueryService.
+ *
+ * @author 高海军 帝奇
*/
-
@ExtensionBinding(group = ExtensionConstant.SERVICE, bindKey = ProcessQueryService.class)
+public class DefaultProcessQueryService implements ProcessQueryService, LifeCycleHook,
+ ProcessEngineConfigurationAware {
-public class DefaultProcessQueryService implements ProcessQueryService, LifeCycleHook ,
- ProcessEngineConfigurationAware {
-
+ private ProcessEngineConfiguration processEngineConfiguration;
private ProcessInstanceStorage processInstanceStorage;
-
-
@Override
public void start() {
-
- this.processInstanceStorage = processEngineConfiguration.getAnnotationScanner().getExtensionPoint(ExtensionConstant.COMMON,ProcessInstanceStorage.class);
-
-
+ this.processInstanceStorage = processEngineConfiguration.getAnnotationScanner()
+ .getExtensionPoint(ExtensionConstant.COMMON, ProcessInstanceStorage.class);
}
@Override
public void stop() {
-
+ // Clean up resources if needed
}
@Override
public ProcessInstance findById(String processInstanceId) {
+ return findById(processInstanceId, null);
+ }
- return findById(processInstanceId,null);
+ @Override
+ public ProcessInstance findById(String processInstanceId, String tenantId) {
+ return processInstanceStorage.findOne(processInstanceId, tenantId, processEngineConfiguration);
}
@Override
- public ProcessInstance findById(String processInstanceId,String tenantId) {
+ public List findList(ProcessInstanceQueryParam processInstanceQueryParam) {
+ return processInstanceStorage.queryProcessInstanceList(processInstanceQueryParam, processEngineConfiguration);
+ }
- return processInstanceStorage.findOne(processInstanceId,tenantId, processEngineConfiguration);
+ @Override
+ public Long count(ProcessInstanceQueryParam processInstanceQueryParam) {
+ return processInstanceStorage.count(processInstanceQueryParam, processEngineConfiguration);
}
@Override
- public List findList(ProcessInstanceQueryParam processInstanceQueryParam) {
+ public List findCompletedProcessList(CompletedProcessQueryParam param) {
+ if (param == null) {
+ return Collections.emptyList();
+ }
+
+ ProcessInstanceQueryParam processInstanceQueryParam = convertToProcessInstanceQueryParam(param);
+ processInstanceQueryParam.setStatus(InstanceStatus.completed.name());
return processInstanceStorage.queryProcessInstanceList(processInstanceQueryParam, processEngineConfiguration);
}
@Override
- public Long count(ProcessInstanceQueryParam processInstanceQueryParam) {
+ public Long countCompletedProcessList(CompletedProcessQueryParam param) {
+ if (param == null) {
+ return 0L;
+ }
+
+ ProcessInstanceQueryParam processInstanceQueryParam = convertToProcessInstanceQueryParam(param);
+ processInstanceQueryParam.setStatus(InstanceStatus.completed.name());
- return processInstanceStorage.count(processInstanceQueryParam,processEngineConfiguration );
+ return processInstanceStorage.count(processInstanceQueryParam, processEngineConfiguration);
}
- private ProcessEngineConfiguration processEngineConfiguration;
+ /**
+ * Convert CompletedProcessQueryParam to ProcessInstanceQueryParam.
+ * Note: For completed process query, we only filter by completeTime, not processStartTime.
+ */
+ private ProcessInstanceQueryParam convertToProcessInstanceQueryParam(CompletedProcessQueryParam param) {
+ ProcessInstanceQueryParam processInstanceQueryParam = new ProcessInstanceQueryParam();
+
+ // Pagination
+ processInstanceQueryParam.setTenantId(param.getTenantId());
+ processInstanceQueryParam.setPageOffset(param.getPageOffset());
+ processInstanceQueryParam.setPageSize(param.getPageSize());
+
+ // Query conditions
+ processInstanceQueryParam.setStartUserId(param.getStartUserId());
+ processInstanceQueryParam.setProcessInstanceIdList(param.getProcessInstanceIdList());
+ processInstanceQueryParam.setBizUniqueId(param.getBizUniqueId());
+
+ // Complete time filter (NOT processStartTime - that was a bug)
+ processInstanceQueryParam.setCompleteTimeStart(param.getCompleteTimeStart());
+ processInstanceQueryParam.setCompleteTimeEnd(param.getCompleteTimeEnd());
+
+ // Process definition type (take first one if multiple provided)
+ // TODO: Consider extending ProcessInstanceQueryParam to support multiple types
+ if (param.getProcessDefinitionTypes() != null && !param.getProcessDefinitionTypes().isEmpty()) {
+ processInstanceQueryParam.setProcessDefinitionType(param.getProcessDefinitionTypes().get(0));
+ }
+
+ return processInstanceQueryParam;
+ }
@Override
public void setProcessEngineConfiguration(ProcessEngineConfiguration processEngineConfiguration) {
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/service/query/impl/DefaultSupervisionQueryService.java b/core/src/main/java/com/alibaba/smart/framework/engine/service/query/impl/DefaultSupervisionQueryService.java
new file mode 100644
index 000000000..d447f737f
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/service/query/impl/DefaultSupervisionQueryService.java
@@ -0,0 +1,96 @@
+package com.alibaba.smart.framework.engine.service.query.impl;
+
+import java.util.List;
+
+import com.alibaba.smart.framework.engine.configuration.ProcessEngineConfiguration;
+import com.alibaba.smart.framework.engine.configuration.aware.ProcessEngineConfigurationAware;
+import com.alibaba.smart.framework.engine.configuration.scanner.AnnotationScanner;
+import com.alibaba.smart.framework.engine.exception.SupervisionException;
+import com.alibaba.smart.framework.engine.exception.ValidationException;
+import com.alibaba.smart.framework.engine.extension.annotation.ExtensionBinding;
+import com.alibaba.smart.framework.engine.extension.constant.ExtensionConstant;
+import com.alibaba.smart.framework.engine.hook.LifeCycleHook;
+import com.alibaba.smart.framework.engine.instance.storage.SupervisionInstanceStorage;
+import com.alibaba.smart.framework.engine.model.instance.SupervisionInstance;
+import com.alibaba.smart.framework.engine.service.param.query.SupervisionQueryParam;
+import com.alibaba.smart.framework.engine.service.query.SupervisionQueryService;
+
+/**
+ * 督办查询服务默认实现
+ *
+ * @author SmartEngine Team
+ */
+@ExtensionBinding(group = ExtensionConstant.SERVICE, bindKey = SupervisionQueryService.class)
+public class DefaultSupervisionQueryService implements SupervisionQueryService, LifeCycleHook, ProcessEngineConfigurationAware {
+
+ private ProcessEngineConfiguration processEngineConfiguration;
+ private SupervisionInstanceStorage supervisionInstanceStorage;
+
+ @Override
+ public void start() {
+ AnnotationScanner annotationScanner = this.processEngineConfiguration.getAnnotationScanner();
+ this.supervisionInstanceStorage = annotationScanner.getExtensionPoint(ExtensionConstant.COMMON, SupervisionInstanceStorage.class);
+ }
+
+ @Override
+ public void stop() {
+ // 清理资源
+ }
+
+ @Override
+ public List findSupervisionList(SupervisionQueryParam param) {
+ if (param == null) {
+ throw new ValidationException("SupervisionQueryParam cannot be null");
+ }
+
+ try {
+ return supervisionInstanceStorage.findSupervisionList(param, processEngineConfiguration);
+ } catch (Exception e) {
+ throw new SupervisionException("Failed to find supervision list", e);
+ }
+ }
+
+ @Override
+ public Long countSupervision(SupervisionQueryParam param) {
+ if (param == null) {
+ throw new ValidationException("SupervisionQueryParam cannot be null");
+ }
+
+ try {
+ return supervisionInstanceStorage.countSupervision(param, processEngineConfiguration);
+ } catch (Exception e) {
+ throw new SupervisionException("Failed to count supervision", e);
+ }
+ }
+
+ @Override
+ public List findActiveSupervisionByTask(String taskInstanceId, String tenantId) {
+ if (taskInstanceId == null) {
+ throw new ValidationException("TaskInstanceId cannot be null");
+ }
+
+ try {
+ return supervisionInstanceStorage.findActiveSupervisionByTask(taskInstanceId, tenantId, processEngineConfiguration);
+ } catch (Exception e) {
+ throw new SupervisionException("Failed to find active supervision by task: " + taskInstanceId, e);
+ }
+ }
+
+ @Override
+ public SupervisionInstance findOne(String supervisionId, String tenantId) {
+ if (supervisionId == null) {
+ throw new ValidationException("SupervisionId cannot be null");
+ }
+
+ try {
+ return supervisionInstanceStorage.find(supervisionId, tenantId, processEngineConfiguration);
+ } catch (Exception e) {
+ throw new SupervisionException("Failed to find supervision: " + supervisionId, e);
+ }
+ }
+
+ @Override
+ public void setProcessEngineConfiguration(ProcessEngineConfiguration processEngineConfiguration) {
+ this.processEngineConfiguration = processEngineConfiguration;
+ }
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/service/query/impl/DefaultTaskQueryService.java b/core/src/main/java/com/alibaba/smart/framework/engine/service/query/impl/DefaultTaskQueryService.java
index 88aa21d47..276af36d1 100644
--- a/core/src/main/java/com/alibaba/smart/framework/engine/service/query/impl/DefaultTaskQueryService.java
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/service/query/impl/DefaultTaskQueryService.java
@@ -1,6 +1,7 @@
package com.alibaba.smart.framework.engine.service.query.impl;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import com.alibaba.smart.framework.engine.configuration.ProcessEngineConfiguration;
@@ -11,46 +12,42 @@
import com.alibaba.smart.framework.engine.hook.LifeCycleHook;
import com.alibaba.smart.framework.engine.instance.storage.TaskInstanceStorage;
import com.alibaba.smart.framework.engine.model.instance.TaskInstance;
+import com.alibaba.smart.framework.engine.service.param.query.CompletedTaskQueryParam;
import com.alibaba.smart.framework.engine.service.param.query.PendingTaskQueryParam;
import com.alibaba.smart.framework.engine.service.param.query.TaskInstanceQueryByAssigneeParam;
import com.alibaba.smart.framework.engine.service.param.query.TaskInstanceQueryParam;
import com.alibaba.smart.framework.engine.service.query.TaskQueryService;
/**
- * Created by 高海军 帝奇 74394 on 2016 November 22:10.
+ * Default implementation of TaskQueryService.
+ *
+ * @author 高海军 帝奇
*/
@ExtensionBinding(group = ExtensionConstant.SERVICE, bindKey = TaskQueryService.class)
+public class DefaultTaskQueryService implements TaskQueryService, LifeCycleHook,
+ ProcessEngineConfigurationAware {
-public class DefaultTaskQueryService implements TaskQueryService, LifeCycleHook ,
- ProcessEngineConfigurationAware {
-
- private ProcessEngineConfiguration processEngineConfiguration ;
+ private ProcessEngineConfiguration processEngineConfiguration;
private TaskInstanceStorage taskInstanceStorage;
@Override
public void start() {
- this.taskInstanceStorage = processEngineConfiguration.getAnnotationScanner().getExtensionPoint(ExtensionConstant.COMMON,TaskInstanceStorage.class);
-
+ this.taskInstanceStorage = processEngineConfiguration.getAnnotationScanner()
+ .getExtensionPoint(ExtensionConstant.COMMON, TaskInstanceStorage.class);
}
-
-
@Override
public void stop() {
-
+ // Clean up resources if needed
}
@Override
public List findPendingTaskList(PendingTaskQueryParam pendingTaskQueryParam) {
-
return taskInstanceStorage.findPendingTaskList(pendingTaskQueryParam, processEngineConfiguration);
}
@Override
public Long countPendingTaskList(PendingTaskQueryParam pendingTaskQueryParam) {
-
-
-
return taskInstanceStorage.countPendingTaskList(pendingTaskQueryParam, processEngineConfiguration);
}
@@ -61,7 +58,7 @@ public List findTaskListByAssignee(TaskInstanceQueryByAssigneePara
@Override
public Long countTaskListByAssignee(TaskInstanceQueryByAssigneeParam param) {
- return taskInstanceStorage.countTaskListByAssignee(param,processEngineConfiguration );
+ return taskInstanceStorage.countTaskListByAssignee(param, processEngineConfiguration);
}
@Override
@@ -70,10 +67,9 @@ public List findAllPendingTaskList(String processInstanceId) {
}
@Override
- public List findAllPendingTaskList(String processInstanceId,String tenantId) {
-
+ public List findAllPendingTaskList(String processInstanceId, String tenantId) {
TaskInstanceQueryParam taskInstanceQueryParam = new TaskInstanceQueryParam();
- List processInstanceIdList = new ArrayList(2);
+ List processInstanceIdList = new ArrayList<>(2);
processInstanceIdList.add(processInstanceId);
taskInstanceQueryParam.setProcessInstanceIdList(processInstanceIdList);
taskInstanceQueryParam.setStatus(TaskInstanceConstant.PENDING);
@@ -84,27 +80,78 @@ public List findAllPendingTaskList(String processInstanceId,String
@Override
public TaskInstance findOne(String taskInstanceId) {
- return this.findOne(taskInstanceId,null);
+ return this.findOne(taskInstanceId, null);
}
@Override
- public TaskInstance findOne(String taskInstanceId,String tenantId) {
- TaskInstance taskInstance = taskInstanceStorage.find(taskInstanceId,tenantId, processEngineConfiguration);
- return taskInstance;
+ public TaskInstance findOne(String taskInstanceId, String tenantId) {
+ return taskInstanceStorage.find(taskInstanceId, tenantId, processEngineConfiguration);
}
@Override
- public List findList(TaskInstanceQueryParam taskInstanceQueryParam){
-
+ public List findList(TaskInstanceQueryParam taskInstanceQueryParam) {
return taskInstanceStorage.findTaskList(taskInstanceQueryParam, processEngineConfiguration);
}
@Override
public Long count(TaskInstanceQueryParam taskInstanceQueryParam) {
+ return taskInstanceStorage.count(taskInstanceQueryParam, processEngineConfiguration);
+ }
+
+ @Override
+ public List findCompletedTaskList(CompletedTaskQueryParam param) {
+ if (param == null) {
+ return Collections.emptyList();
+ }
+
+ TaskInstanceQueryParam taskInstanceQueryParam = convertToTaskInstanceQueryParam(param);
+ taskInstanceQueryParam.setStatus(TaskInstanceConstant.COMPLETED);
+
+ return taskInstanceStorage.findTaskList(taskInstanceQueryParam, processEngineConfiguration);
+ }
+
+ @Override
+ public Long countCompletedTaskList(CompletedTaskQueryParam param) {
+ if (param == null) {
+ return 0L;
+ }
+
+ TaskInstanceQueryParam taskInstanceQueryParam = convertToTaskInstanceQueryParam(param);
+ taskInstanceQueryParam.setStatus(TaskInstanceConstant.COMPLETED);
return taskInstanceStorage.count(taskInstanceQueryParam, processEngineConfiguration);
}
+ /**
+ * Convert CompletedTaskQueryParam to TaskInstanceQueryParam.
+ */
+ private TaskInstanceQueryParam convertToTaskInstanceQueryParam(CompletedTaskQueryParam param) {
+ TaskInstanceQueryParam taskInstanceQueryParam = new TaskInstanceQueryParam();
+
+ // Pagination
+ taskInstanceQueryParam.setTenantId(param.getTenantId());
+ taskInstanceQueryParam.setPageOffset(param.getPageOffset());
+ taskInstanceQueryParam.setPageSize(param.getPageSize());
+
+ // Query conditions
+ taskInstanceQueryParam.setClaimUserId(param.getClaimUserId());
+ taskInstanceQueryParam.setProcessInstanceIdList(param.getProcessInstanceIdList());
+ taskInstanceQueryParam.setTitle(param.getTitle());
+ taskInstanceQueryParam.setTag(param.getTag());
+ taskInstanceQueryParam.setComment(param.getComment());
+
+ // Process definition type (take first one if multiple provided)
+ // TODO: Consider extending TaskInstanceQueryParam to support multiple types
+ if (param.getProcessDefinitionTypes() != null && !param.getProcessDefinitionTypes().isEmpty()) {
+ taskInstanceQueryParam.setProcessDefinitionType(param.getProcessDefinitionTypes().get(0));
+ }
+
+ // Complete time filter
+ taskInstanceQueryParam.setCompleteTimeStart(param.getCompleteTimeStart());
+ taskInstanceQueryParam.setCompleteTimeEnd(param.getCompleteTimeEnd());
+
+ return taskInstanceQueryParam;
+ }
@Override
public void setProcessEngineConfiguration(ProcessEngineConfiguration processEngineConfiguration) {
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/storage/StorageContext.java b/core/src/main/java/com/alibaba/smart/framework/engine/storage/StorageContext.java
new file mode 100644
index 000000000..bfbdde700
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/storage/StorageContext.java
@@ -0,0 +1,100 @@
+package com.alibaba.smart.framework.engine.storage;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Context information for storage operations.
+ * Can be used to pass request-level storage preferences.
+ *
+ * @author SmartEngine Team
+ */
+public class StorageContext implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * The tenant ID for multi-tenant scenarios
+ */
+ private String tenantId;
+
+ /**
+ * The storage mode to use for this request
+ */
+ private StorageMode storageMode;
+
+ /**
+ * Additional context properties
+ */
+ private Map properties;
+
+ private StorageContext() {
+ this.properties = new HashMap<>();
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public String getTenantId() {
+ return tenantId;
+ }
+
+ public StorageMode getStorageMode() {
+ return storageMode;
+ }
+
+ public Map getProperties() {
+ return properties;
+ }
+
+ public Object getProperty(String key) {
+ return properties.get(key);
+ }
+
+ @SuppressWarnings("unchecked")
+ public T getProperty(String key, Class type) {
+ Object value = properties.get(key);
+ if (value == null) {
+ return null;
+ }
+ return (T) value;
+ }
+
+ public static class Builder {
+ private final StorageContext context;
+
+ private Builder() {
+ this.context = new StorageContext();
+ }
+
+ public Builder tenantId(String tenantId) {
+ context.tenantId = tenantId;
+ return this;
+ }
+
+ public Builder storageMode(StorageMode storageMode) {
+ context.storageMode = storageMode;
+ return this;
+ }
+
+ public Builder property(String key, Object value) {
+ context.properties.put(key, value);
+ return this;
+ }
+
+ public StorageContext build() {
+ return context;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StorageContext{" +
+ "tenantId='" + tenantId + '\'' +
+ ", storageMode=" + storageMode +
+ ", properties=" + properties +
+ '}';
+ }
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/storage/StorageMode.java b/core/src/main/java/com/alibaba/smart/framework/engine/storage/StorageMode.java
new file mode 100644
index 000000000..a59b2de20
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/storage/StorageMode.java
@@ -0,0 +1,34 @@
+package com.alibaba.smart.framework.engine.storage;
+
+/**
+ * Enumeration of storage modes for SmartEngine.
+ * Supports runtime selection of storage implementation per request.
+ *
+ * @author SmartEngine Team
+ */
+public enum StorageMode {
+
+ /**
+ * Use database storage (default).
+ * Data is persisted to relational database.
+ */
+ DATABASE,
+
+ /**
+ * Use custom storage (e.g. in-memory, custom persistence, etc.).
+ * Backed by storage-custom module implementations.
+ */
+ CUSTOM,
+
+ /**
+ * Dual-write mode.
+ * Writes to both database and memory, reads from memory for performance.
+ */
+ DUAL_WRITE,
+
+ /**
+ * Read from memory, write to database mode.
+ * Provides fast reads while ensuring data persistence.
+ */
+ READ_MEMORY_WRITE_DATABASE
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/storage/StorageModeHolder.java b/core/src/main/java/com/alibaba/smart/framework/engine/storage/StorageModeHolder.java
new file mode 100644
index 000000000..78e27c890
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/storage/StorageModeHolder.java
@@ -0,0 +1,104 @@
+package com.alibaba.smart.framework.engine.storage;
+
+/**
+ * ThreadLocal holder for storage mode.
+ * Allows request-level storage mode selection via ThreadLocal.
+ *
+ * Example usage:
+ *
{@code
+ * StorageModeHolder.set(StorageMode.DATABASE);
+ * try {
+ * // All storage operations in this thread will use DATABASE mode
+ * processService.startProcess(request);
+ * } finally {
+ * StorageModeHolder.clear();
+ * }
+ * }
+ *
+ * @author SmartEngine Team
+ */
+public final class StorageModeHolder {
+
+ private static final ThreadLocal MODE_HOLDER = new ThreadLocal<>();
+ private static final ThreadLocal CONTEXT_HOLDER = new ThreadLocal<>();
+
+ private StorageModeHolder() {
+ // Utility class, no instantiation
+ }
+
+ /**
+ * Set the storage mode for the current thread.
+ *
+ * @param mode the storage mode to use
+ */
+ public static void set(StorageMode mode) {
+ if (mode == null) {
+ MODE_HOLDER.remove();
+ } else {
+ MODE_HOLDER.set(mode);
+ }
+ }
+
+ /**
+ * Get the storage mode for the current thread.
+ *
+ * @return the current storage mode, or null if not set
+ */
+ public static StorageMode get() {
+ return MODE_HOLDER.get();
+ }
+
+ /**
+ * Get the storage mode for the current thread, or return the default if not set.
+ *
+ * @param defaultMode the default mode to return if not set
+ * @return the current storage mode, or the default if not set
+ */
+ public static StorageMode getOrDefault(StorageMode defaultMode) {
+ StorageMode mode = MODE_HOLDER.get();
+ return mode != null ? mode : defaultMode;
+ }
+
+ /**
+ * Clear the storage mode for the current thread.
+ * Should be called in a finally block to prevent memory leaks.
+ */
+ public static void clear() {
+ MODE_HOLDER.remove();
+ }
+
+ /**
+ * Set the storage context for the current thread.
+ *
+ * @param context the storage context to use
+ */
+ public static void setContext(StorageContext context) {
+ if (context == null) {
+ CONTEXT_HOLDER.remove();
+ } else {
+ CONTEXT_HOLDER.set(context);
+ // Also set the mode from context
+ if (context.getStorageMode() != null) {
+ MODE_HOLDER.set(context.getStorageMode());
+ }
+ }
+ }
+
+ /**
+ * Get the storage context for the current thread.
+ *
+ * @return the current storage context, or null if not set
+ */
+ public static StorageContext getContext() {
+ return CONTEXT_HOLDER.get();
+ }
+
+ /**
+ * Clear all ThreadLocal values.
+ * Should be called in a finally block to prevent memory leaks.
+ */
+ public static void clearAll() {
+ MODE_HOLDER.remove();
+ CONTEXT_HOLDER.remove();
+ }
+}
diff --git a/core/src/main/java/com/alibaba/smart/framework/engine/storage/StorageRegistry.java b/core/src/main/java/com/alibaba/smart/framework/engine/storage/StorageRegistry.java
new file mode 100644
index 000000000..156f939cf
--- /dev/null
+++ b/core/src/main/java/com/alibaba/smart/framework/engine/storage/StorageRegistry.java
@@ -0,0 +1,121 @@
+package com.alibaba.smart.framework.engine.storage;
+
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Registry for storage implementations by mode.
+ * Maintains a mapping from StorageMode and storage type to actual implementation.
+ *
+ * @author SmartEngine Team
+ */
+public class StorageRegistry {
+
+ /**
+ * Storage implementations by mode and type
+ * Map, Object>>
+ */
+ private final Map, Object>> storageMap;
+
+ public StorageRegistry() {
+ this.storageMap = new EnumMap<>(StorageMode.class);
+ for (StorageMode mode : StorageMode.values()) {
+ storageMap.put(mode, new HashMap<>());
+ }
+ }
+
+ /**
+ * Register a storage implementation for a specific mode and type.
+ *
+ * @param mode the storage mode
+ * @param storageType the storage interface type
+ * @param instance the storage implementation instance
+ * @param