You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@seata.apache.org by yi...@apache.org on 2024/02/02 02:48:15 UTC

(incubator-seata-go) branch feature/saga updated: feature: add serverice task parse in statelang (#650)

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 <92...@qq.com>
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: notifications-unsubscribe@seata.apache.org
For additional commands, e-mail: notifications-help@seata.apache.org