• 首页
  • 产品中心
    • 数式Oinone四大产品

      低代码开发平台无代码开发平台集成开发平台AI大模型开发
    • 数式Oinone体系能力

      用户与组织权限管理文件管理消息中心国际化业务审计
    • 数式Oinone核心产品特性

      低无一体面向软件公司场景无限制应用级扩容可分可合
  • 服务中心
    • 客户服务

      预约演示方案咨询私有部署找人定制
    • 开发者

      问答下载
    • Oinone学院

      社区学习

    《精讲面向软件公司的低代码平台——以Oinone为例》

  • 合作伙伴
    渠道申请伙伴名录专家库
  • 关于数式
0571-88757863

Functions API


I. Function Definition

Oinone provides various environment configurations and method annotations to standardize model method behavior and data interaction logic. Below is a detailed explanation of core annotations:

(Ⅰ) Function Source Types

1、Regular Function

Function Definition in Model Class

When defining functions in a model class, use annotations to clarify their functional attributes:

@Model.model(TestModel.MODEL_MODEL)
@Model(displayName = "TestModel")
public class TestModel extends IdModel {
    public static final String MODEL_MODEL = "test.TestModel";

    // Set function open level to API, which will build HTTP service interfaces based on GraphQL protocol
    // The operation semantic is QUERY, and the corresponding interface will be categorized under the query field of GraphQL, otherwise under mutation
    @Function(openLevel = FunctionOpenEnum.API)
    @Function.Advanced(type = FunctionTypeEnum.QUERY)
    public TestModel sayHello(TestModel data) {
        // Business logic processing
        return data;
    }
}

Interface and Implementation Class Configuration

Interface Definition: Add the @Function annotation to interface methods to ensure automatic registration of remote service consumers when other modules depend on the API package.

@Fun(TestModel.MODEL_MODEL)
// You can alternatively use the @Model.model annotation to specify the namespace
// @Model.model(TestModel.MODEL_MODEL)
public interface TestModelHelloService {
    @Function
    PetShop sayHello(TestModel data);
}

Implementation Class Configuration: The implementation class must add @Fun and @Function annotations simultaneously to inherit interface functional attributes:

@Fun(TestModel.MODEL_MODEL)
// You can alternatively use the @Model.model annotation to specify the namespace
// @Model.model(TestModel.MODEL_MODEL)
@Component
public class TestModelHelloServiceImpl implements TestModelHelloService {
    @Override
    @Function(openLevel = FunctionOpenEnum.API)
    @Function.Advanced(type = FunctionTypeEnum.QUERY)
    public PetShop sayHello(TestModel data) {
        // Business logic processing
        return data;
    }
}

These functions are independent of model definitions but are associated with models through binding, used to supplement or extend model functions.

Independent Function Interface and Implementation

If a function serves as an independent public logic unit, differentiate it through namespaces:

@Fun(TestModelHelloService.FUN_NAMESPACE)
public interface TestModelHelloService {
    String FUN_NAMESPACE = "test.TestModelHelloService";
    @Function
    TestModel sayHello(TestModel data);
}

@Fun(TestModelHelloService.FUN_NAMESPACE)
@Component
public class TestModelHelloServiceImpl implements TestModelHelloService {
    @Override
    @Function(openLevel = FunctionOpenEnum.API)
    @Function.Advanced(type = FunctionTypeEnum.QUERY)
    public TestModel sayHello(TestModel data) {
        // Business logic processing
        return data;
    }
}

Such functions exist as general public logic, not bound to specific models, and are commonly used to handle repeated logic in multiple business scenarios.

Core Points:

  • Control function open levels, operation semantics, and interface construction rules through @Function series annotations;
  • Interface methods must add the @Function annotation to support automatic remote service registration;
  • Implementation classes must synchronize annotations to ensure consistency with interface configurations and avoid service invocation exceptions.

2、Extension Point Function Extpoint

Extension point functions are used to extend existing functions, implemented through interfaces and implementation classes.

@Ext(TestModel.class)
public interface TestModelExtpoint {
    @ExtPoint(displayName = "Pre-extension point for TestModel's create function")
    public TestModel createBefore(TestModel data);
}

@Ext(TestModel.class)
public class TestModelExtpointImpl implements TestModelExtpoint {
    @Override
    @ExtPoint.Implement(displayName = "Implementation of TestModel's create function pre-extension point", expression = "context.requestFromModule==\"ce_expenses\"")
    public TestModel createBefore(TestModel data) {
        PamirsSession.getMessageHub().info("Implementation of TestModel's create function pre-extension point");
        return data;
    }
}

3、Interceptor Function Hook

Interceptor functions can intercept processing before and after function execution.

@Component
public class TestModelCreateBeforeHook implements HookBefore {

    @Override
    @Hook(model = {TestModel.MODEL_MODEL}, fun = {"create"}, priority = 1)
    public Object run(Function function, Object... args) {
        PamirsSession.getMessageHub().info("TestModel's create function BeforeHook");
        return args;
    }

}

4、Server Action Function ServerAction

Server action functions trigger specific operations on the page.

@Model.model(TestActionModel.MODEL_MODEL)
public class TestActionModelAction {

    @Action.Advanced(invisible = ExpConstants.idValueNotExist)// Hide on page when ID is empty
    @Action(displayName = "actionDoSomething", bindingType = {ViewTypeEnum.FORM, ViewTypeEnum.TABLE})
    public TestActionModel actionDoSomething(TestActionModel testActionModel){
        // do something
        return testActionModel;
    }
}

(Ⅱ) Function Invocation Methods

In the system, functions can be invoked in various ways, mainly including:

  1. Invoke via function manager using namespace and function code: Use the function manager to invoke functions based on namespace and function code, specifically Fun.run(namespace, fun, parameters).
  2. Invoke via function manager using function configuration: Invoke through function manager based on function configuration, i.e., Fun.run(function, parameters).
  3. Invoke via function manager using functional interface: Utilize the function manager to invoke through a functional interface, such as Fun.run(functional interface, parameters).
  4. Invoke using Spring bean: Use Spring bean to invoke functions. First, inject the FunctionInterface instance via the @Resource annotation, then invoke through functionInterface.method(parameters).

Note

Backend programming-invoked function interceptors and extension points are not enabled by default. You can manually set meta directives to enable them, as shown in the example:

Models.directive().run(() -> {
    return Fun.run(namespace, fun, parameters);
}, SystemDirectiveEnum.HOOK, SystemDirectiveEnum.EXT_POINT);

Additionally, function invocations by the function manager (Fun) are compatible with both Spring bean and reflection invocations. If the function's class is not managed by Spring, reflection invocation is used; otherwise, Spring bean invocation is employed.

(Ⅲ) Function Configuration

1、Identification of Non-Model Function Classes

For classes that are not models but contain functions, they must be explicitly identified using the @Fun annotation. To provide remote services, declare function interfaces using the @Fun annotation in the API package.

2、Function Operation Semantic Types

Function operation semantics use binary multi-select enums, covering four core operation types:

  • Create: For data addition operations.
  • Delete: For data deletion operations.
  • Update: For data update operations.
  • Query: For data query operations.

The system defaults the function operation semantics to "Update". If a function only involves data reading and does not perform any write operations (i.e., read-only function), you must explicitly specify its operation semantics as "Query" through annotations to ensure the system correctly identifies and processes function behavior.

Tip

Function type semantics include create, delete, update, and query. When the Function level is API, generating the GraphQL Schema is affected by this. "Query" type functions are categorized under the query field in the generated GraphQL Schema, while "create", "delete", and "update" type functions are categorized under the mutation field.

3、Function Overloading Rules and Common Examples

  • Only one overloaded function with the same namespace and function code takes effect.
  • Functions defined in the model class have higher priority than those defined outside the model class; within the model class, later definitions have higher priority.

Data Construction Function construct Overloading

/**
 * When opening the new page, the frontend will default to calling the construct of the given model
 */
