This is an automated email from the ASF dual-hosted git repository.

yixia pushed a commit to branch feature/saga
in repository https://gitbox.apache.org/repos/asf/incubator-seata-go.git


The following commit(s) were added to refs/heads/feature/saga by this push:
     new 9c4c0f44 feature: add serverice task parse in statelang (#650)
9c4c0f44 is described below

commit 9c4c0f44a0d59a51bdd861601cce30f811f3df39
Author: Jingliu Xiong <[email protected]>
AuthorDate: Fri Feb 2 10:48:10 2024 +0800

    feature: add serverice task parse in statelang (#650)
---
 pkg/saga/statemachine/constant/constant.go         |  28 ++
 pkg/saga/statemachine/constant/contant.go          |  20 -
 .../statelang/parser/choice_state_json_parser.go   |  10 +-
 .../parser/compensation_trigger_state_parser.go    |  31 ++
 .../statelang/parser/end_state_parser.go           |  66 ++++
 .../statelang/parser/statemachine_json_parser.go   |  42 +-
 .../parser/statemachine_json_parser_test.go        |  36 +-
 .../statelang/parser/statemachine_parser.go        | 133 ++++++-
 .../statelang/parser/sub_state_machine_parser.go   |  84 ++++
 .../statelang/parser/task_state_json_parser.go     | 330 ++++++++++++++++
 pkg/saga/statemachine/statelang/state.go           |   4 +
 .../statemachine/statelang/state/choice_state.go   |   5 +-
 .../statelang/state/compensation_trigger_state.go  |  22 ++
 pkg/saga/statemachine/statelang/state/end_state.go |  64 ++++
 .../statelang/state/sub_state_machine.go           |  69 ++++
 .../statemachine/statelang/state/task_state.go     | 407 +++++++++++++++++++-
 .../statelang/simple_statelang_with_choice.json    |  38 ++
 testdata/saga/statelang/simple_statemachine.json   | 138 +++++++
 .../saga/statelang/state_machine_new_designer.json | 424 +++++++++++++++++++++
 19 files changed, 1909 insertions(+), 42 deletions(-)

diff --git a/pkg/saga/statemachine/constant/constant.go 
b/pkg/saga/statemachine/constant/constant.go
new file mode 100644
index 00000000..f9e1eeba
--- /dev/null
+++ b/pkg/saga/statemachine/constant/constant.go
@@ -0,0 +1,28 @@
+package constant
+
+const (
+       VarNameProcessType                  string = "_ProcessType_"
+       VarNameOperationName                string = "_operation_name_"
+       OperationNameStart                  string = "start"
+       VarNameAsyncCallback                string = "_async_callback_"
+       VarNameStateMachineInst             string = 
"_current_statemachine_instance_"
+       VarNameStateMachine                 string = "_current_statemachine_"
+       VarNameStateMachineEngine           string = 
"_current_statemachine_engine_"
+       VarNameStateMachineConfig           string = "_statemachine_config_"
+       VarNameStateMachineContext          string = "context"
+       VarNameIsAsyncExecution             string = "_is_async_execution_"
+       VarNameStateInst                    string = "_current_state_instance_"
+       SeqEntityStateMachineInst           string = "STATE_MACHINE_INST"
+       VarNameBusinesskey                  string = "_business_key_"
+       VarNameParentId                     string = "_parent_id_"
+       StateTypeServiceTask                string = "ServiceTask"
+       StateTypeChoice                     string = "Choice"
+       StateTypeSubStateMachine            string = "SubStateMachine"
+       CompensateSubMachine                string = "CompensateSubMachine"
+       StateTypeSucceed                    string = "Succeed"
+       StateTypeFail                       string = "Fail"
+       StateTypeCompensationTrigger        string = "CompensationTrigger"
+       StateTypeScriptTask                 string = "ScriptTask"
+       CompensateSubMachineStateNamePrefix string = 
"_compensate_sub_machine_state_"
+       DefaultScriptType                   string = "groovy"
+)
diff --git a/pkg/saga/statemachine/constant/contant.go 
b/pkg/saga/statemachine/constant/contant.go
deleted file mode 100644
index 39cb2924..00000000
--- a/pkg/saga/statemachine/constant/contant.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package constant
-
-const (
-       VarNameProcessType         string = "_ProcessType_"
-       VarNameOperationName       string = "_operation_name_"
-       OperationNameStart         string = "start"
-       VarNameAsyncCallback       string = "_async_callback_"
-       VarNameStateMachineInst    string = "_current_statemachine_instance_"
-       VarNameStateMachine        string = "_current_statemachine_"
-       VarNameStateMachineEngine  string = "_current_statemachine_engine_"
-       VarNameStateMachineConfig  string = "_statemachine_config_"
-       VarNameStateMachineContext string = "context"
-       VarNameIsAsyncExecution    string = "_is_async_execution_"
-       VarNameStateInst           string = "_current_state_instance_"
-       SeqEntityStateMachineInst  string = "STATE_MACHINE_INST"
-       VarNameBusinesskey         string = "_business_key_"
-       VarNameParentId            string = "_parent_id_"
-       StateTypeServiceTask       string = "ServiceTask"
-       StateTypeChoice            string = "Choice"
-)
diff --git a/pkg/saga/statemachine/statelang/parser/choice_state_json_parser.go 
b/pkg/saga/statemachine/statelang/parser/choice_state_json_parser.go
index 04729c57..9b4d51cd 100644
--- a/pkg/saga/statemachine/statelang/parser/choice_state_json_parser.go
+++ b/pkg/saga/statemachine/statelang/parser/choice_state_json_parser.go
@@ -3,21 +3,23 @@ package parser
 import (
        "fmt"
        "github.com/pkg/errors"
-       "github.com/seata/seata-go/pkg/saga/statemachine/engine"
+       "github.com/seata/seata-go/pkg/saga/statemachine/constant"
        "github.com/seata/seata-go/pkg/saga/statemachine/statelang"
        "github.com/seata/seata-go/pkg/saga/statemachine/statelang/state"
 )
 
 type ChoiceStateParser struct {
-       BaseStateParser
+       *BaseStateParser
 }
 
 func NewChoiceStateParser() *ChoiceStateParser {
-       return &ChoiceStateParser{}
+       return &ChoiceStateParser{
+               &BaseStateParser{},
+       }
 }
 
 func (c ChoiceStateParser) StateType() string {
-       return engine.StateTypeChoice
+       return constant.StateTypeChoice
 }
 
 func (c ChoiceStateParser) Parse(stateName string, stateMap 
map[string]interface{}) (statelang.State, error) {
diff --git 
a/pkg/saga/statemachine/statelang/parser/compensation_trigger_state_parser.go 
b/pkg/saga/statemachine/statelang/parser/compensation_trigger_state_parser.go
new file mode 100644
index 00000000..d41a6d29
--- /dev/null
+++ 
b/pkg/saga/statemachine/statelang/parser/compensation_trigger_state_parser.go
@@ -0,0 +1,31 @@
+package parser
+
+import (
+       "github.com/seata/seata-go/pkg/saga/statemachine/constant"
+       "github.com/seata/seata-go/pkg/saga/statemachine/statelang"
+       "github.com/seata/seata-go/pkg/saga/statemachine/statelang/state"
+)
+
+type CompensationTriggerStateParser struct {
+       *BaseStateParser
+}
+
+func NewCompensationTriggerStateParser() *CompensationTriggerStateParser {
+       return &CompensationTriggerStateParser{
+               &BaseStateParser{},
+       }
+}
+
+func (c CompensationTriggerStateParser) StateType() string {
+       return constant.StateTypeCompensationTrigger
+}
+
+func (c CompensationTriggerStateParser) Parse(stateName string, stateMap 
map[string]interface{}) (statelang.State, error) {
+       compensateSubStateMachineStateImpl := 
state.NewCompensationTriggerStateImpl()
+       err := c.ParseBaseAttributes(stateName, 
compensateSubStateMachineStateImpl, stateMap)
+       if err != nil {
+               return nil, err
+       }
+
+       return compensateSubStateMachineStateImpl, nil
+}
diff --git a/pkg/saga/statemachine/statelang/parser/end_state_parser.go 
b/pkg/saga/statemachine/statelang/parser/end_state_parser.go
new file mode 100644
index 00000000..823ee8df
--- /dev/null
+++ b/pkg/saga/statemachine/statelang/parser/end_state_parser.go
@@ -0,0 +1,66 @@
+package parser
+
+import (
+       "github.com/seata/seata-go/pkg/saga/statemachine/constant"
+       "github.com/seata/seata-go/pkg/saga/statemachine/statelang"
+       "github.com/seata/seata-go/pkg/saga/statemachine/statelang/state"
+)
+
+type SucceedEndStateParser struct {
+       *BaseStateParser
+}
+
+func NewSucceedEndStateParser() *SucceedEndStateParser {
+       return &SucceedEndStateParser{
+               &BaseStateParser{},
+       }
+}
+
+func (s SucceedEndStateParser) StateType() string {
+       return constant.StateTypeSucceed
+}
+
+func (s SucceedEndStateParser) Parse(stateName string, stateMap 
map[string]interface{}) (statelang.State, error) {
+       succeedEndStateImpl := state.NewSucceedEndStateImpl()
+       err := s.ParseBaseAttributes(stateName, succeedEndStateImpl, stateMap)
+       if err != nil {
+               return nil, err
+       }
+
+       return succeedEndStateImpl, nil
+}
+
+type FailEndStateParser struct {
+       *BaseStateParser
+}
+
+func NewFailEndStateParser() *FailEndStateParser {
+       return &FailEndStateParser{
+               &BaseStateParser{},
+       }
+}
+
+func (f FailEndStateParser) StateType() string {
+       return constant.StateTypeFail
+}
+
+func (f FailEndStateParser) Parse(stateName string, stateMap 
map[string]interface{}) (statelang.State, error) {
+       failEndStateImpl := state.NewFailEndStateImpl()
+       err := f.ParseBaseAttributes(stateName, failEndStateImpl, stateMap)
+       if err != nil {
+               return nil, err
+       }
+
+       errorCode, err := f.GetStringOrDefault(stateName, stateMap, 
"ErrorCode", "")
+       if err != nil {
+               return nil, err
+       }
+       failEndStateImpl.SetErrorCode(errorCode)
+
+       message, err := f.GetStringOrDefault(stateName, stateMap, "Message", "")
+       if err != nil {
+               return nil, err
+       }
+       failEndStateImpl.SetMessage(message)
+       return failEndStateImpl, nil
+}
diff --git a/pkg/saga/statemachine/statelang/parser/statemachine_json_parser.go 
b/pkg/saga/statemachine/statelang/parser/statemachine_json_parser.go
index c0977034..4bcfdcbc 100644
--- a/pkg/saga/statemachine/statelang/parser/statemachine_json_parser.go
+++ b/pkg/saga/statemachine/statelang/parser/statemachine_json_parser.go
@@ -3,14 +3,19 @@ package parser
 import (
        "encoding/json"
        "github.com/pkg/errors"
+       "github.com/seata/seata-go/pkg/saga/statemachine/constant"
        "github.com/seata/seata-go/pkg/saga/statemachine/statelang"
+       "github.com/seata/seata-go/pkg/saga/statemachine/statelang/state"
 )
 
 type JSONStateMachineParser struct {
+       *BaseStateParser
 }
 
 func NewJSONStateMachineParser() *JSONStateMachineParser {
-       return &JSONStateMachineParser{}
+       return &JSONStateMachineParser{
+               &BaseStateParser{},
+       }
 }
 
 func (stateMachineParser JSONStateMachineParser) GetType() string {
@@ -77,15 +82,40 @@ func (stateMachineParser JSONStateMachineParser) 
Parse(content string) (statelan
                stateMachine.States()[stateName] = state
        }
 
-       //TODO setCompensateState
-       //for stateName, state := range stateMachine.GetStates() {
-       //
-       //}
-       //
+       for _, stateValue := range stateMachine.States() {
+               if stateMachineParser.isTaskState(stateValue.Type()) {
+                       stateMachineParser.setForCompensation(stateValue, 
stateMachine)
+               }
+       }
 
        return stateMachine, nil
 }
 
+func (stateMachineParser JSONStateMachineParser) setForCompensation(stateValue 
statelang.State, stateMachine *statelang.StateMachineImpl) {
+       switch stateValue.Type() {
+       case stateValue.Type():
+               serviceTaskStateImpl, ok := 
stateValue.(*state.ServiceTaskStateImpl)
+               if ok {
+                       if serviceTaskStateImpl.CompensateState() != "" {
+                               compState := 
stateMachine.States()[serviceTaskStateImpl.CompensateState()]
+                               if 
stateMachineParser.isTaskState(compState.Type()) {
+                                       compStateImpl, ok := 
compState.(state.ServiceTaskStateImpl)
+                                       if ok {
+                                               
compStateImpl.SetForCompensation(true)
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
+func (stateMachineParser JSONStateMachineParser) isTaskState(stateType string) 
bool {
+       if stateType == constant.StateTypeServiceTask {
+               return true
+       }
+       return false
+}
+
 type StateMachineJsonObject struct {
        Name                        string                 `json:"Name"`
        Comment                     string                 `json:"Comment"`
diff --git 
a/pkg/saga/statemachine/statelang/parser/statemachine_json_parser_test.go 
b/pkg/saga/statemachine/statelang/parser/statemachine_json_parser_test.go
index c6d00b75..0028309a 100644
--- a/pkg/saga/statemachine/statelang/parser/statemachine_json_parser_test.go
+++ b/pkg/saga/statemachine/statelang/parser/statemachine_json_parser_test.go
@@ -1,12 +1,44 @@
 package parser
 
 import (
+       "os"
        "testing"
 )
 
 func TestParseChoice(t *testing.T) {
-       var content = "{\n    \"Name\":\"ChoiceTest\",\n    
\"Comment\":\"ChoiceTest\",\n    \"StartState\":\"ChoiceState\",\n    
\"Version\":\"0.0.1\",\n    \"States\":{\n        \"ChoiceState\":{\n           
 \"Type\":\"Choice\",\n            \"Choices\":[\n                {\n           
         \"Expression\":\"[a] == 1\",\n                    
\"Next\":\"SecondState\"\n                },\n                {\n               
     \"Expression\":\"[a] == 2\",\n                    \"Next\":\"Thir [...]
-       _, err := NewJSONStateMachineParser().Parse(content)
+       filePath := 
"../../../../../testdata/saga/statelang/simple_statelang_with_choice.json"
+       fileContent, err := os.ReadFile(filePath)
+       if err != nil {
+               t.Error("parse fail: " + err.Error())
+               return
+       }
+       _, err = NewJSONStateMachineParser().Parse(string(fileContent))
+       if err != nil {
+               t.Error("parse fail: " + err.Error())
+       }
+}
+
+func TestParseServiceTaskForSimpleStateMachine(t *testing.T) {
+       filePath := 
"../../../../../testdata/saga/statelang/simple_statemachine.json"
+       fileContent, err := os.ReadFile(filePath)
+       if err != nil {
+               t.Error("parse fail: " + err.Error())
+               return
+       }
+       _, err = NewJSONStateMachineParser().Parse(string(fileContent))
+       if err != nil {
+               t.Error("parse fail: " + err.Error())
+       }
+}
+
+func TestParseServiceTaskForNewDesigner(t *testing.T) {
+       filePath := 
"../../../../../testdata/saga/statelang/state_machine_new_designer.json"
+       fileContent, err := os.ReadFile(filePath)
+       if err != nil {
+               t.Error("parse fail: " + err.Error())
+               return
+       }
+       _, err = NewJSONStateMachineParser().Parse(string(fileContent))
        if err != nil {
                t.Error("parse fail: " + err.Error())
        }
diff --git a/pkg/saga/statemachine/statelang/parser/statemachine_parser.go 
b/pkg/saga/statemachine/statelang/parser/statemachine_parser.go
index 99ee95e1..a74c79ff 100644
--- a/pkg/saga/statemachine/statelang/parser/statemachine_parser.go
+++ b/pkg/saga/statemachine/statelang/parser/statemachine_parser.go
@@ -3,6 +3,8 @@ package parser
 import (
        "github.com/pkg/errors"
        "github.com/seata/seata-go/pkg/saga/statemachine/statelang"
+       "strconv"
+       "strings"
        "sync"
 )
 
@@ -19,20 +21,23 @@ type StateParser interface {
 type BaseStateParser struct {
 }
 
+func NewBaseStateParser() *BaseStateParser {
+       return &BaseStateParser{}
+}
+
 func (b BaseStateParser) ParseBaseAttributes(stateName string, state 
statelang.State, stateMap map[string]interface{}) error {
        state.SetName(stateName)
 
-       comment, err := b.GetString(stateName, stateMap, "Comment")
+       comment, err := b.GetStringOrDefault(stateName, stateMap, "Comment", "")
        if err != nil {
                return err
        }
        state.SetComment(comment)
 
-       next, err := b.GetString(stateName, stateMap, "Next")
+       next, err := b.GetStringOrDefault(stateName, stateMap, "Next", "")
        if err != nil {
                return err
        }
-
        state.SetNext(next)
        return nil
 }
@@ -52,9 +57,21 @@ func (b BaseStateParser) GetString(stateName string, 
stateMap map[string]interfa
        return valueAsString, nil
 }
 
-func (b BaseStateParser) GetSlice(stateName string, stateMap 
map[string]interface{}, key string) ([]interface{}, error) {
+func (b BaseStateParser) GetStringOrDefault(stateName string, stateMap 
map[string]interface{}, key string, defaultValue string) (string, error) {
        value := stateMap[key]
+       if value == nil {
+               return defaultValue, nil
+       }
 
+       valueAsString, ok := value.(string)
+       if !ok {
+               return defaultValue, errors.New("State [" + stateName + "] " + 
key + " illegal, required string")
+       }
+       return valueAsString, nil
+}
+
+func (b BaseStateParser) GetSlice(stateName string, stateMap 
map[string]interface{}, key string) ([]interface{}, error) {
+       value := stateMap[key]
        if value == nil {
                var result []interface{}
                return result, errors.New("State [" + stateName + "] " + key + 
" not exist")
@@ -62,12 +79,103 @@ func (b BaseStateParser) GetSlice(stateName string, 
stateMap map[string]interfac
 
        valueAsSlice, ok := value.([]interface{})
        if !ok {
-               var result []interface{}
-               return result, errors.New("State [" + stateName + "] " + key + 
" illegal, required slice")
+               var slice []interface{}
+               return slice, errors.New("State [" + stateName + "] " + key + " 
illegal, required []interface{}")
        }
        return valueAsSlice, nil
 }
 
+func (b BaseStateParser) GetSliceOrDefault(stateName string, stateMap 
map[string]interface{}, key string, defaultValue []interface{}) ([]interface{}, 
error) {
+       value := stateMap[key]
+
+       if value == nil {
+               return defaultValue, nil
+       }
+
+       valueAsSlice, ok := value.([]interface{})
+       if !ok {
+               return defaultValue, errors.New("State [" + stateName + "] " + 
key + " illegal, required []interface{}")
+       }
+       return valueAsSlice, nil
+}
+
+func (b BaseStateParser) GetMapOrDefault(stateMap map[string]interface{}, key 
string, defaultValue map[string]interface{}) (map[string]interface{}, error) {
+       value := stateMap[key]
+
+       if value == nil {
+               return defaultValue, nil
+       }
+
+       valueAsMap, ok := value.(map[string]interface{})
+       if !ok {
+               return defaultValue, nil
+       }
+       return valueAsMap, nil
+}
+
+func (b BaseStateParser) GetBool(stateName string, stateMap 
map[string]interface{}, key string) (bool, error) {
+       value := stateMap[key]
+
+       if value == nil {
+               return false, errors.New("State [" + stateName + "] " + key + " 
not exist")
+       }
+
+       valueAsBool, ok := value.(bool)
+       if !ok {
+               return false, errors.New("State [" + stateName + "] " + key + " 
illegal, required bool")
+       }
+       return valueAsBool, nil
+}
+
+func (b BaseStateParser) GetBoolOrDefault(stateName string, stateMap 
map[string]interface{}, key string, defaultValue bool) (bool, error) {
+       value := stateMap[key]
+
+       if value == nil {
+               return defaultValue, nil
+       }
+
+       valueAsBool, ok := value.(bool)
+       if !ok {
+               return false, errors.New("State [" + stateName + "] " + key + " 
illegal, required bool")
+       }
+       return valueAsBool, nil
+}
+
+func (b BaseStateParser) GetIntOrDefault(stateName string, stateMap 
map[string]interface{}, key string, defaultValue int) (int, error) {
+       value := stateMap[key]
+
+       if value == nil {
+               return defaultValue, nil
+       }
+
+       // just use float64 to convert, json reader will read all number as 
float64
+       valueAsFloat64, ok := value.(float64)
+       if !ok {
+               return defaultValue, errors.New("State [" + stateName + "] " + 
key + " illegal, required int")
+       }
+
+       floatStr := strconv.FormatFloat(valueAsFloat64, 'f', -1, 64)
+       if strings.Contains(floatStr, ".") {
+               return defaultValue, errors.New("State [" + stateName + "] " + 
key + " illegal, required int")
+       }
+
+       return int(valueAsFloat64), nil
+}
+
+func (b BaseStateParser) GetFloat64OrDefault(stateName string, stateMap 
map[string]interface{}, key string, defaultValue float64) (float64, error) {
+       value := stateMap[key]
+
+       if value == nil {
+               return defaultValue, nil
+       }
+
+       valueAsFloat64, ok := value.(float64)
+       if !ok {
+               return defaultValue, errors.New("State [" + stateName + "] " + 
key + " illegal, required float64")
+       }
+       return valueAsFloat64, nil
+}
+
 type StateParserFactory interface {
        RegistryStateParser(stateType string, stateParser StateParser)
 
@@ -89,8 +197,21 @@ func NewDefaultStateParserFactory() 
*DefaultStateParserFactory {
 // InitDefaultStateParser init StateParser by default
 func (d *DefaultStateParserFactory) InitDefaultStateParser() {
        choiceStateParser := NewChoiceStateParser()
+       serviceTaskStateParser := NewServiceTaskStateParser()
+       subStateMachineParser := NewSubStateMachineParser()
+       succeedEndStateParser := NewSucceedEndStateParser()
+       compensationTriggerStateParser := NewCompensationTriggerStateParser()
+       failEndStateParser := NewFailEndStateParser()
+       scriptTaskStateParser := NewScriptTaskStateParser()
 
        d.RegistryStateParser(choiceStateParser.StateType(), choiceStateParser)
+       d.RegistryStateParser(serviceTaskStateParser.StateType(), 
serviceTaskStateParser)
+       d.RegistryStateParser(subStateMachineParser.StateType(), 
subStateMachineParser)
+       d.RegistryStateParser(succeedEndStateParser.StateType(), 
succeedEndStateParser)
+       d.RegistryStateParser(compensationTriggerStateParser.StateType(), 
compensationTriggerStateParser)
+       d.RegistryStateParser(compensationTriggerStateParser.StateType(), 
compensationTriggerStateParser)
+       d.RegistryStateParser(failEndStateParser.StateType(), 
failEndStateParser)
+       d.RegistryStateParser(scriptTaskStateParser.StateType(), 
scriptTaskStateParser)
 }
 
 func (d *DefaultStateParserFactory) RegistryStateParser(stateType string, 
stateParser StateParser) {
diff --git a/pkg/saga/statemachine/statelang/parser/sub_state_machine_parser.go 
b/pkg/saga/statemachine/statelang/parser/sub_state_machine_parser.go
new file mode 100644
index 00000000..dc466da5
--- /dev/null
+++ b/pkg/saga/statemachine/statelang/parser/sub_state_machine_parser.go
@@ -0,0 +1,84 @@
+package parser
+
+import (
+       "fmt"
+       "github.com/pkg/errors"
+       "github.com/seata/seata-go/pkg/saga/statemachine/constant"
+       "github.com/seata/seata-go/pkg/saga/statemachine/statelang"
+       "github.com/seata/seata-go/pkg/saga/statemachine/statelang/state"
+)
+
+type SubStateMachineParser struct {
+       *AbstractTaskStateParser
+}
+
+func NewSubStateMachineParser() *SubStateMachineParser {
+       return &SubStateMachineParser{
+               NewAbstractTaskStateParser(),
+       }
+}
+
+func (s SubStateMachineParser) StateType() string {
+       return constant.StateTypeSubStateMachine
+}
+
+func (s SubStateMachineParser) Parse(stateName string, stateMap 
map[string]interface{}) (statelang.State, error) {
+       subStateMachineImpl := state.NewSubStateMachineImpl()
+
+       err := s.ParseTaskAttributes(stateName, 
subStateMachineImpl.AbstractTaskState, stateMap)
+       if err != nil {
+               return nil, err
+       }
+
+       stateMachineName, err := s.BaseStateParser.GetString(stateName, 
stateMap, "StateMachineName")
+       if err != nil {
+               return nil, err
+       }
+       subStateMachineImpl.SetName(stateMachineName)
+
+       if subStateMachineImpl.CompensateState() == "" {
+               // build default SubStateMachine compensate state
+               compensateSubStateMachineStateParser := 
NewCompensateSubStateMachineStateParser()
+               compensateState, err := 
compensateSubStateMachineStateParser.Parse(stateName, nil)
+               if err != nil {
+                       return nil, err
+               }
+               compensateStateImpl, ok := compensateState.(state.TaskState)
+               if !ok {
+                       return nil, errors.New(fmt.Sprintf("State [name:%s] has 
wrong compensateState type", stateName))
+               }
+               subStateMachineImpl.SetCompensateStateImpl(compensateStateImpl)
+               
subStateMachineImpl.SetCompensateState(compensateStateImpl.Name())
+       }
+       return subStateMachineImpl, nil
+}
+
+type CompensateSubStateMachineStateParser struct {
+       *AbstractTaskStateParser
+}
+
+func NewCompensateSubStateMachineStateParser() 
*CompensateSubStateMachineStateParser {
+       return &CompensateSubStateMachineStateParser{
+               NewAbstractTaskStateParser(),
+       }
+}
+
+func (c CompensateSubStateMachineStateParser) StateType() string {
+       return constant.CompensateSubMachine
+}
+
+func (c CompensateSubStateMachineStateParser) Parse(stateName string, stateMap 
map[string]interface{}) (statelang.State, error) {
+       compensateSubStateMachineStateImpl := 
state.NewCompensateSubStateMachineStateImpl()
+       compensateSubStateMachineStateImpl.SetForCompensation(true)
+
+       if stateMap != nil {
+               err := c.ParseTaskAttributes(stateName, 
compensateSubStateMachineStateImpl.ServiceTaskStateImpl.AbstractTaskState, 
stateMap)
+               if err != nil {
+                       return nil, err
+               }
+       }
+       if compensateSubStateMachineStateImpl.Name() == "" {
+               
compensateSubStateMachineStateImpl.SetName(constant.CompensateSubMachineStateNamePrefix
 + compensateSubStateMachineStateImpl.Hashcode())
+       }
+       return compensateSubStateMachineStateImpl, nil
+}
diff --git a/pkg/saga/statemachine/statelang/parser/task_state_json_parser.go 
b/pkg/saga/statemachine/statelang/parser/task_state_json_parser.go
new file mode 100644
index 00000000..5e702487
--- /dev/null
+++ b/pkg/saga/statemachine/statelang/parser/task_state_json_parser.go
@@ -0,0 +1,330 @@
+package parser
+
+import (
+       "fmt"
+       "github.com/pkg/errors"
+       "github.com/seata/seata-go/pkg/saga/statemachine/constant"
+       "github.com/seata/seata-go/pkg/saga/statemachine/statelang"
+       "github.com/seata/seata-go/pkg/saga/statemachine/statelang/state"
+)
+
+type AbstractTaskStateParser struct {
+       *BaseStateParser
+}
+
+func NewAbstractTaskStateParser() *AbstractTaskStateParser {
+       return &AbstractTaskStateParser{
+               &BaseStateParser{},
+       }
+}
+
+func (a *AbstractTaskStateParser) ParseTaskAttributes(stateName string, state 
*state.AbstractTaskState, stateMap map[string]interface{}) error {
+       err := a.ParseBaseAttributes(state.Name(), state.BaseState, stateMap)
+       if err != nil {
+               return err
+       }
+
+       compensateState, err := a.GetStringOrDefault(stateName, stateMap, 
"CompensateState", "")
+       if err != nil {
+               return err
+       }
+       state.SetCompensateState(compensateState)
+
+       isForCompensation, err := a.GetBoolOrDefault(stateName, stateMap, 
"IsForCompensation", false)
+       if err != nil {
+               return err
+       }
+       state.SetForCompensation(isForCompensation)
+
+       isForUpdate, err := a.GetBoolOrDefault(stateName, stateMap, 
"IsForUpdate", false)
+       if err != nil {
+               return err
+       }
+       state.SetForUpdate(isForUpdate)
+
+       isPersist, err := a.GetBoolOrDefault(stateName, stateMap, "IsPersist", 
false)
+       if err != nil {
+               return err
+       }
+       state.SetPersist(isPersist)
+
+       isRetryPersistModeUpdate, err := a.GetBoolOrDefault(stateName, 
stateMap, "IsRetryPersistModeUpdate", false)
+       if err != nil {
+               return err
+       }
+       state.SetRetryPersistModeUpdate(isRetryPersistModeUpdate)
+
+       isCompensatePersistModeUpdate, err := a.GetBoolOrDefault(stateName, 
stateMap, "IsCompensatePersistModeUpdate", false)
+       if err != nil {
+               return err
+       }
+       state.SetCompensatePersistModeUpdate(isCompensatePersistModeUpdate)
+
+       retryInterfaces, err := a.GetSliceOrDefault(stateName, stateMap, 
"Retry", nil)
+       if err != nil {
+               return err
+       }
+       if retryInterfaces != nil {
+               retries, err := a.parseRetries(state.Name(), retryInterfaces)
+               if err != nil {
+                       return err
+               }
+               state.SetRetry(retries)
+       }
+
+       catchInterfaces, err := a.GetSliceOrDefault(stateName, stateMap, 
"Catch", nil)
+       if err != nil {
+               return err
+       }
+       if catchInterfaces != nil {
+               catches, err := a.parseCatches(state.Name(), catchInterfaces)
+               if err != nil {
+                       return err
+               }
+               state.SetCatches(catches)
+       }
+
+       inputInterfaces, err := a.GetSliceOrDefault(stateName, stateMap, 
"Input", nil)
+       if err != nil {
+               return err
+       }
+       if inputInterfaces != nil {
+               state.SetInput(inputInterfaces)
+       }
+
+       output, err := a.GetMapOrDefault(stateMap, "Output", nil)
+       if err != nil {
+               return err
+       }
+       if output != nil {
+               state.SetOutput(output)
+       }
+
+       statusMap, ok := stateMap["Status"].(map[string]string)
+       if ok {
+               state.SetStatus(statusMap)
+       }
+
+       loopMap, ok := stateMap["Loop"].(map[string]interface{})
+       if ok {
+               loop := a.parseLoop(stateName, loopMap)
+               state.SetLoop(loop)
+       }
+
+       return nil
+}
+
+func (a *AbstractTaskStateParser) parseLoop(stateName string, loopMap 
map[string]interface{}) state.Loop {
+       loopImpl := &state.LoopImpl{}
+       parallel, err := a.GetIntOrDefault(stateName, loopMap, "Parallel", 1)
+       if err != nil {
+               return nil
+       }
+       loopImpl.SetParallel(parallel)
+
+       collection, err := a.GetStringOrDefault(stateName, loopMap, 
"Collection", "")
+       if err != nil {
+               return nil
+       }
+       loopImpl.SetCollection(collection)
+
+       elementVariableName, err := a.GetStringOrDefault(stateName, loopMap, 
"ElementVariableName", "loopElement")
+       if err != nil {
+               return nil
+       }
+       loopImpl.SetElementVariableName(elementVariableName)
+
+       elementIndexName, err := a.GetStringOrDefault(stateName, loopMap, 
"ElementIndexName", "loopCounter")
+       if err != nil {
+               return nil
+       }
+       loopImpl.SetElementIndexName(elementIndexName)
+
+       completionCondition, err := a.GetStringOrDefault(stateName, loopMap, 
"CompletionCondition", "[nrOfInstances] == [nrOfCompletedInstances]")
+       if err != nil {
+               return nil
+       }
+       loopImpl.SetElementIndexName(completionCondition)
+       return loopImpl
+}
+
+func (a *AbstractTaskStateParser) parseRetries(stateName string, 
retryInterfaces []interface{}) ([]state.Retry, error) {
+       retries := make([]state.Retry, 0)
+       for _, retryInterface := range retryInterfaces {
+               retryMap, ok := retryInterface.(map[string]interface{})
+               if !ok {
+
+                       return nil, errors.New("State [" + stateName + "] " + 
"Retry illegal, require map[string]interface{}")
+               }
+               retry := &state.RetryImpl{}
+               errorTypes, err := a.GetSliceOrDefault(stateName, retryMap, 
"Exceptions", nil)
+               if err != nil {
+                       return nil, err
+               }
+               if errorTypes != nil {
+                       errorTypeNames := make([]string, 0)
+                       for _, errorType := range errorTypes {
+                               errorTypeNames = append(errorTypeNames, 
errorType.(string))
+                       }
+                       retry.SetErrorTypeNames(errorTypeNames)
+               }
+
+               maxAttempts, err := a.GetIntOrDefault(stateName, retryMap, 
"MaxAttempts", 0)
+               if err != nil {
+                       return nil, err
+               }
+               retry.SetMaxAttempt(maxAttempts)
+
+               backoffInterval, err := a.GetFloat64OrDefault(stateName, 
retryMap, "BackoffInterval", 0)
+               if err != nil {
+                       return nil, err
+               }
+               retry.SetBackoffRate(backoffInterval)
+
+               intervalSeconds, err := a.GetFloat64OrDefault(stateName, 
retryMap, "IntervalSeconds", 0)
+               if err != nil {
+                       return nil, err
+               }
+               retry.SetIntervalSecond(intervalSeconds)
+               retries = append(retries, retry)
+       }
+       return retries, nil
+}
+
+func (a *AbstractTaskStateParser) parseCatches(stateName string, 
catchInterfaces []interface{}) ([]state.ErrorMatch, error) {
+       errorMatches := make([]state.ErrorMatch, 0, len(catchInterfaces))
+       for _, catchInterface := range catchInterfaces {
+               catchMap, ok := catchInterface.(map[string]interface{})
+               if !ok {
+                       return nil, errors.New("State [" + stateName + "] " + 
"Catch illegal, require map[string]interface{}")
+               }
+               errorMatch := &state.ErrorMatchImpl{}
+               errorInterfaces, err := a.GetSliceOrDefault(stateName, 
catchMap, "Exceptions", nil)
+               if err != nil {
+                       return nil, err
+               }
+               if errorInterfaces != nil {
+                       errorNames := make([]string, 0)
+                       for _, errorType := range errorInterfaces {
+                               errorNames = append(errorNames, 
errorType.(string))
+                       }
+                       errorMatch.SetErrors(errorNames)
+               }
+               next, err := a.GetStringOrDefault(stateName, catchMap, "Next", 
"")
+               if err != nil {
+                       return nil, err
+               }
+               errorMatch.SetNext(next)
+               errorMatches = append(errorMatches, errorMatch)
+       }
+       return errorMatches, nil
+}
+
+type ServiceTaskStateParser struct {
+       *AbstractTaskStateParser
+}
+
+func NewServiceTaskStateParser() *ServiceTaskStateParser {
+       return &ServiceTaskStateParser{
+               NewAbstractTaskStateParser(),
+       }
+}
+
+func (s ServiceTaskStateParser) StateType() string {
+       return constant.StateTypeServiceTask
+}
+
+func (s ServiceTaskStateParser) Parse(stateName string, stateMap 
map[string]interface{}) (statelang.State, error) {
+       serviceTaskStateImpl := state.NewServiceTaskStateImpl()
+
+       err := s.ParseTaskAttributes(stateName, 
serviceTaskStateImpl.AbstractTaskState, stateMap)
+       if err != nil {
+               return nil, err
+       }
+
+       serviceName, err := s.GetString(stateName, stateMap, "ServiceName")
+       if err != nil {
+               return nil, err
+       }
+       serviceTaskStateImpl.SetServiceName(serviceName)
+
+       serviceMethod, err := s.GetString(stateName, stateMap, "ServiceMethod")
+       if err != nil {
+               return nil, err
+       }
+       serviceTaskStateImpl.SetServiceMethod(serviceMethod)
+
+       serviceType, err := s.GetStringOrDefault(stateName, stateMap, 
"ServiceType", "")
+       if err != nil {
+               return nil, err
+       }
+       serviceTaskStateImpl.SetServiceType(serviceType)
+
+       parameterTypeInterfaces, err := s.GetSliceOrDefault(stateName, 
stateMap, "ParameterTypes", nil)
+       if err != nil {
+               return nil, err
+       }
+       if parameterTypeInterfaces != nil {
+               var parameterTypes []string
+               for i := range parameterTypeInterfaces {
+                       parameterType, ok := parameterTypeInterfaces[i].(string)
+                       if !ok {
+                               return nil, errors.New(fmt.Sprintf("State [%s] 
parameterType required string", stateName))
+                       }
+
+                       parameterTypes = append(parameterTypes, parameterType)
+               }
+               serviceTaskStateImpl.SetParameterTypes(parameterTypes)
+       }
+
+       isAsync, err := s.GetBoolOrDefault(stateName, stateMap, "IsAsync", 
false)
+       if err != nil {
+               return nil, err
+       }
+       serviceTaskStateImpl.SetIsAsync(isAsync)
+
+       return serviceTaskStateImpl, nil
+}
+
+type ScriptTaskStateParser struct {
+       *AbstractTaskStateParser
+}
+
+func NewScriptTaskStateParser() *ScriptTaskStateParser {
+       return &ScriptTaskStateParser{
+               NewAbstractTaskStateParser(),
+       }
+}
+
+func (s ScriptTaskStateParser) StateType() string {
+       return constant.StateTypeScriptTask
+}
+
+func (s ScriptTaskStateParser) Parse(stateName string, stateMap 
map[string]interface{}) (statelang.State, error) {
+       scriptTaskStateImpl := state.NewScriptTaskStateImpl()
+
+       err := s.ParseTaskAttributes(stateName, 
scriptTaskStateImpl.AbstractTaskState, stateMap)
+       if err != nil {
+               return nil, err
+       }
+
+       scriptType, err := s.GetStringOrDefault(stateName, stateMap, 
"ScriptType", "")
+       if err != nil {
+               return nil, err
+       }
+       if scriptType != "" {
+               scriptTaskStateImpl.SetScriptType(scriptType)
+       }
+
+       scriptContent, err := s.GetStringOrDefault(stateName, stateMap, 
"ScriptContent", "")
+       if err != nil {
+               return nil, err
+       }
+       scriptTaskStateImpl.SetScriptContent(scriptContent)
+
+       scriptTaskStateImpl.SetForCompensation(false)
+       scriptTaskStateImpl.SetForUpdate(false)
+       scriptTaskStateImpl.SetPersist(false)
+
+       return scriptTaskStateImpl, nil
+}
diff --git a/pkg/saga/statemachine/statelang/state.go 
b/pkg/saga/statemachine/statelang/state.go
index 1e490ac0..29ac6727 100644
--- a/pkg/saga/statemachine/statelang/state.go
+++ b/pkg/saga/statemachine/statelang/state.go
@@ -30,6 +30,10 @@ type BaseState struct {
        stateMachine StateMachine
 }
 
+func NewBaseState() *BaseState {
+       return &BaseState{}
+}
+
 func (b *BaseState) Name() string {
        return b.name
 }
diff --git a/pkg/saga/statemachine/statelang/state/choice_state.go 
b/pkg/saga/statemachine/statelang/state/choice_state.go
index e1cd973c..c3f48276 100644
--- a/pkg/saga/statemachine/statelang/state/choice_state.go
+++ b/pkg/saga/statemachine/statelang/state/choice_state.go
@@ -21,14 +21,15 @@ type Choice interface {
 }
 
 type ChoiceStateImpl struct {
-       statelang.BaseState
+       *statelang.BaseState
        defaultChoice string   `alias:"Default"`
        choices       []Choice `alias:"Choices"`
 }
 
 func NewChoiceStateImpl() *ChoiceStateImpl {
        return &ChoiceStateImpl{
-               choices: make([]Choice, 0),
+               BaseState: statelang.NewBaseState(),
+               choices:   make([]Choice, 0),
        }
 }
 
diff --git 
a/pkg/saga/statemachine/statelang/state/compensation_trigger_state.go 
b/pkg/saga/statemachine/statelang/state/compensation_trigger_state.go
new file mode 100644
index 00000000..cac71420
--- /dev/null
+++ b/pkg/saga/statemachine/statelang/state/compensation_trigger_state.go
@@ -0,0 +1,22 @@
+package state
+
+import (
+       "github.com/seata/seata-go/pkg/saga/statemachine/constant"
+       "github.com/seata/seata-go/pkg/saga/statemachine/statelang"
+)
+
+type CompensationTriggerState interface {
+       statelang.State
+}
+
+type CompensationTriggerStateImpl struct {
+       *statelang.BaseState
+}
+
+func NewCompensationTriggerStateImpl() *CompensationTriggerStateImpl {
+       s := &CompensationTriggerStateImpl{
+               BaseState: statelang.NewBaseState(),
+       }
+       s.SetType(constant.StateTypeCompensationTrigger)
+       return s
+}
diff --git a/pkg/saga/statemachine/statelang/state/end_state.go 
b/pkg/saga/statemachine/statelang/state/end_state.go
new file mode 100644
index 00000000..099e2e07
--- /dev/null
+++ b/pkg/saga/statemachine/statelang/state/end_state.go
@@ -0,0 +1,64 @@
+package state
+
+import (
+       "github.com/seata/seata-go/pkg/saga/statemachine/constant"
+       "github.com/seata/seata-go/pkg/saga/statemachine/statelang"
+)
+
+type EndState interface {
+       statelang.State
+}
+
+type SucceedEndState interface {
+       EndState
+}
+
+type SucceedEndStateImpl struct {
+       *statelang.BaseState
+}
+
+func NewSucceedEndStateImpl() *SucceedEndStateImpl {
+       s := &SucceedEndStateImpl{
+               BaseState: statelang.NewBaseState(),
+       }
+       s.SetType(constant.StateTypeSucceed)
+       return s
+}
+
+type FailEndState interface {
+       EndState
+
+       ErrorCode() string
+
+       Message() string
+}
+
+type FailEndStateImpl struct {
+       *statelang.BaseState
+       errorCode string
+       message   string
+}
+
+func NewFailEndStateImpl() *FailEndStateImpl {
+       s := &FailEndStateImpl{
+               BaseState: statelang.NewBaseState(),
+       }
+       s.SetType(constant.StateTypeFail)
+       return s
+}
+
+func (f *FailEndStateImpl) ErrorCode() string {
+       return f.errorCode
+}
+
+func (f *FailEndStateImpl) SetErrorCode(errorCode string) {
+       f.errorCode = errorCode
+}
+
+func (f *FailEndStateImpl) Message() string {
+       return f.message
+}
+
+func (f *FailEndStateImpl) SetMessage(message string) {
+       f.message = message
+}
diff --git a/pkg/saga/statemachine/statelang/state/sub_state_machine.go 
b/pkg/saga/statemachine/statelang/state/sub_state_machine.go
new file mode 100644
index 00000000..bf0d96d5
--- /dev/null
+++ b/pkg/saga/statemachine/statelang/state/sub_state_machine.go
@@ -0,0 +1,69 @@
+package state
+
+import (
+       "github.com/google/uuid"
+       "github.com/seata/seata-go/pkg/saga/statemachine/constant"
+)
+
+type SubStateMachine interface {
+       TaskState
+
+       StateMachineName() string
+
+       CompensateStateImpl() TaskState
+}
+
+type SubStateMachineImpl struct {
+       *ServiceTaskStateImpl
+       stateMachineName string
+       compensateState  TaskState
+}
+
+func NewSubStateMachineImpl() *SubStateMachineImpl {
+       return &SubStateMachineImpl{
+               ServiceTaskStateImpl: NewServiceTaskStateImpl(),
+       }
+}
+
+func (s *SubStateMachineImpl) StateMachineName() string {
+       return s.stateMachineName
+}
+
+func (s *SubStateMachineImpl) SetStateMachineName(stateMachineName string) {
+       s.stateMachineName = stateMachineName
+}
+
+func (s *SubStateMachineImpl) CompensateStateImpl() TaskState {
+       return s.compensateState
+}
+
+func (s *SubStateMachineImpl) SetCompensateStateImpl(compensateState 
TaskState) {
+       s.compensateState = compensateState
+}
+
+type CompensateSubStateMachineState interface {
+       ServiceTaskState
+}
+
+type CompensateSubStateMachineStateImpl struct {
+       *ServiceTaskStateImpl
+       hashcode string
+}
+
+func NewCompensateSubStateMachineStateImpl() 
*CompensateSubStateMachineStateImpl {
+       uuid := uuid.New()
+       c := &CompensateSubStateMachineStateImpl{
+               ServiceTaskStateImpl: NewServiceTaskStateImpl(),
+               hashcode:             uuid.String(),
+       }
+       c.SetType(constant.CompensateSubMachine)
+       return c
+}
+
+func (c *CompensateSubStateMachineStateImpl) Hashcode() string {
+       return c.hashcode
+}
+
+func (c *CompensateSubStateMachineStateImpl) SetHashcode(hashcode string) {
+       c.hashcode = hashcode
+}
diff --git a/pkg/saga/statemachine/statelang/state/task_state.go 
b/pkg/saga/statemachine/statelang/state/task_state.go
index a9164d35..63e33f3c 100644
--- a/pkg/saga/statemachine/statelang/state/task_state.go
+++ b/pkg/saga/statemachine/statelang/state/task_state.go
@@ -1,7 +1,9 @@
 package state
 
 import (
+       "github.com/seata/seata-go/pkg/saga/statemachine/constant"
        "github.com/seata/seata-go/pkg/saga/statemachine/statelang"
+       "reflect"
 )
 
 type TaskState interface {
@@ -9,9 +11,39 @@ type TaskState interface {
 
        CompensateState() string
 
-       Status() map[string]string
+       ForCompensation() bool
+
+       ForUpdate() bool
 
        Retry() []Retry
+
+       Catches() []ErrorMatch
+
+       Status() map[string]string
+
+       Loop() Loop
+}
+
+type Loop interface {
+       Parallel() int
+
+       Collection() string
+
+       ElementVariableName() string
+
+       ElementIndexName() string
+
+       CompletionCondition() string
+}
+
+type ErrorMatch interface {
+       Errors() []string
+
+       ErrorTypes() []reflect.Type
+
+       SetErrorTypes(errorTypes []reflect.Type)
+
+       Next() string
 }
 
 type Retry interface {
@@ -26,5 +58,376 @@ type Retry interface {
 
 type ServiceTaskState interface {
        TaskState
-       //TODO add serviceTask
+
+       ServiceType() string
+
+       ServiceName() string
+
+       ServiceMethod() string
+
+       ParameterTypes() []string
+
+       Persist() bool
+
+       RetryPersistModeUpdate() bool
+
+       CompensatePersistModeUpdate() bool
+}
+
+type AbstractTaskState struct {
+       *statelang.BaseState
+       loop                        Loop
+       catches                     []ErrorMatch
+       input                       []interface{}
+       output                      map[string]interface{}
+       compensatePersistModeUpdate bool
+       retryPersistModeUpdate      bool
+       forCompensation             bool
+       forUpdate                   bool
+       persist                     bool
+       compensateState             string
+       status                      map[string]string
+       retry                       []Retry
+}
+
+func NewAbstractTaskState() *AbstractTaskState {
+       return &AbstractTaskState{
+               BaseState: &statelang.BaseState{},
+       }
+}
+
+func (a *AbstractTaskState) Input() []interface{} {
+       return a.input
+}
+
+func (a *AbstractTaskState) SetInput(input []interface{}) {
+       a.input = input
+}
+
+func (a *AbstractTaskState) Output() map[string]interface{} {
+       return a.output
+}
+
+func (a *AbstractTaskState) SetOutput(output map[string]interface{}) {
+       a.output = output
+}
+
+func (a *AbstractTaskState) CompensatePersistModeUpdate() bool {
+       return a.compensatePersistModeUpdate
+}
+
+func (a *AbstractTaskState) 
SetCompensatePersistModeUpdate(isCompensatePersistModeUpdate bool) {
+       a.compensatePersistModeUpdate = isCompensatePersistModeUpdate
+}
+
+func (a *AbstractTaskState) RetryPersistModeUpdate() bool {
+       return a.retryPersistModeUpdate
+}
+
+func (a *AbstractTaskState) SetRetryPersistModeUpdate(retryPersistModeUpdate 
bool) {
+       a.retryPersistModeUpdate = retryPersistModeUpdate
+}
+
+func (a *AbstractTaskState) Persist() bool {
+       return a.persist
+}
+
+func (a *AbstractTaskState) SetPersist(persist bool) {
+       a.persist = persist
+}
+
+func (a *AbstractTaskState) SetLoop(loop Loop) {
+       a.loop = loop
+}
+
+func (a *AbstractTaskState) SetCatches(catches []ErrorMatch) {
+       a.catches = catches
+}
+
+func (a *AbstractTaskState) SetForCompensation(forCompensation bool) {
+       a.forCompensation = forCompensation
+}
+
+func (a *AbstractTaskState) SetForUpdate(forUpdate bool) {
+       a.forUpdate = forUpdate
+}
+
+func (a *AbstractTaskState) SetCompensateState(compensateState string) {
+       a.compensateState = compensateState
+}
+
+func (a *AbstractTaskState) SetStatus(status map[string]string) {
+       a.status = status
+}
+
+func (a *AbstractTaskState) SetRetry(retry []Retry) {
+       a.retry = retry
+}
+
+func (a *AbstractTaskState) ForCompensation() bool {
+       return a.forCompensation
+}
+
+func (a *AbstractTaskState) ForUpdate() bool {
+       return a.forUpdate
+}
+
+func (a *AbstractTaskState) Catches() []ErrorMatch {
+       return a.catches
+}
+
+func (a *AbstractTaskState) Loop() Loop {
+       return a.loop
+}
+
+func (a *AbstractTaskState) CompensateState() string {
+       return a.compensateState
+}
+
+func (a *AbstractTaskState) Status() map[string]string {
+       return a.status
+}
+
+func (a *AbstractTaskState) Retry() []Retry {
+       return a.retry
+}
+
+type ServiceTaskStateImpl struct {
+       *AbstractTaskState
+       serviceType                 string
+       serviceName                 string
+       serviceMethod               string
+       parameterTypes              []string
+       persist                     bool
+       retryPersistModeUpdate      bool
+       compensatePersistModeUpdate bool
+       isAsync                     bool
+}
+
+func NewServiceTaskStateImpl() *ServiceTaskStateImpl {
+       return &ServiceTaskStateImpl{
+               AbstractTaskState: NewAbstractTaskState(),
+       }
+}
+
+func (s *ServiceTaskStateImpl) IsAsync() bool {
+       return s.isAsync
+}
+
+func (s *ServiceTaskStateImpl) SetIsAsync(isAsync bool) {
+       s.isAsync = isAsync
+}
+
+func (s *ServiceTaskStateImpl) SetServiceType(serviceType string) {
+       s.serviceType = serviceType
+}
+
+func (s *ServiceTaskStateImpl) SetServiceName(serviceName string) {
+       s.serviceName = serviceName
+}
+
+func (s *ServiceTaskStateImpl) SetServiceMethod(serviceMethod string) {
+       s.serviceMethod = serviceMethod
+}
+
+func (s *ServiceTaskStateImpl) SetParameterTypes(parameterTypes []string) {
+       s.parameterTypes = parameterTypes
+}
+
+func (s *ServiceTaskStateImpl) SetPersist(persist bool) {
+       s.persist = persist
+}
+
+func (s *ServiceTaskStateImpl) 
SetRetryPersistModeUpdate(retryPersistModeUpdate bool) {
+       s.retryPersistModeUpdate = retryPersistModeUpdate
+}
+
+func (s *ServiceTaskStateImpl) 
SetCompensatePersistModeUpdate(compensatePersistModeUpdate bool) {
+       s.compensatePersistModeUpdate = compensatePersistModeUpdate
+}
+
+func (s *ServiceTaskStateImpl) Loop() Loop {
+       return s.loop
+}
+
+func (s *ServiceTaskStateImpl) ServiceType() string {
+       return s.serviceType
+}
+
+func (s *ServiceTaskStateImpl) ServiceName() string {
+       return s.serviceName
+}
+
+func (s *ServiceTaskStateImpl) ServiceMethod() string {
+       return s.serviceMethod
+}
+
+func (s *ServiceTaskStateImpl) ParameterTypes() []string {
+       return s.parameterTypes
+}
+
+func (s *ServiceTaskStateImpl) Persist() bool {
+       return s.persist
+}
+
+func (s *ServiceTaskStateImpl) RetryPersistModeUpdate() bool {
+       return s.retryPersistModeUpdate
+}
+
+func (s *ServiceTaskStateImpl) CompensatePersistModeUpdate() bool {
+       return s.compensatePersistModeUpdate
+}
+
+type LoopImpl struct {
+       parallel            int
+       collection          string
+       elementVariableName string
+       elementIndexName    string
+       completionCondition string
+}
+
+func (l *LoopImpl) SetParallel(parallel int) {
+       l.parallel = parallel
+}
+
+func (l *LoopImpl) SetCollection(collection string) {
+       l.collection = collection
+}
+
+func (l *LoopImpl) SetElementVariableName(elementVariableName string) {
+       l.elementVariableName = elementVariableName
+}
+
+func (l *LoopImpl) SetElementIndexName(elementIndexName string) {
+       l.elementIndexName = elementIndexName
+}
+
+func (l *LoopImpl) SetCompletionCondition(completionCondition string) {
+       l.completionCondition = completionCondition
+}
+
+func (l *LoopImpl) Parallel() int {
+       return l.parallel
+}
+
+func (l *LoopImpl) Collection() string {
+       return l.collection
+}
+
+func (l *LoopImpl) ElementVariableName() string {
+       return l.elementVariableName
+}
+
+func (l *LoopImpl) ElementIndexName() string {
+       return l.elementIndexName
+}
+
+func (l *LoopImpl) CompletionCondition() string {
+       return l.completionCondition
+}
+
+type RetryImpl struct {
+       errorTypeNames []string
+       intervalSecond float64
+       maxAttempt     int
+       backoffRate    float64
+}
+
+func (r *RetryImpl) SetErrorTypeNames(errorTypeNames []string) {
+       r.errorTypeNames = errorTypeNames
+}
+
+func (r *RetryImpl) SetIntervalSecond(intervalSecond float64) {
+       r.intervalSecond = intervalSecond
+}
+
+func (r *RetryImpl) SetMaxAttempt(maxAttempt int) {
+       r.maxAttempt = maxAttempt
+}
+
+func (r *RetryImpl) SetBackoffRate(backoffRate float64) {
+       r.backoffRate = backoffRate
+}
+
+func (r *RetryImpl) ErrorTypeNames() []string {
+       return r.errorTypeNames
+}
+
+func (r *RetryImpl) IntervalSecond() float64 {
+       return r.intervalSecond
+}
+
+func (r *RetryImpl) MaxAttempt() int {
+       return r.maxAttempt
+}
+
+func (r *RetryImpl) BackoffRate() float64 {
+       return r.backoffRate
+}
+
+type ErrorMatchImpl struct {
+       errors     []string
+       errorTypes []reflect.Type
+       next       string
+}
+
+func (e *ErrorMatchImpl) SetErrors(errors []string) {
+       e.errors = errors
+}
+
+func (e *ErrorMatchImpl) SetNext(next string) {
+       e.next = next
+}
+
+func (e *ErrorMatchImpl) Errors() []string {
+       return e.errors
+}
+
+func (e *ErrorMatchImpl) ErrorTypes() []reflect.Type {
+       return e.errorTypes
+}
+
+func (e *ErrorMatchImpl) SetErrorTypes(errorTypes []reflect.Type) {
+       e.errorTypes = errorTypes
+}
+
+func (e *ErrorMatchImpl) Next() string {
+       return e.next
+}
+
+type ScriptTaskState interface {
+       TaskState
+
+       ScriptType() string
+
+       ScriptContent() string
+}
+
+type ScriptTaskStateImpl struct {
+       *AbstractTaskState
+       scriptType    string
+       scriptContent string
+}
+
+func NewScriptTaskStateImpl() *ScriptTaskStateImpl {
+       return &ScriptTaskStateImpl{
+               AbstractTaskState: NewAbstractTaskState(),
+               scriptType:        constant.DefaultScriptType,
+       }
+}
+
+func (s *ScriptTaskStateImpl) SetScriptType(scriptType string) {
+       s.scriptType = scriptType
+}
+
+func (s *ScriptTaskStateImpl) SetScriptContent(scriptContent string) {
+       s.scriptContent = scriptContent
+}
+
+func (s *ScriptTaskStateImpl) ScriptType() string {
+       return s.scriptType
+}
+
+func (s *ScriptTaskStateImpl) ScriptContent() string {
+       return s.scriptContent
 }
diff --git a/testdata/saga/statelang/simple_statelang_with_choice.json 
b/testdata/saga/statelang/simple_statelang_with_choice.json
new file mode 100644
index 00000000..4be7a48f
--- /dev/null
+++ b/testdata/saga/statelang/simple_statelang_with_choice.json
@@ -0,0 +1,38 @@
+{
+    "Name": "simpleChoiceTestStateMachine",
+    "Comment": "带条件分支的测试状态机定义",
+    "StartState": "FirstState",
+    "Version": "0.0.1",
+    "States": {
+        "FirstState": {
+            "Type": "ServiceTask",
+            "ServiceName": "demoService",
+            "ServiceMethod": "foo",
+            "Next": "ChoiceState"
+        },
+        "ChoiceState":{
+            "Type": "Choice",
+            "Choices":[
+                {
+                    "Expression":"[a] == 1",
+                    "Next":"SecondState"
+                },
+                {
+                    "Expression":"[a] == 2",
+                    "Next":"ThirdState"
+                }
+            ],
+            "Default":"SecondState"
+        },
+        "SecondState": {
+            "Type": "ServiceTask",
+            "ServiceName": "demoService",
+            "ServiceMethod": "bar"
+        },
+        "ThirdState": {
+            "Type": "ServiceTask",
+            "ServiceName": "demoService",
+            "ServiceMethod": "foo"
+        }
+    }
+}
\ No newline at end of file
diff --git a/testdata/saga/statelang/simple_statemachine.json 
b/testdata/saga/statelang/simple_statemachine.json
new file mode 100644
index 00000000..91ed5019
--- /dev/null
+++ b/testdata/saga/statelang/simple_statemachine.json
@@ -0,0 +1,138 @@
+{
+  "Name": "simpleTestStateMachine",
+  "Comment": "测试状态机定义",
+  "StartState": "FirstState",
+  "Version": "0.0.1",
+  "States": {
+    "FirstState": {
+      "Type": "ServiceTask",
+      "ServiceName": "is.seata.saga.DemoService",
+      "ServiceMethod": "foo",
+      "IsPersist": false,
+      "Next": "ScriptState"
+    },
+    "ScriptState": {
+      "Type": "ScriptTask",
+      "ScriptType": "groovy",
+      "ScriptContent": "return 'hello ' + inputA",
+      "Input": [
+        {
+          "inputA": "$.data1"
+        }
+      ],
+      "Output": {
+        "scriptStateResult": "$.#root"
+      },
+      "Next": "ChoiceState"
+    },
+    "ChoiceState": {
+      "Type": "Choice",
+      "Choices": [
+        {
+          "Expression": "foo == 1",
+          "Next": "FirstMatchState"
+        },
+        {
+          "Expression": "foo == 2",
+          "Next": "SecondMatchState"
+        }
+      ],
+      "Default": "FailState"
+    },
+    "FirstMatchState": {
+      "Type": "ServiceTask",
+      "ServiceName": "is.seata.saga.DemoService",
+      "ServiceMethod": "bar",
+      "CompensateState": "CompensateFirst",
+      "Status": {
+        "return.code == 'S'": "SU",
+        "return.code == 'F'": "FA",
+        "$exception{java.lang.Throwable}": "UN"
+      },
+      "Input": [
+        {
+          "inputA1": "$.data1",
+          "inputA2": {
+            "a": "$.data2.a"
+          }
+        },
+        {
+          "inputB": "$.header"
+        }
+      ],
+      "Output": {
+        "firstMatchStateResult": "$.#root"
+      },
+      "Retry": [
+        {
+          "Exceptions": ["java.lang.Exception"],
+          "IntervalSeconds": 2,
+          "MaxAttempts": 3,
+          "BackoffRate": 1.5
+        }
+      ],
+      "Catch": [
+        {
+          "Exceptions": [
+            "java.lang.Exception"
+          ],
+          "Next": "CompensationTrigger"
+        }
+      ],
+      "Next": "SuccessState"
+    },
+    "CompensateFirst": {
+      "Type": "ServiceTask",
+      "ServiceName": "is.seata.saga.DemoService",
+      "ServiceMethod": "compensateBar",
+      "IsForCompensation": true,
+      "IsForUpdate": true,
+      "Input": [
+        {
+          "input": "$.data"
+        }
+      ],
+      "Output": {
+        "firstMatchStateResult": "$.#root"
+      },
+      "Status": {
+        "return.code == 'S'": "SU",
+        "return.code == 'F'": "FA",
+        "$exception{java.lang.Throwable}": "UN"
+      }
+    },
+    "CompensationTrigger": {
+      "Type": "CompensationTrigger",
+      "Next": "CompensateEndState"
+    },
+    "CompensateEndState": {
+      "Type": "Fail",
+      "ErrorCode": "StateCompensated",
+      "Message": "State Compensated!"
+    },
+    "SecondMatchState": {
+      "Type": "SubStateMachine",
+      "StateMachineName": "simpleTestSubStateMachine",
+      "Input": [
+        {
+          "input": "$.data"
+        },
+        {
+          "header": "$.header"
+        }
+      ],
+      "Output": {
+        "firstMatchStateResult": "$.#root"
+      },
+      "Next": "SuccessState"
+    },
+    "FailState": {
+      "Type": "Fail",
+      "ErrorCode": "DefaultStateError",
+      "Message": "No Matches!"
+    },
+    "SuccessState": {
+      "Type": "Succeed"
+    }
+  }
+}
\ No newline at end of file
diff --git a/testdata/saga/statelang/state_machine_new_designer.json 
b/testdata/saga/statelang/state_machine_new_designer.json
new file mode 100644
index 00000000..0fd815ec
--- /dev/null
+++ b/testdata/saga/statelang/state_machine_new_designer.json
@@ -0,0 +1,424 @@
+{
+    "Name": "StateMachineNewDesigner",
+    "Comment": "This state machine is modeled by designer tools.",
+    "Version": "0.0.1",
+    "style": {
+        "bounds": {
+            "x": 200,
+            "y": 200,
+            "width": 36,
+            "height": 36
+        }
+    },
+    "States": {
+        "ServiceTask-a9h2o51": {
+            "style": {
+                "bounds": {
+                    "x": 300,
+                    "y": 178,
+                    "width": 100,
+                    "height": 80
+                }
+            },
+            "Name": "ServiceTask-a9h2o51",
+            "IsForCompensation": false,
+            "Input": [
+                {}
+            ],
+            "Output": {},
+            "Status": {},
+            "Retry": [],
+            "ServiceName": "",
+            "ServiceMethod": "",
+            "Type": "ServiceTask",
+            "Next": "Choice-4ajl8nt",
+            "edge": {
+                "Choice-4ajl8nt": {
+                    "style": {
+                        "waypoints": [
+                            {
+                                "original": {
+                                    "x": 400,
+                                    "y": 218
+                                },
+                                "x": 400,
+                                "y": 218
+                            },
+                            {
+                                "x": 435,
+                                "y": 218
+                            },
+                            {
+                                "original": {
+                                    "x": 455,
+                                    "y": 218
+                                },
+                                "x": 455,
+                                "y": 218
+                            }
+                        ],
+                        "source": "ServiceTask-a9h2o51",
+                        "target": "Choice-4ajl8nt"
+                    },
+                    "Type": "Transition"
+                }
+            },
+            "CompensateState": "CompensateFirstState"
+        },
+        "Choice-4ajl8nt": {
+            "style": {
+                "bounds": {
+                    "x": 455,
+                    "y": 193,
+                    "width": 50,
+                    "height": 50
+                }
+            },
+            "Name": "Choice-4ajl8nt",
+            "Type": "Choice",
+            "Choices": [
+                {
+                    "Expression": "",
+                    "Next": "SubStateMachine-cauj9uy"
+                },
+                {
+                    "Expression": "",
+                    "Next": "ServiceTask-vdij28l"
+                }
+            ],
+            "Default": "SubStateMachine-cauj9uy",
+            "edge": {
+                "SubStateMachine-cauj9uy": {
+                    "style": {
+                        "waypoints": [
+                            {
+                                "original": {
+                                    "x": 505,
+                                    "y": 218
+                                },
+                                "x": 505,
+                                "y": 218
+                            },
+                            {
+                                "x": 530,
+                                "y": 218
+                            },
+                            {
+                                "original": {
+                                    "x": 550,
+                                    "y": 218
+                                },
+                                "x": 550,
+                                "y": 218
+                            }
+                        ],
+                        "source": "Choice-4ajl8nt",
+                        "target": "SubStateMachine-cauj9uy"
+                    },
+                    "Type": "ChoiceEntry"
+                },
+                "ServiceTask-vdij28l": {
+                    "style": {
+                        "waypoints": [
+                            {
+                                "original": {
+                                    "x": 480,
+                                    "y": 243
+                                },
+                                "x": 480,
+                                "y": 243
+                            },
+                            {
+                                "x": 600,
+                                "y": 290
+                            },
+                            {
+                                "original": {
+                                    "x": 600,
+                                    "y": 310
+                                },
+                                "x": 600,
+                                "y": 310
+                            }
+                        ],
+                        "source": "Choice-4ajl8nt",
+                        "target": "ServiceTask-vdij28l"
+                    },
+                    "Type": "ChoiceEntry"
+                }
+            }
+        },
+        "CompensateFirstState": {
+            "style": {
+                "bounds": {
+                    "x": 300,
+                    "y": 310,
+                    "width": 100,
+                    "height": 80
+                }
+            },
+            "Name": "CompensateFirstState",
+            "IsForCompensation": true,
+            "Input": [
+                {}
+            ],
+            "Output": {},
+            "Status": {},
+            "Retry": [],
+            "ServiceName": "",
+            "ServiceMethod": "",
+            "Type": "ServiceTask"
+        },
+        "SubStateMachine-cauj9uy": {
+            "style": {
+                "bounds": {
+                    "x": 550,
+                    "y": 178,
+                    "width": 100,
+                    "height": 80
+                }
+            },
+            "Name": "SubStateMachine-cauj9uy",
+            "IsForCompensation": false,
+            "Input": [
+                {}
+            ],
+            "Output": {},
+            "Status": {},
+            "Retry": [],
+            "StateMachineName": "",
+            "Type": "SubStateMachine",
+            "Next": "Succeed-5x3z98u",
+            "edge": {
+                "Succeed-5x3z98u": {
+                    "style": {
+                        "waypoints": [
+                            {
+                                "original": {
+                                    "x": 650,
+                                    "y": 218
+                                },
+                                "x": 650,
+                                "y": 218
+                            },
+                            {
+                                "x": 702,
+                                "y": 218
+                            },
+                            {
+                                "original": {
+                                    "x": 722,
+                                    "y": 218
+                                },
+                                "x": 722,
+                                "y": 218
+                            }
+                        ],
+                        "source": "SubStateMachine-cauj9uy",
+                        "target": "Succeed-5x3z98u"
+                    },
+                    "Type": "Transition"
+                }
+            }
+        },
+        "ServiceTask-vdij28l": {
+            "style": {
+                "bounds": {
+                    "x": 550,
+                    "y": 310,
+                    "width": 100,
+                    "height": 80
+                }
+            },
+            "Name": "ServiceTask-vdij28l",
+            "IsForCompensation": false,
+            "Input": [
+                {}
+            ],
+            "Output": {},
+            "Status": {},
+            "Retry": [],
+            "ServiceName": "",
+            "ServiceMethod": "",
+            "Catch": [
+                {
+                    "Exceptions": [],
+                    "Next": "CompensationTrigger-uldp2ou"
+                }
+            ],
+            "Type": "ServiceTask",
+            "catch": {
+                "style": {
+                    "bounds": {
+                        "x": 632,
+                        "y": 372,
+                        "width": 36,
+                        "height": 36
+                    }
+                },
+                "edge": {
+                    "CompensationTrigger-uldp2ou": {
+                        "style": {
+                            "waypoints": [
+                                {
+                                    "original": {
+                                        "x": 668,
+                                        "y": 390
+                                    },
+                                    "x": 668,
+                                    "y": 390
+                                },
+                                {
+                                    "x": 702,
+                                    "y": 390
+                                },
+                                {
+                                    "original": {
+                                        "x": 722,
+                                        "y": 390
+                                    },
+                                    "x": 722,
+                                    "y": 390
+                                }
+                            ],
+                            "source": "ServiceTask-vdij28l",
+                            "target": "CompensationTrigger-uldp2ou"
+                        },
+                        "Type": "ExceptionMatch"
+                    }
+                }
+            },
+            "Next": "Succeed-5x3z98u",
+            "edge": {
+                "Succeed-5x3z98u": {
+                    "style": {
+                        "waypoints": [
+                            {
+                                "original": {
+                                    "x": 600,
+                                    "y": 310
+                                },
+                                "x": 600,
+                                "y": 310
+                            },
+                            {
+                                "x": 740,
+                                "y": 256
+                            },
+                            {
+                                "original": {
+                                    "x": 740,
+                                    "y": 236
+                                },
+                                "x": 740,
+                                "y": 236
+                            }
+                        ],
+                        "source": "ServiceTask-vdij28l",
+                        "target": "Succeed-5x3z98u"
+                    },
+                    "Type": "Transition"
+                }
+            }
+        },
+        "Succeed-5x3z98u": {
+            "style": {
+                "bounds": {
+                    "x": 722,
+                    "y": 200,
+                    "width": 36,
+                    "height": 36
+                }
+            },
+            "Name": "Succeed-5x3z98u",
+            "Type": "Succeed"
+        },
+        "CompensationTrigger-uldp2ou": {
+            "style": {
+                "bounds": {
+                    "x": 722,
+                    "y": 372,
+                    "width": 36,
+                    "height": 36
+                }
+            },
+            "Name": "CompensationTrigger-uldp2ou",
+            "Type": "CompensationTrigger",
+            "Next": "Fail-9roxcv5",
+            "edge": {
+                "Fail-9roxcv5": {
+                    "style": {
+                        "waypoints": [
+                            {
+                                "original": {
+                                    "x": 758,
+                                    "y": 390
+                                },
+                                "x": 758,
+                                "y": 390
+                            },
+                            {
+                                "x": 792,
+                                "y": 390
+                            },
+                            {
+                                "original": {
+                                    "x": 812,
+                                    "y": 390
+                                },
+                                "x": 812,
+                                "y": 390
+                            }
+                        ],
+                        "source": "CompensationTrigger-uldp2ou",
+                        "target": "Fail-9roxcv5"
+                    },
+                    "Type": "Transition"
+                }
+            }
+        },
+        "Fail-9roxcv5": {
+            "style": {
+                "bounds": {
+                    "x": 812,
+                    "y": 372,
+                    "width": 36,
+                    "height": 36
+                }
+            },
+            "Name": "Fail-9roxcv5",
+            "ErrorCode": "",
+            "Message": "",
+            "Type": "Fail"
+        }
+    },
+    "StartState": "ServiceTask-a9h2o51",
+    "edge": {
+        "style": {
+            "waypoints": [
+                {
+                    "original": {
+                        "x": 236,
+                        "y": 218
+                    },
+                    "x": 236,
+                    "y": 218
+                },
+                {
+                    "x": 280,
+                    "y": 218
+                },
+                {
+                    "original": {
+                        "x": 300,
+                        "y": 218
+                    },
+                    "x": 300,
+                    "y": 218
+                }
+            ],
+            "target": "ServiceTask-a9h2o51"
+        },
+        "Type": "Transition"
+    }
+}
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to