@Function.Advanced( displayName = "Initialize data", type = {FunctionTypeEnum.QUERY})
@Function(summary = "Data construction function", openLevel = {FunctionOpenEnum.LOCAL, FunctionOpenEnum.API, FunctionOpenEnum.REMOTE})
public TestModel construct(TestModel data) {
    // TODO doSomething, additional specific business logic can be appended here based on actual business needs.
    log.info("doSomething 4 TestModel construct");
    data.construct();
    return data;
}

Create (create) Overloading

@Transactional(rollbackFor = {Throwable.class})
@Action.Advanced(name = FunctionConstants.create, type = {FunctionTypeEnum.CREATE}, managed = true, invisible = ExpConstants.idValueExist, check = true)
@Action( displayName = "Create", label = "Confirm", summary = "Add", bindingType = {ViewTypeEnum.FORM})
@Function(name = FunctionConstants.create)
@Function.fun(FunctionConstants.create)
public TestModel create(TestModel data) {
    if (null == data) {
        return null;
    }
    // TODO doSomething, additional specific business logic can be appended here based on actual business needs.
    log.info("doSomething 4 TestModel create");
    // Only perform its own creation operation, without automatically processing associated fields based on creation strategies.
    data.create();
    // Then save one2many, many2many relationship fields
    data.fieldSave(TestModel::getPartners);
    return data;
}

Warning

@Action.Advanced(check = true), if not configured, validation constraints (Validation configuration) will not be performed

Delete (delete) Overloading

@Transactional(rollbackFor = Throwable.class)
@Action.Advanced(name = FunctionConstants.delete, type = FunctionTypeEnum.DELETE, managed = true, priority = 66)
@Action(displayName = "Delete", label = "Delete", contextType = ActionContextTypeEnum.SINGLE_AND_BATCH)
@Function(name = FunctionConstants.delete)
@Function.fun(FunctionConstants.deleteWithFieldBatch)
public List<TestModel> delete(List<TestModel> dataList) {
    if (CollectionUtils.isEmpty(dataList)) {
        return dataList;
    }
    // TODO doSomething, additional specific business logic can be appended here based on actual business needs to customarily control related data or processes before and after the delete operation, achieving more flexible and business-scenario-appropriate function settings.
    log.info("doSomething 4 TestModel delete");
    // Only perform its own delete operation, without automatically processing associated fields based on delete strategies.
    new TestModel().deleteByPks(dataList);
    new TestModel().listRelationDelete(dataList, TestModel::getPartners);
    return dataList;
}

Update (update) Overloading

@Transactional(rollbackFor = {Throwable.class} )
@Action.Advanced(name = FunctionConstants.update, type = {FunctionTypeEnum.UPDATE}, managed = true, invisible = ExpConstants.idValueNotExist, check = true)
@Action(displayName = "Update", label = "Confirm", summary = "Modify", bindingType = {ViewTypeEnum.FORM})
@Function(name = FunctionConstants.update)
@Function.fun(FunctionConstants.update)
public TestModel update(TestModel data) {
    if (null == data) {
        return null;
    }
    // TODO doSomething, additional specific business logic can be appended here based on actual business needs.
    log.info("doSomething 4 TestModel update");
    // Only perform its own update operation, without automatically processing associated fields based on update strategies.
    data.updateById();
    // saveOnCascade adds or updates associated relationship fields (full volume) and processes old relationship data according to field cascade strategies (e.g., delete, SET_NULL). Using the fieldSave method requires self-handling of relationship deltas, such as deleting old records
    data.fieldSaveOnCascade(TestModel::getPartners);
    return data;
}

Warning

@Action.Advanced(check = true), if not configured, validation constraints (Validation configuration) will not be performed

Paged Query (queryPage) Overloading

/**
 * When the table view clicks search, the frontend will default to calling the queryPage of the given model
 */
@Function.Advanced(displayName = "Query record list and total count by conditions", type = {FunctionTypeEnum.QUERY}, category = FunctionCategoryEnum.QUERY_PAGE)
@Function.fun(FunctionConstants.queryPage)
@Function(openLevel = {FunctionOpenEnum.LOCAL, FunctionOpenEnum.REMOTE, FunctionOpenEnum.API})
public Pagination<TestModel> queryPage(Pagination<TestModel> page, IWrapper<TestModel> queryWrapper) {
    if (null == page) {
        return null;
    }
    // TODO doSomething, additional specific business logic can be appended here based on actual business needs.
    log.info("doSomething 4 TestModel queryPage");
    return new TestModel().queryPage(page, queryWrapper);
}

Single Query (queryOne) Overloading

@Function.Advanced(displayName = "Query a single record", type = {FunctionTypeEnum.QUERY}, category = FunctionCategoryEnum.QUERY_ONE)
@Function.fun(FunctionConstants.queryByEntity)
@Function(openLevel = {FunctionOpenEnum.LOCAL, FunctionOpenEnum.REMOTE, FunctionOpenEnum.API})
public TestModel queryOne(TestModel query) {
    if (null == query) {
        return null;
    }
    // TODO doSomething, additional specific business logic can be appended here based on actual business needs.
    log.info("doSomething 4 TestModel queryOne");
    return query.queryOne();
}

4、Determination of Namespace and Function Code

  • Namespace

    • Use @Model.model or @Fun annotations to configure the function namespace.
    • Prefer the @Model.model annotation value. First, look for this annotation in the current class. If not configured or the annotation value is empty, look for it in the parent class or interface; if still empty, take the @Fun annotation value, first in the current class, then in the parent class or interface if the current class is not configured or empty; if both are empty, take the fully qualified class name.
  • Function Code

    • Use the @Function.fun annotation to configure the function code.
    • First, look for this annotation in the current class method to obtain the function code. If not configured or the annotation value is empty in the current class method, look for it in the parent class or interface method; if both are empty, take the method name.

5、Function Open Levels

In daily development, to ensure security, we often set different open levels for methods or manage interfaces to be opened to the Web through application layering, placing them in independent applications. Oinone adopts a similar strategy, managing all logic through Function. Function supports defining three open levels: API, REMOTE, and LOCAL, with configurable multi-selection, described as follows:

  • Local Call (LOCAL): Supports invocation within the same application, commonly used for other modules within the application to invoke model-related logic.
  • Remote Call (REMOTE): Enabled by default, supports invocation through remote services, suitable for interaction between different services in a distributed system.
  • Open (API): Supports exposure to external systems through public interfaces, suitable for scenarios where services are provided externally.

Warning

Function open level rules: Enabling API implies default support for LOCAL and REMOTE; enabling REMOTE implies default support for LOCAL; LOCAL is optional for local use only.

Warning

For server actions (ServerAction), their corresponding functions are automatically set to the API open level. This is because such functions are associated with operation buttons on the frontend interface, and API must be opened to ensure frontend invocability.

6、Naming Conventions

Model AttributeDefault Value ConventionNaming Rule Convention
namespaceConfigurable via @Model.model or @Fun annotationLength ≤ 128 characters
nameDefaults to Java method nameContains only numbers and letters, starts with a letter, length ≤ 128 characters, does not start with get, set, unSet
funDefaults to nameLength ≤ 128 characters
summaryDefaults to displayNameNo semicolons, length ≤ 500 characters
Advanced.displayNameDefaults to nameLength ≤ 128 characters

7、Annotation Configuration

@Fun Function Declaration

└── value Namespace

@Model

└── model Namespace

@Function

├── name Technical name

├── scene Available scenarios

├── summary Description summary

├── openLevel Open level

├── Advanced More configurations

│ ├── displayName Display name

│ ├── type Function type, default FunctionTypeEnum.UPDATE

│ ├── managed Data manager function, default false

│ ├── check Validation, default false

│ ├── language Language, default FunctionLanguageEnum.JAVA

│ ├── builtin Whether built-in function, default no

│ ├── category Category, FunctionCategoryEnum.OTHER

│ ├── group System group, default: pamirs

│ ├── version System version, default: 1.0.0

│ ├── timeout Timeout, default: 5000

│ ├── retries Number of retries, default: 0

│ ├── isLongPolling Whether supports long polling, default false

│ ├── longPollingKey Supports obtaining fields from context as key, default userId

│ └── longPollingTimeout Long polling timeout, default value 1

└── fun

└── value

@PamirsTransactional

├── value @AliasFor("transactionManager")

├── transactionManager @AliasFor("value") default value: ""

├── propagation Transaction propagation type default value: Propagation.REQUIRED

├── isolation Transaction isolation level default value: Isolation.DEFAULT

├── timeout Expiration time default: -1

├── readOnly Read-only default: false

├── rollbackFor Rollback exception classes

├── rollbackForClassName Rollback exception class names

├── rollbackForExpCode Rollback exception codes

├── noRollbackFor Ignored exception classes

├── noRollbackForClassName Ignored exception class names

├── noRollbackForExpCode Ignored exception codes

└── enableXa Distributed transaction default is false

(Ⅳ) Function Metadata

1、FunctionDefinition

Element Data CompositionMeaningCorresponding AnnotationRemarks
namespaceFunction namespace@Fun("")
@Model.model("")
@Fun or @Model.model
nameTechnical name@Function(
name="",
scene={},
summary="",
openLevel=FunctionOpenEnum.REMOTE
)
sceneAvailable scenariosSee: FunctionSceneEnum
descriptionDescription
openLevelOpen levelSee: FunctionOpenEnum
funCode@Function.fun("")
displayNameDisplay name@Function.Advanced(
displayName="",
type=FunctionTypeEnum.UPDATE,
dataManager=false,
language=FunctionLanguageEnum.JAVA,
isBuiltin=false,
category=FunctionCategoryEnum.OTHER,
group="pamirs",
version="1.0.0",
timeout=5000,
retries=0,
isLongPolling=false,
longPollingKey="userId"
longPollingTimeout=1
)
typeFunction type
Default: 4 (update)
See: FunctionTypeEnum
dataManagerData manager function
Default: false
languageFunction language
Default: DSL
See: FunctionLanguageEnum
isBuiltinWhether built-in function
Default: false
categoryCategory
Default: OTHER
See: FunctionCategoryEnum
groupSystem group
Default: pamirs
versionSystem version
Default: 1.0.0
timeoutTimeout
Default: 5000
retriesNumber of retries
Default: 0
isLongPollingWhether supports long polling, default false
longPollingKeySupports obtaining fields from context as key
longPollingTimeoutLong polling timeout
Default value is 1
transactionConfigTransaction configuration
JSON storage
See: TransactionConfig
Configuration
@PamirsTransactional
sourceSourceSystem inferred value, see: FunctionSourceEnum
extPointListFunction includes extension pointsSystem inferred value
module所属模块 (Affiliated module)System inferred value
bitOptionsBitSystem inferred value
attributesAttributesSystem inferred value
importsContext referencesSystem inferred value
contextContext variablesSystem inferred value
codesFunction contentSystem inferred value
beanNameBean nameSystem inferred value
ruleFrontend rulesSystem inferred value, generally passed down by Action.rule
clazzFunction locationSystem inferred value
methodFunction methodSystem inferred value
argumentListFunction parametersSystem inferred value, List<`Argument`>
returnTypeReturn value typeSystem inferred value

2、TransactionConfig

Transaction configuration for function transaction management

Element Data CompositionMeaningCorresponding AnnotationRemarks
transactionManagerTransaction manager@PamirsTransactional(
transactionManager="",
enableXa=false,
isolation=Isolation.DEFAULT,
propagation=Propagation.REQUIRED,
timeout=-1,
readOnly=false,
rollbackFor={},
rollbackForClassName={},
noRollbackFor={},
noRollbackForClassName={},
rollbackForExpCode={},
noRollbackForExpCode={}
)
enableXaDistributed transaction
Default is false
isolationTransaction isolation level
propagationTransaction propagation type
timeoutExpiration time
Default: -1
readOnlyRead-only
Default: false
rollbackForExpCodeRollback exception codes
rollbackForExpCodeIgnored exception codes
namespaceFunction namespaceSystem inferred value
funFunction codeSystem inferred value
activeEffective
Default is true
System inferred value

II. Extpoint Extension Points

(Ⅰ) Default Extension Points

All functions in Oinone provide default pre-extension points, override extension points, and post-extension points. Their technical names follow the rule of adding "Before", "Override", and "After" suffixes to the function code fun of the extended function. For example:

1、Extension Point Definition Example

@Ext(TestModel.class)
public interface TestModelExtpoint {
    @ExtPoint(displayName = "Pre-extension point for TestModel's create function")
    public TestModel createBefore(TestModel data);
}
@Ext(TestModel.class)
public class TestModelExtpointImpl implements TestModelExtpoint {
    @Override
    @ExtPoint.Implement(displayName = "Implementation of TestModel's create function pre-extension point", expression = "context.requestFromModule==\"ce_expenses\"")
    public TestModel createBefore(TestModel data) {
        PamirsSession.getMessageHub().info("Implementation of TestModel's create function pre-extension point");
        return data;
    }
}

Use @Ext(TestModel.class) to mark the class where the extended function is located, thereby clarifying the namespace. Use @ExtPoint to define the extension point and @ExtPoint.Implement to define the extension point implementation. Set the extension point's activation condition and priority through expression and priority. In the expression, you can use context and function parameters (such as data in the example) as variables, where context's requestFromModule represents the module that initiated the request.

2、Quick Definition Method:

@Ext(TestModel.class)
public class TestModelExtpointImpl implements CreateBeforeExtPoint<TestModel> {
    @Override
    @ExtPoint.Implement(displayName = "Implementation of TestModel's create function pre-extension point", expression = "context.requestFromModule==\"ce_expenses\"")
    public TestModel createBefore(TestModel data) {
        PamirsSession.getMessageHub().info("Implementation of TestModel's create function pre-extension point");
        return data;
    }
}

3、Model Extension Point Quick Definition List

Extension PointName (name)Description
CountBeforeExtPointcountBeforePre-extension point for getting count
CreateAfterExtPointcreateAfterPost-extension point for creation
CreateBatchAfterExtPointcreateBatchAfterPost-extension point for batch creation
CreateBatchBeforeExtPointcreateBatchBeforePre-extension point for batch creation
CreateBeforeExtPointcreateBeforePre-extension point for creation
DeleteAfterExtPointJava method: deleteAfter name: deleteWithFieldBatchAfterPost-extension point for deletion
DeleteBeforeExtPointJava method: deleteBefore name: deleteWithFieldBatchBeforePre-extension point for deletion
PageAfterExtPointqueryPageAfterPost-extension point for paged query
PageBeforeExtPointqueryPageBeforePre-extension point for paged query
QueryByPkAfterExtPointqueryByPkAfterPost-extension point for query by primary key
QueryByPkBeforeExtPointqueryByPkBeforePre-extension point for query by primary key
QueryListAfterExtPointJava method: queryListAfter name: queryListByEntityAfterPost-extension point for list query
QueryListBeforeExtPointJava method: queryListBefore name: queryListByWrapperBeforePre-extension point for list query
QueryOneAfterExtPointJava method: queryOneAfter name: queryByEntityAfterPost-extension point for single query
QueryOneBeforeExtPointJava method: queryOneBefore name: queryByEntityBeforePre-extension point for single query
UpdateAfterExtPointupdateAfterPost-extension point for update
UpdateBatchAfterExtPointupdateBatchAfterPost-extension point for batch update
UpdateBatchBeforeExtPointupdateBatchBeforePre-extension point for batch update
UpdateBeforeExtPointupdateBeforePre-extension point for update
UpdateConditionAfterExtPointJava method: updateConditionAfter name: updateByWrapperAfterPost-extension point for conditional update
UpdateConditionBeforeExtPointJava method: updateConditionBefore name: updateByWrapperBeforePre-extension point for conditional update

Warning

Extension points implemented using the quick method do not support remote invocation

(Ⅱ) Custom Extension Points

During daily development, as our understanding of business deepens, we often reserve extension points in certain logic to flexibly replace local logic to meet various future needs.

1、Define Custom Extension Points

@Ext
public interface TestModelDoSomethingExtpoint {

    @ExtPoint(displayName = "doSomething")
    List<TestModel> doSomething();

}
@Ext(TestModelDoSomethingExtpoint.class)
public class TestModelDoSomethingExtpointImpl implements TestModelDoSomethingExtpoint {
    @Override
    @ExtPoint.Implement(displayName = "doSomething")
    public List<TestModel> doSomething() {
        // Specific business logic processing
        return null;
    }
}

2、Invoke Custom Extension Points

In business methods, use Ext.run to trigger extension point logic through functional interfaces:

// Trigger using Ext.run
public List<TestModel> includeCallExtpoint(){
    // Pre-business logic
    // Invoke extension point logic
    List<TestModel> testModels = Ext.run(TestModelDoSomethingExtpoint::doSomething, new Object[]{});
    // Subsequent business processing
    return testModels;
}

Through the above steps, you can complete the creation and invocation of custom extension points to achieve dynamic extension and flexible replacement of business logic.

(Ⅲ) Override Original Extension Points

An extension point can have multiple extension point implementations, and Oinone will ultimately select only one to execute based on conditions and priority. The default priority is 99, and the smaller the number, the higher the priority. For example:

@Ext(TestModelDoSomethingExtpoint.class)
public class TestModelDoSomethingExtpointImpl2 implements TestModelDoSomethingExtpoint {
    @Override
    @ExtPoint.Implement(displayName = "doSomething2", priority = 66 )
    public List<TestModel> doSomething() {
        // New business logic processing
        return null;
    }
}

(Ⅳ) Notes

Warning

Extension points can configure activation conditions through the expression attribute, which takes effect automatically when left empty. An extension point can have multiple extension point implementations, and Oinone will ultimately select only one to execute based on conditions and priority.

Warning

Sub-models not only inherit fields and functions from the parent model but also synchronously inherit the extension points of the inherited functions when inheriting functions.

Note

Backend programming-invoked function extension points are not enabled by default. You can manually set meta directives to enable them, as shown in the example:

Models.directive().run(() -> {
    return Fun.run(namespace, fun, parameters);
}, SystemDirectiveEnum.EXT_POINT);

Warning

Do not name function parameters context, as this naming conflicts with Oinone's built-in context, causing expression execution exceptions.

III. Hook Interceptors

As an important part of the platform, interceptors can extend corresponding logic before and after function execution for functions that meet specific conditions in a non-intrusive manner.

(Ⅰ) Interceptor Usage Method

By adding the @Hook annotation to a method, you can mark the method as an interceptor. Specifically, pre-extension points need to implement the HookBefore interface, while post-extension points need to implement the HookAfter interface. Their input parameters include the definition of the intercepted function and the function's input parameters, allowing interceptors to flexibly process execution logic based on function definitions and input parameters.

(Ⅱ) Interceptor Classification and Characteristics

Interceptors are mainly divided into two categories: pre-interceptors and post-interceptors, which differ in function and data processing:

1、Pre-Interceptor:

Its input and output parameters are the input parameters of the intercepted function. By implementing the HookBefore interface, logic processing is performed before function execution. For example:

@Component
public class BeforeXXXHook implements HookBefore {

    @Hook
    @Override
    public Object run(Function function, Object... args) {
        // Process input parameters and execute interceptor logic here
        return args;
    }

}

2、Post-Interceptor:

Its input and output parameters are the output parameters of the intercepted function. By implementing the HookAfter interface, the return value is processed after function execution. For example:

@Component
public class AfterXXXHook implements HookAfter {

    @Hook
    @Override
    public Object run(Function function, Object ret) {
        // Process the return value and execute interceptor logic here
        return ret;
    }

}

(Ⅲ) Interceptor Filtering and Activation Rules

You can use the non-mandatory fields module, model, fun, function type, and active of the @Hook annotation to filter interceptors that need to take effect for the current intercepted method. If no filtering attributes are configured, the interceptor will take effect for all functions.

(Ⅳ) Interceptor Execution Order Adjustment

The execution order of interceptors can be flexibly adjusted according to the interceptor's priority attribute. The smaller the priority number, the earlier the interceptor executes. For example, when there are multiple pre-interceptors, the pre-interceptor with the smallest priority value will execute its logic first. This mechanism allows developers to finely control the logical extension of functions at different stages based on business needs, thereby improving system flexibility and maintainability.

(Ⅴ) Notes

Warning

Since interceptors intercept all functions, if there are too many interceptors, it will inevitably lead to performance degradation.

Note

Backend programming-invoked function interceptors are not enabled by default. You can manually set meta directives to enable them, as shown in the example:

Models.directive().run(() -> {
    return Fun.run(namespace, fun, parameters);
}, SystemDirectiveEnum.HOOK);

IV. Trigger

A trigger is a mechanism that drives logic execution based on function execution events. The following details configuration, task types, etc.

(Ⅰ) Enable Trigger Configuration

1、YMAL Configuration

Complete event configuration, data record configuration, and add dependencies on the sql_record and trigger modules. The specific configuration is as follows:

spring:
  rocketmq:
    name-server: 127.0.0.1:9876 # RocketMQ NameServer address for producers and consumers to locate the cluster
    producer:
      enableMsgTrace: true       # Enable message trace tracking for troubleshooting message sending issues
      customizedTraceTopic: TRACE_PRODUCER # Custom message trace topic
    consumer:
      enableMsgTrace: true       # Enable message trace tracking on the consumer side
      customizedTraceTopic: TRACE_CONSUMER # Consumer-side custom trace topic
pamirs:
  event:
    enabled: true       # Globally control the enable status of the event function, default is true, true for enable, false for disable
    topic-prefix: oinone # Uniform prefix for all event topics to facilitate standardized topic name management
    notify-map:
      system: ROCKETMQ  # Message queue type for system messages,可选 (optional) ROCKETMQ, KAFKA, or RABBITMQ
      biz: ROCKETMQ     # Message queue type for business messages,可选 (optional) ROCKETMQ, KAFKA, or RABBITMQ
      logger: ROCKETMQ  # Message queue type for log messages,可选 (optional) ROCKETMQ, KAFKA, or RABBITMQ
    schedule:
      enabled: true    # Trigger switch, default is true
  record:
    sql:
      # This path specifies the storage directory for SQL log files and can be modified as needed
      store: /oinone/sql/record
  boot:
    modules:
      - sql_record
      - trigger

Warning

The example uses rocketmq as the message queue, which can be switched to other message queues like Kafka or RabbitMQ.

2、Dependency Addition

Introduce the following dependencies in the startup project:

<dependency>
    <groupId>pro.shushi.pamirs.core</groupId>
    <artifactId>pamirs-sql-record-core</artifactId>
</dependency>
<dependency>
  <groupId>pro.shushi.pamirs.core</groupId>
  <artifactId>pamirs-trigger-core</artifactId>
</dependency>
<dependency>
  <groupId>pro.shushi.pamirs.core</groupId>
  <artifactId>pamirs-trigger-bridge-tbschedule</artifactId>
</dependency>

(Ⅱ) Triggered Tasks

1、Trigger Annotation

Prerequisites:

  1. The function must be successfully registered with the system through the Function annotation in the program.
  2. The input and output parameters of the function definition must be of the same model type.

Usage Example:

@Fun("test.TestModel")
public class TestModelFunction {

    @Trigger(name = "Trigger on create", condition = TriggerConditionEnum.ON_CREATE)
    @Function
    public TestModel onCreate(TestModel data) {
        return data;
    }

    @Trigger(name = "Trigger on update", condition = TriggerConditionEnum.ON_UPDATE)
    @Function
    public TestModel onUpdate(TestModel before, TestModel after) {
        return data;
    }

    @Trigger(name = "Trigger on delete", condition = TriggerConditionEnum.ON_DELETE)
    @Function
    public TestModel onDelete(TestModel data) {
        return data;
    }
}

Explanation:

  • Line 1: Register the function via annotation, used to define the function namespace, which is currently the model code.
  • Lines 5, 11, 17: Register specific functions via annotation, marking functions to be registered with the system.
  • Line 4: Register a trigger task via annotation, where the name attribute corresponds to the displayName in the model; the condition attribute corresponds to the condition in the model; the active attribute corresponds to the active in the model;

2、Business Module Configuration

The business module project needs to introduce the api package of the trigger module:

<dependency>
		<groupId>pro.shushi.pamirs.core</groupId>
		<artifactId>pamirs-trigger-api</artifactId>
</dependency>

(Ⅲ) Scheduled Tasks

1、Definition of Scheduled Tasks

Oinone XSchedule is a functional module provided by the Oinone framework for implementing scheduled task scheduling, allowing developers to easily define and execute scheduled tasks in applications. The following is the usage description of Oinone XSchedule:

Define scheduled tasks by adding the @XSchedule annotation to methods. The @XSchedule annotation currently only supports configuring task execution time through the cron attribute, as shown in the example:

@Component
@Fun(CronJobExample.FUN_NAMESPACE)
public class CronJobExample {
    String FUN_NAMESPACE = "test.CronJobExample";

    @XSchedule(cron = "0 0 10 * * *") // Execute at 10 AM daily
    @Function
    public void cronJob() {
        System.out.println("Cron job is running...");
    }
}

The above is the basic usage of Oinone XSchedule. Through these steps, you can easily implement scheduled task scheduling in Oinone applications.

Warning

Oinone XSchedule has distributed characteristics and can automatically allocate a machine from the cluster to execute scheduled tasks. There is no need to manually handle task allocation and coordination in a distributed environment, making it suitable for complex systems with multi-node deployments, ensuring efficient and orderly task execution and reducing development and operation costs.

Note

Oinone XSchedule requires scheduled task functions to be Oinone functions without input parameters. When defining functions, be sure to follow this rule; otherwise, tasks will not execute normally, affecting the stability of the system scheduling function.

2、Cron Expression Details

A Cron expression is a string used to define the execution time of scheduled tasks, composed of 6 mandatory fields (seconds, minutes, hours, day of month, month, day of week) and 1 optional field (year), with fields separated by spaces. Complex time rules can be flexibly configured through special character combinations, as described in detail below:

Expression Structure and Value Range

Field NameValue RangeExample
Seconds (Seconds)0 - 590 (0th second of the hour)
Minutes (Minutes)0 - 5915 (15th minute)
Hours (Hours)0 - 23 (0 represents midnight)8 (8 AM)
Day of month (Day of month)1 - 311 (1st day of each month)
Month (Month)1 - 12 or English abbreviationsJAN (January)
Day of week (Day of week)0 - 6 (0 represents Sunday) or English abbreviationsSUN (Sunday)
Year (Year)1970 - 2099 (can be omitted)2024 (specified year)

Core Special Characters

CharacterMeaning and UsageExample and Explanation
*Wildcard, representing all possible values for the field* * * * * *: Execute every minute and second; 0 0 * * * *: Execute at the 0th second of every hour
,Separate multiple values0 0 8,12,16 * * *: Execute at 8 AM, 12 PM, and 4 PM daily
-Specify a continuous range0 0 9-17 * * *: Execute once every hour from 9 AM to 5 PM
/Define interval frequency0 0/15 * * * ?: Execute every 15 minutes (starting from the 0th minute, i.e., 0, 15, 30, 45 minutes)
?Only used in date and week fields, indicating no specified value (both cannot set specific values simultaneously)0 0 12 * * ?: Execute at 12 PM daily, no specified week; 0 0 12 1 * ?: Execute at 12 PM on the 1st day of each month
#Only used in the week field, specifying the nth week X of the month0 0 12 ? * 1#3: Execute at 12 PM on the third Monday of each month
LOnly used in date and week fields, representing "last"Date: 0 0 12 L * *: Execute at 12 PM on the last day of each month; Week: 0 0 12 ? * 6L: Execute at 12 PM on the last Saturday of each month

Common Application Scenarios

Cron ExpressionExecution RuleApplication Scenario
0 0 12 * * *Execute at 12 PM dailyDaily data statistics, regular backups
0 15 10 * * *Execute at 10:15 AM dailyMorning task triggering
0 0 0 1 1 *Execute at 12 AM on January 1st each yearAnnual data initialization, cross-year tasks
0 0 12 ? * WEDExecute at 12 PM every WednesdayPeriodic maintenance, weekly report generation
0 0 12 L * ?Execute at 12 PM on the last day of each monthEnd-of-month data settlement
0 0 12 ? * 2#3Execute at 12 PM on the third Tuesday of each monthTask scheduling for specific weeks

Usage Notes

  • System Differences: There are subtle differences in Cron expression support between different frameworks (e.g., Spring, Linux Cron), so refer to the corresponding documentation.
  • Date and Week Conflicts: When setting both date and week fields, one must be replaced with ? to avoid logical contradictions.
  • Time Zone Impact: Expression time calculations depend on the server time zone, and cross-regional tasks require additional time zone conversion processing.
  • Complex Rule Verification: Use online tools (e.g., CronMaker) to verify expression accuracy and prevent configuration errors.

By reasonably combining special characters, Cron expressions can meet various定时 (scheduled) requirements from simple to complex, widely used in automated task scenarios such as database maintenance, log cleanup, and data synchronization.

(Ⅳ) Asynchronous Tasks

Asynchronous tasks are commonly used patterns in distributed development, widely applied in high-concurrency processing, time-consuming task decoupling, and other scenarios. The following is a detailed usage description of asynchronous tasks in Oinone:

1、Definition of Asynchronous Tasks

Define asynchronous tasks through annotations and interface implementations, as shown in the example:

@Fun(XAsyncService.FUN_NAMESPACE)
public interface XAsyncService {
    String FUN_NAMESPACE = "test.XAsyncService";
    @Function
    public void testXAsync(TestModel testModel);
}



@Fun(XAsyncService.FUN_NAMESPACE)
@Component
public class XAsyncServiceImpl implements XAsyncService {
    @Override
    @Function
    @XAsync(displayName = "Asynchronous example", limitRetryNumber = 3, nextRetryTimeValue = 60)
    public void testXAsync(TestModel testModel) {
        // do something
    }
}

Annotation Parameter Description:

  • displayName: Define the display name of the asynchronous task for easy identification and management.
  • limitRetryNumber: Set the maximum number of retries after task failure, with a default value of -1 (infinite retries).
  • nextRetryTimeValue: Specify the interval duration for task retries after failure, with a default value of 60.
  • nextRetryTimeUnit: Define the time unit for retry intervals, with a default value of TimeUnitEnum.SECOND (seconds).
  • delayTime: Set the duration for delayed task execution, with a default value of 0 (immediate execution).
  • delayTimeUnit: Define the time unit for delayed execution, with a default value of TimeUnitEnum.SECOND (seconds).

2、Execution Unit Isolation in Multi-Application Scenarios

In a distributed environment with multiple modules started independently (boot), to avoid repeated execution of asynchronous tasks, implement task data isolation through configuration:

pamirs:
  event:
    schedule:
      ownSign: demo

By setting the pamirs.event.schedule.ownSign parameter, different applications can isolate task data based on custom identifiers (e.g., demo). After configuration, each application only processes its own asynchronous tasks, ensuring task execution uniqueness and accuracy, and improving system operation efficiency and stability.

V. Built-in Functions

Built-in functions are pre-defined functions in the system, supporting direct invocation in expressions, covering various functional types, as follows:

(Ⅰ) General Functions

1、Mathematical Functions

ExpressionNameDescriptionExample
ABSAbsolute valueGet the absolute value of a numberABS(number)
FLOORFloorRound a number downFLOOR(number)
CEILCeilingRound a number upCEIL(number)
ROUNDRoundRound a number to the nearest integerROUND(number)
MODModuloCalculate the remainder of A divided by BMOD(A, B)
SQRTSquare rootCalculate the square root of a numberSQRT(number)
SINSineCalculate the sine value of a numberSIN(number)
COSCosineCalculate the cosine value of a numberCOS(number)
PIPiReturn the value of piPI()
ADDAddCalculate the sum of A and BADD(A, B)
SUBTRACTSubtractCalculate the difference between A and BSUBTRACT(A, B)
MULTIPLYMultiplyCalculate the product of A and BMULTIPLY(A, B)
DIVIDEDivideCalculate the quotient of A divided by BDIVIDE(A, B)
MAXMaxReturn the maximum value in a collection (parameter is a collection or array)MAX(collection)
MINMinReturn the minimum value in a collection (parameter is a collection or array)MIN(collection)
SUMSumReturn the total of a collection (parameter is a collection or array)SUM(collection)
AVGAverageReturn the average of a collection (parameter is a collection or array)AVG(collection)
COUNTCountReturn the total number of elements in a collection (parameter is a collection or array)COUNT(collection)
UPPER_MONEYUppercase amountConvert a number or numeric string to uppercase amount formatUPPER_MONEY(number)
POWPower OperationCalculate the power of number A to number BPOW(A, B)
LOGLogarithmic OperationCalculate the logarithm of number A with base number BLOG(A, B)

2、Text Functions

ExpressionNameDescriptionExample
TRIMEmpty string filterRemove leading and trailing spaces from a text string, return empty for empty stringTRIM(text)
IS_BLANKIs empty stringDetermine if a text string is emptyIS_BLANK(text)
STARTS_WITHStarts with specified stringDetermine if text starts with start string, handle empty text as empty stringSTARTS_WITH(text, start)
ENDS_WITHEnds with specified stringDetermine if text ends with end string, handle empty text as empty stringENDS_WITH(text, end)
CONTAINSContainsDetermine if text contains subtext, handle empty text as empty stringCONTAINS(text, subtext)
LOWERLowercaseConvert text string to lowercase, handle empty text as empty stringLOWER(text)
UPPERUppercaseConvert text string to uppercase, handle empty text as empty stringUPPER(text)
REPLACEReplace stringReplace oldtext with newtext in textREPLACE(text, oldtext, newtext)
LENGet string lengthGet the length of a text string, handle empty text as empty stringLEN(text)
JOINJoin stringsJoin text with join string, handle empty text as empty stringJOIN(text, join)
PARSEDeserialize JSON stringConvert a JSON string to a collection or MapPARSE(text)
JSONSerialize to JSON stringConvert an object to a JSON stringJSON(object)
NOT_CONTAINSNot ContainDetermine whether the text string "text" does not contain the text string "subtext". When "text" is empty, it is treated as an empty stringNOT_CONTAINS(text, subtext)
SUBSTRING_ENDExtract Substring from Specified Position to EndExtract the substring of the "Hello" string from position 1 to the end, returning "ello"SUBSTRING_END("Hello", 1)
SUBSTRINGExtract Substring from Specified PositionsExtract the substring of the "Hello" string from position 1 to position 3, returning "el"SUBSTRING("Hello", 1, 3)

3、Regular Expression Functions

ExpressionNameDescription
MATCHESRegular expression matchingFunction scenario: Expression function example: MATCHES(text, regex) Function description: Verify if a string meets regular expression matching, e.g., regex is [a-zA-Z][a-zA-Z0-9]*$ to verify if text matches
CHECK_PHONEPhone number verificationFunction scenario: Expression function example: CHECK_PHONE(text) Function description: Verify if a phone number is correct
CHECK_EMAILEmail verificationFunction scenario: Expression function example: CHECK_EMAIL(text) Function description: Verify if an email is correct
CHECK_USER_NAMEUser name verificationFunction scenario: Expression function example: CHECK_USER_NAME(text) Function description: Verify if a user name is correct
CHECK_PWDPassword strength verificationFunction scenario: Expression function example: CHECK_PWD(text) Function description: Determine if a password meets strength verification
CHECK_INTEGERInteger verificationFunction scenario: Expression function example: CHECK_INTEGER(text) Function description: Verify if it is an integer
CHECK_ID_CARDID card verificationFunction scenario: Expression function example: CHECK_ID_CARD(text) Function description: Verify if an ID card is correct
CHECK_URLLegal URL verificationFunction scenario: Expression function example: CHECK_URL(text) Function description: Verify if a URL is correct
CHECK_CHINESEChinese verificationFunction scenario: Expression function example: CHECK_CHINESE(text) Function description: Verify if it is Chinese text
CHECK_NUMBERPure number verificationFunction scenario: Expression function example: CHECK_NUMBER(text) Function description: Verify if it is a pure number
CHECK_TWO_DIGVerify if two decimal placesFunction scenario: Expression function example: CHECK_TWO_DIG(text) Function description: Verify if two decimal places
CHECK_IPIP address verificationFunction scenario: Expression function example: CHECK_IP(text) Function description: Verify if an IP address is correct
CHECK_CONTAINS_CHINESEContains Chinese verificationFunction scenario: Expression function example: CHECK_CONTAINS_CHINESE(text) Function description: Verify if it contains Chinese
CHECK_SIZE_MAXOnly input n charactersFunction scenario: Expression function example: CHECK_SIZE_MAX(text,n) Function description: Only input n characters
CHECK_SIZE_MINAt least input n charactersFunction scenario: Expression function example: CHECK_SIZE_MIN(text,n) Function description: At least input n characters
CHECK_SIZEInput m-n charactersFunction scenario: Expression function example: CHECK_SIZE(text,m,n) Function description: Input m-n characters
CHECK_CODECan only be composed of English, numbers, and underscoresFunction scenario: Expression function example: CHECK_CODE(text) Function description: Can only be composed of English, numbers, and underscores
CHECK_ENG_NUMCan only contain English and numbersFunction scenario: Expression function example: CHECK_ENG_NUM(text) Function description: Can only contain English and numbers

4、Time Functions

ExpressionNameDescriptionExample
NOWReturn current timeGet the current system timeNOW()
NOW_STRReturn current time stringGet the current time string (format: yyyy-MM-dd hh:mm:ss)NOW_STR()
TODAY_STRReturn today's date stringGet the current date string (format: yyyy-MM-dd)TODAY_STR()
ADD_DAYAdd/subtract specified daysAdd or subtract specified days from a date (negative for subtraction)ADD_DAY(date, days)
ADD_MONTHAdd/subtract specified monthsAdd or subtract specified months from a date (negative for subtraction)ADD_MONTH(date, months)
ADD_YEARAdd/subtract specified yearsAdd or subtract specified years from a date (negative for subtraction)ADD_YEAR(date, years)
TO_DATEConvert to timeConvert a string to a time object according to a specified formatTO_DATE(date, pattern)
ADD_WORK_DAYAdd/subtract working daysAdd or subtract working days from a date (automatically skip weekends)ADD_WORK_DAY(date, days)
YEARExtract YearExtract the year from the dateYEAR(date)
MONTHExtract MonthExtract the month from the dateMONTH(date)
DAYExtract DayExtract the day from the dateDAY(date)

5、Collection Functions

ExpressionNameDescriptionExample
LIST_GETGet collection elementGet the element at the specified index in the collectionLIST_GET(list, index)
LIST_IS_EMPTYDetermine if collection is emptyDetermine if the collection is emptyLIST_IS_EMPTY(list)
LIST_CONTAINSDetermine if collection contains elementDetermine if the collection contains the specified elementLIST_CONTAINS(list, item)
LIST_ADDAdd element to collectionAdd an element to the end of the collectionLIST_ADD(list, item)
LIST_ADD_BY_INDEXAdd element to specified positionAdd an element to the specified index in the collectionLIST_ADD_BY_INDEX(list, index, item)
LIST_REMOVERemove collection elementRemove the specified element from the collectionLIST_REMOVE(list, item)
LIST_COUNTGet collection element countReturn the total number of elements in the collectionLIST_COUNT(list)
LIST_IDSGet collection all idsGet a list of ids of all objects in the collectionLIST_IDS(list)
LIST_FIELD_VALUESConvert attribute collectionConvert an object collection to a collection of specified attribute valuesLIST_FIELD_VALUES(list, model, field)
LIST_FIELD_EQUALSDetermine attribute value matchDetermine if attribute values in the object collection match the specified value, returning a boolean collectionLIST_FIELD_EQUALS(list, model, field, value)
LIST_FIELD_NOT_EQUALSDetermine attribute value mismatchDetermine if attribute values in the object collection do not match the specified value, returning a boolean collectionLIST_FIELD_NOT_EQUALS(list, model, field, value)
LIST_FIELD_INDetermine attribute value in collectionDetermine if attribute values in the object collection are in the specified collection, returning a boolean collectionLIST_FIELD_IN(list, model, field, list)
LIST_FIELD_NOT_INDetermine attribute value not in collectionDetermine if attribute values in the object collection are not in the specified collection, returning a boolean collectionLIST_FIELD_NOT_IN(list, model, field, list)
LIST_ANDBoolean collection logical ANDPerform logical AND operation on a boolean collection, returning a boolean valueLIST_AND(list)
LIST_ORBoolean collection logical ORPerform logical OR operation on a boolean collection, returning a boolean valueLIST_OR(list)
STRING_LIST_TO_NUMBER_LISTCharacter collection to numeric collectionConvert a character collection to a numeric collection, returning the original collection on conversion failureSTRING_LIST_TO_NUMBER_LIST(list)
COMMACollection element comma concatenationConcatenate collection elements with commas (elements must be Number or String)COMMA(list)
CONCATCollection element specified symbol concatenationConcatenate collection elements with a specified symbol (elements must be Number or String)CONCAT(list, split)

6、Key-Value Pair Functions

ExpressionNameDescriptionExample
MAP_GETGet key-value pair valueGet the value of a specified key from a key-value pairMAP_GET(map, key)
MAP_IS_EMPTYDetermine if key-value pair is emptyDetermine if a key-value pair is emptyMAP_IS_EMPTY(map)
MAP_PUTAdd key-value to key-value pairAdd a new key-value pair to the key-value pairMAP_PUT(map, key, value)
MAP_REMOVERemove key-value pair elementRemove a specified key from the key-value pairMAP_REMOVE(map, key)
MAP_COUNTGet key-value pair countReturn the total number of key-value pairs in the key-value pairMAP_COUNT(map)

7、Object Functions

ExpressionNameDescriptionExample
IS_NULLIs NullDetermine if an object is null, return true if nullIS_NULL(object)
EQUALSEqualsDetermine if two objects are equalEQUALS(A, B)
FIELD_GETGet Object AttributeGet attribute value from an object via dot expressionFIELD_GET(obj, dotExpression)

8、Logical Functions

ExpressionNameDescriptionExample
IFIf ConditionReturn different results based on conditions, supports nestingIF(condition, result1, result2)
ANDLogical ANDReturn the logical AND result of two conditionsAND(condition1, condition2)
ORLogical ORReturn the logical OR result of two conditionsOR(condition1, condition2)
NOTLogical NOTReturn the logical NOT result of a conditionNOT(condition)

(Ⅱ) Scenario-Specific Functions

1、Business Functions

ExpressionNameDescriptionExample
CURRENT_CORP_IDGet Current User Company IDGet the ID of the company to which the current user belongsCURRENT_CORP_ID()
CURRENT_CORPGet Current User CompanyGet the company object of the current userCURRENT_CORP()
CURRENT_SHOP_IDGet Current User Shop IDGet the ID of the shop to which the current user belongsCURRENT_SHOP_ID()
CURRENT_SHOPGet Current User ShopGet the shop object of the current userCURRENT_SHOP()

View implementation code: pro.shushi.pamirs.framework.faas.fun.builtin.business.BusinessFunctions, which requires implementing the corresponding SPI.

2、Context Functions

ExpressionNameDescriptionExample
CURRENT_UIDGet Current User IDGet the ID of the currently logged-in userCURRENT_UID()
CURRENT_USER_NAMEGet Current User NameGet the user name of the currently logged-in userCURRENT_USER_NAME()
CURRENT_USERGet Current UserGet the user object of the currently logged-in userCURRENT_USER()
CURRENT_ROLE_IDSGet Current User Role IDs ListGet all role IDs of the current userCURRENT_ROLE_IDS()
CURRENT_ROLESGet Current User Roles ListGet all roles of the current userCURRENT_ROLES()
CURRENT_PARTNER_IDGet Current User Partner IDGet the ID of the partner to which the current user belongsCURRENT_PARTNER_ID()
CURRENT_PARTNERGet Current User PartnerGet the partner object of the current userCURRENT_PARTNER()

View implementation code: pro.shushi.pamirs.framework.faas.fun.builtin.ContextFunctions.

(Ⅲ) Extending Built-in Functions

Developing expression functions in Oinone follows a similar process to defining regular functions, with the key difference being the specification of the function namespace: the namespace must be set to NamespaceConstants.expression to clarify that the function is for expression calculation scenarios. The specific example is as follows:

@Fun(NamespaceConstants.expression)
public class TestExpressionFunctions {
    // Get current user language
    @fun("CURRENT_USER_LANG")
    @Function(
        name = "CURRENT_USER_LANG",
        scene = {FunctionSceneEnum.EXPRESSION},
        openLevel = {FunctionOpenEnum.LOCAL},
        summary = "Function example: CURRENT_USER_LANG()\nFunction description: Get current user language"
    )
    public static ResourceLang currentUserLang() {
        return CommonApiFactory.getApi(UserService.class).queryById((Long)PamirsSession.getUserId()).getLang();
    }
}

VI. Expressions

An expression is a combination of numbers, operators, functions, number grouping symbols (parentheses), free variables, and bound variables, arranged meaningfully to obtain a numerical value. Bound variables have specified values within the expression, while free variables can have values specified outside the expression.

Expressions can use operators (+, -, *, /, &&, ||, !, ==, !=), dot expressions (e.g., ModelA.fieldC.relatedModelFieldA), and built-in functions. The expression format is like: IF(ISNULL(ModelA.fieldX), ModelA.fieldY.relatedModelFieldZ, ModelA.fieldM).

In expressions, the front-end display of model fields uses the display name displayName, while the original expression content uses the technical name name.

(Ⅰ) Dot Expressions

A dot expression is a subset of expressions, composed of a variable name and a dot. The variable before the dot and the variable after the dot have a subordinate relationship, with the variable after the dot being subordinate to the one before. Dot expressions can be used to obtain the value of the variable after the last dot determined by the full expression.

(Ⅱ) Regular Expressions

Corresponding Built-in FunctionDescriptionRegular Expression
CHECK_PHONEPhone Number Validation^(1[3-9])\\d{9}$
CHECK_EMAILEmail Validation^[a-z0-9A-Z]+[-|a-z0-9A-Z._]+@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\.)+[a-z]{2,}$
CHECK_USER_NAMEUser Name ValidationNon-empty validation
CHECK_PWDPassword Strength Validation (Strong Password)^(?=.*[a-z])(?=.*[A-Z])[a-zA-Z0-9~!@&%#_(.)]{8,16}$
CHECK_INTEGERInteger Validation^-{0,1}[1-9]\d*$
CHECK_ID_CARDID Card Validation^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$
CHECK_URLValid URL Validation^(?:(?:https?)://)(?:(?:1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|[1-9])(?:\.(?:1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)){2}(?:\.(?:1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*)(?::([1-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5]))?(?:/\S*)?$
CHECK_CHINESEChinese Validation^[\u4e00-\u9fa5]{0,}$
CHECK_NUMBERPure Number Validation^[0-9]*$
CHECK_TWO_DIGVerify Two Decimal Places^[0-9]+(\.[0-9]{2})?$
CHECK_IPIP Address Validation^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$
CHECK_CONTAINS_CHINESEContains Chinese Validation^.?[\u4e00-\u9fa5]{0,}.?$
CHECK_SIZEOnly Input n Characters^.{n}$
CHECK_SIZE_MINAt Least Input n Characters^.{n,}$
CHECK_SIZE_MAXAt Most Input n Characters^.{0,n}$
CHECK_SIZE_RANGEInput m-n Characters^.{m,n}$
CHECK_CODEOnly Composed of English, Numbers, and Underscores^[a-z0-9A-Z_]*$
CHECK_ENG_NUMOnly Contains English and Numbers^[a-z0-9A-Z]*$

(Ⅲ) Built-in Variables

In expressions, dot expressions can be used to obtain attributes of built-in variables and attributes of their sub-attributes. For example, use activeRecord to get the current record, and activeRecord.id to get the ID of the currently selected row record.

1、Data Variables

VariableNameDescription
activeRecordCurrent Selection ValueWhen initializing the view after selecting a single row record, the value is the single selected record; when initializing after selecting multiple row records, the value is the list of selected records; during full form validation, the value is the submitted record of the current form; during single field validation, the value is the current field value. When used as an action filtering condition, the value is the action model definition data.
activeModelModel of Current Selection ValueThe model of the currently selected record, which may be empty
activeFieldField of Current Selection ValueThe field of the currently selected record, which may be empty

2、Context Variables

VariableNameDescription
moduleModuleUsage example: context.module Example description: Execution module in the request context
requestFromModuleRequest Initiating ModuleUsage example: context.requestFromModule Example description: Request initiating module in the request context
langLanguageUsage example: context.lang Example description: Language in the request context
countryCountryUsage example: context.country Example description: Country in the request context
envEnvironmentUsage example: context.env Example description: Environment in the request context
extendExtended InformationUsage example: context.extend.extendedVariableName Example description: Extended information in the request context

(Ⅳ) Built-in Functions

The built-in functions described in the built-in functions section can be used in expressions. For example, use ABS(activeRecord.amount) to get the absolute value of the amount in the currently selected record.

VII. Transaction Control

In the Oinone platform, transaction management is a core function to ensure data consistency and reliability. Its design is deeply compatible with the Spring transaction mechanism while being enhanced for multi-data source scenarios to ensure stable operation in high-concurrency environments.

(Ⅰ) Function-Level Transaction Configuration

Transaction configuration for functions (Function) follows the principle of minimal intrusion, with transactions disabled by default:

  • isTransaction: Transaction switch, default off (false)
  • propagationBehavior: Transaction propagation behavior, default PROPAGATION_SUPPORTS, which prefers to use an existing transaction or execute in non-transactional mode if none exists
  • isolationLevel: Transaction isolation level, default inherits the database's native configuration

The platform also provides global transaction configuration capabilities, supporting unified adjustment of transaction strategies to meet project-level customization needs.

(Ⅱ) Transaction Management Capabilities

In Oinone's transaction management system, multi-mode compatibility and multi-data source enhancement are two core advantages, jointly providing solid guarantees for data consistency and system stability in complex business scenarios.

Oinone seamlessly integrates with Spring's declarative and programmatic transactions, providing developers with rich and flexible transaction management methods.

1、Declarative Transactions

Developers can easily apply transaction management to classes or methods through the @PamirsTransactional annotation, which is fully compatible with Spring's @Transactional annotation. The following is an example:

@Fun(TestModelHelloService.FUN_NAMESPACE)
@Component
public class TestModelHelloServiceImpl implements TestModelHelloService {

    @Override
    @PamirsTransactional
    //@Transactional // Fully compatible with Spring's annotation
    @Function(openLevel = FunctionOpenEnum.API)
    @Function.Advanced(type = FunctionTypeEnum.QUERY)
    public TestModel sayHello(TestModel data) {
        // Business logic processing
        return data;
    }
}

2、Programmatic Transactions

Oinone's PamirsTransactionTemplate has a consistent programming interface with Spring's TransactionTemplate. In high-concurrency scenarios, the programmatic transaction development mode has significant performance advantages, allowing developers to finely control the duration of transaction opening and complete time-consuming query work and data preparation as much as possible before transaction opening. The basic usage pattern is as follows:

Tx.build(new TxConfig().setPropagation(Propagation.REQUIRED.value())).executeWithoutResult(status -> {
    // Execution logic
});

In this way, developers can more flexibly control transaction boundaries, thereby improving overall system performance.

3、Multi-Data Source Enhancement

Oinone's transaction management demonstrates strong adaptability and reliability in multi-data source environments.

  • Multi-Database Transaction Support

A key difference between PamirsTransactional and Spring's Transactional is that PamirsTransactional supports multi-database transactions. Although such multi-database transactions are not strictly distributed multi-database transactions, they can meet the needs of most practical business scenarios.

  • Intelligent Lock Mechanism

Oinone supports nested independent transactions for multiple data sources and effectively avoids deadlock risks through an intelligent lock mechanism. This mechanism ensures stable system operation in complex multi-data source environments and prevents system failures caused by deadlocks.

  • Built-in Isolation Strategy

To ensure data consistency in multi-data source and table sharding scenarios, Oinone has built-in powerful isolation strategies to eliminate dirty reads. This strategy guarantees data accuracy and integrity during concurrent operations, providing a strong guarantee for normal business operations.

(Ⅲ) Core Transaction Characteristics

CharacteristicDefinitionApplication Value
AtomicityTransaction operations are indivisible; all operations are either all committed or all rolled backAvoid data inconsistency caused by partial success
ConsistencyData integrity remains consistent before and after transaction executionEnsure business logic correctness
IsolationConcurrent transactions are isolated from each other and do not interfereImprove data access reliability
DurabilityData is permanently saved to disk after transaction commitEnsure data security and no loss

(Ⅳ) Transaction Isolation Levels

1、Concurrent Access Risks

Without configured isolation mechanisms, concurrent transactions may cause the following issues:

Issue TypeDescriptionTypical Scenario
Dirty ReadA transaction reads updated data from other uncommitted transactionsBalance query during an uncommitted bank transfer
Non-Repeatable ReadInconsistent query results within the same transactionData modification during order query
Phantom ReadNew or disappearing data occurs during transaction executionChange in the number of data rows during batch operations

2、Supported Isolation Levels

Isolation LevelDescriptionRisk MitigationPerformance Impact
DEFAULTAdopts the database's default isolation (Read Committed for Oracle, Repeatable Read for MySQL)Basic protectionLow
READ_UNCOMMITTEDThe lowest level, allowing dirty readsNo protectionLowest
READ_COMMITTEDOnly reads committed data, with risks of non-repeatable reads and phantom readsAvoids dirty readsMedium
REPEATABLE_READEnsures consistent query results within the same transaction, but phantom reads are still possibleSolves non-repeatable readsMedium-high
SERIALIZABLEThe highest level, completely eliminating all concurrency issues through serializationFull protectionHighest

(Ⅴ) Transaction Propagation Behavior

Transaction propagation behavior defines the transaction processing strategy for method calls:

1、Same Transaction Scenarios

  • PROPAGATION_REQUIRED: The default strategy; if no transaction exists, create a new one; if existing, reuse it
  • PROPAGATION_SUPPORTS: Prefers to use an existing transaction; if none exists, execute in non-transactional mode
  • PROPAGATION_MANDATORY: Must execute in a transaction environment; otherwise, throw an exception

2、Independent Transaction Scenarios

  • PROPAGATION_REQUIRES_NEW: Suspend the current transaction and create a brand-new independent transaction
  • PROPAGATION_NOT_SUPPORTED: Force non-transactional execution and suspend existing transactions
  • PROPAGATION_NEVER: Prohibit transaction environments; throw an exception if a transaction exists
  • PROPAGATION_NESTED: Create a nested transaction within the existing transaction

3、Special Notes on Nested Transactions

When method A nests method B's transaction (PROPAGATION_REQUIRES_NEW), method B must be in a different class from A to avoid potential invocation conflicts. The rollback strategies for different exception scenarios are as follows:

ScenarioPROPAGATION_REQUIRES_NEWPROPAGATION_NESTEDPROPAGATION_REQUIRED
A exceptions, B normalA rolls back, B commitsA and B roll back togetherA and B roll back together
A normal, B exceptions①A catches: B rolls back, A commits ②A does not catch: B rolls back, A rolls backB rolls back, A commitsA and B roll back together
Both A and B exceptionsB rolls back, A rolls backA and B roll back togetherA and B roll back together
Both A and B normalB commits first, A commits laterA and B commit togetherA and B commit together

Through the complete transaction management system described above, the Oinone platform provides developers with flexible and reliable transaction processing solutions, effectively addressing data consistency challenges in complex business scenarios.

Edit this page
Last Updated:1/15/26, 4:02 AM
Prev
ORM API
Next
Actions API
默认页脚
Copyright © 2026 Mr.Hope