Files
condflow/tests/example_test.go

592 lines
19 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package tests
import (
"fmt"
"git.fsdpf.net/go/condflow"
"git.fsdpf.net/go/condflow/tests/fixtures"
"git.fsdpf.net/go/condition"
"git.fsdpf.net/go/contracts/base"
"git.fsdpf.net/go/reflux"
"git.fsdpf.net/go/reflux/fieldx"
"git.fsdpf.net/go/req"
"git.fsdpf.net/go/req/resx"
"github.com/samber/do/v2"
)
// setupExampleExecutor 创建并注册示例 executor
func setupExampleExecutor(app do.Injector) {
// 创建 resource
exampleRes := resx.New(app, "Example", "examples",
resx.WithUuid("660e8400-e29b-41d4-a716-446655440002"),
resx.WithName("示例"),
resx.WithFields(
resx.NewResField("id", "", resx.FieldWithName("ID"), resx.FieldWithDataType(req.ResInteger)),
resx.NewResField("created_user", "", resx.FieldWithName("创建者"), resx.FieldWithDefault("00000000-0000-0000-0000-000000000000"), resx.FieldWithDataType(req.ResString)),
resx.NewResField("owned_user", "", resx.FieldWithName("拥有者"), resx.FieldWithDefault("00000000-0000-0000-0000-000000000000"), resx.FieldWithDataType(req.ResString)),
resx.NewResField("created_at", "", resx.FieldWithName("创建时间"), resx.FieldWithDefault("sql:CURRENT_TIMESTAMP"), resx.FieldWithDataType(req.ResTimestamp)),
resx.NewResField("updated_at", "", resx.FieldWithName("更新时间"), resx.FieldWithDefault("sql:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"), resx.FieldWithDataType(req.ResTimestamp)),
resx.NewResField("uuid", "", resx.FieldWithName("UUID"), resx.FieldWithDefault("sql:uuid()"), resx.FieldWithDataType(req.ResString)),
resx.NewResField("status", "", resx.FieldWithName("状态"), resx.FieldWithDefault("pending"), resx.FieldWithDataType(req.ResString)),
resx.NewResField("type", "", resx.FieldWithName("类型"), resx.FieldWithDataType(req.ResString)),
resx.NewResField("data", "", resx.FieldWithName("数据"), resx.FieldWithDefault("{}"), resx.FieldWithDataType(req.ResJson)),
),
)
executor := condflow.NewActionInvoker(fixtures.NewExampleExecutor(exampleRes), nil)
do.ProvideNamedValue(app, fmt.Sprintf("%s/ExampleExecutor", executor.Res().GetCode()), executor)
}
// Example_userApprovalFlow 用户审批流示例
func Example_userApprovalFlow() {
// 初始化 DI 容器并注册 executor
app := do.New()
setupExampleExecutor(app)
// 创建条件流
flow := condflow.New("user-approval-flow")
// Case 1: 金额 <= 1000,自动通过
case1Cond := condition.New(condition.Describe("小额自动审批"))
case1Cond.SetExpr(condition.NewExpr("@", "amount", condition.Operator(condition.LE), condition.Token("1000", condition.STRING)))
case1Config, _ := fieldx.SchemaFromMap(map[string]any{
"notify": map[string]any{
"type": "string",
"value": "true",
},
"message": map[string]any{
"type": "string",
"value": "申请已自动通过",
},
})
case1 := condflow.NewCondCase(
"auto-approve-small",
case1Cond,
"Example/ExampleExecutor@AutoApprove",
).
WithPriority(100).
WithActionConfig(case1Config)
// Case 2: 1000 < 金额 <= 10000,需要经理审批
case2Cond := condition.New(condition.Describe("经理审批"))
case2Cond.SetExpr(condition.NewExpr("@", "amount", condition.Operator(condition.GT), condition.Token("1000", condition.STRING))).
SetExpr(condition.NewExpr("@", "amount", condition.Operator(condition.LE), condition.Token("10000", condition.STRING)))
case2Config, _ := fieldx.SchemaFromMap(map[string]any{
"approver_role": map[string]any{
"type": "string",
"value": "manager",
},
"timeout_hours": map[string]any{
"type": "string",
"value": "24",
},
"notify": map[string]any{
"type": "string",
"value": "true",
},
})
case2 := condflow.NewCondCase(
"manager-approval",
case2Cond,
"Example/ExampleExecutor@ManagerApproval",
).
WithPriority(90).
WithActionConfig(case2Config)
// Case 3: 金额 > 10000,需要总监审批
case3Cond := condition.New(condition.Describe("总监审批"))
case3Cond.SetExpr(condition.NewExpr("@", "amount", condition.Operator(condition.GT), condition.Token("10000", condition.STRING)))
case3Config, _ := fieldx.SchemaFromMap(map[string]any{
"approver_role": map[string]any{
"type": "string",
"value": "director",
},
"timeout_hours": map[string]any{
"type": "string",
"value": "48",
},
"notify": map[string]any{
"type": "string",
"value": "true",
},
"cc_roles": map[string]any{
"type": "string",
"value": "manager,ceo",
},
})
case3 := condflow.NewCondCase(
"director-approval",
case3Cond,
"Example/ExampleExecutor@DirectorApproval",
).
WithPriority(80).
WithActionConfig(case3Config)
// Case 4: 用户信用评分 < 60,直接拒绝
case4Cond := condition.New(condition.Describe("信用不足拒绝"))
case4Cond.SetExpr(condition.NewExpr("@", "user->>'credit_score'", condition.Operator(condition.LT), condition.Token("60", condition.STRING)))
case4Config, _ := fieldx.SchemaFromMap(map[string]any{
"reason": map[string]any{
"type": "string",
"value": "信用评分不足",
},
"notify": map[string]any{
"type": "string",
"value": "true",
},
"suggest_retry": map[string]any{
"type": "string",
"value": "false",
},
})
case4 := condflow.NewCondCase(
"reject-low-credit",
case4Cond,
"Example/ExampleExecutor@Reject",
).
WithPriority(200). // 最高优先级,优先检查
WithActionConfig(case4Config)
// 添加所有条件分支到流程
flow.AddCase(case4). // 优先级最高
AddCase(case1).
AddCase(case2).
AddCase(case3)
// 生成包含多种数据类型的测试 map
input := reflux.New(map[string]any{
// 基础类型
"amount": 5000.50, // float64
"count": 100, // int
"active": true, // bool
"user_name": "张三", // string
"age": int32(25), // int32
"total": int64(1000000), // int64
"score": float32(98.5), // float32
"is_vip": false, // bool
// 数组/切片类型
"tags": []string{"vip", "premium", "active"}, // slice
"scores": []int{85, 90, 95}, // slice of int
"prices": []float64{19.99, 29.99, 39.99}, // slice of float
// Map 类型
"preferences": map[string]any{
"newsletter": true,
"theme": "dark",
"language": "zh-CN",
},
// 嵌套结构
"user": map[string]any{
"id": 12345,
"email": "user@example.com",
"level": 5,
"credit_score": 85,
"created_at": "2024-01-01",
},
// 空值测试
"nullable_field": nil,
"empty_string": "",
})
flow.Run(app, input, base.GetSystemUser())
fmt.Printf("条件流名称: %s\n", flow.Name())
fmt.Printf("条件分支数量: %d\n", flow.CaseCount())
// Output:
// 条件流名称: user-approval-flow
// 条件分支数量: 4
}
// Example_emailCampaignFlow 邮件营销活动流示例
func Example_emailCampaignFlow() {
// 初始化 DI 容器并注册 executor
app := do.New()
setupExampleExecutor(app)
// 创建邮件营销条件流
flow := condflow.New("email-campaign-flow")
// Case 1: VIP 用户 - 发送专属优惠
vipCond := condition.New(condition.Describe("VIP用户筛选"))
vipCond.SetExpr(condition.NewExpr("User", "level", condition.Operator(condition.GE), condition.Token("5", condition.STRING))).
SetExpr(condition.NewExpr("User", "email", condition.Operator(condition.IS_NOT_NULL))).
SetExpr(condition.NewExpr("User", "preferences->'newsletter'", condition.Token("true", condition.STRING))).
SetExpr(condition.NewExpr("User", "tags", condition.FieldSqlFn("json_contains", ""), condition.Token("vip", condition.STRING)))
vipConfig, _ := fieldx.SchemaFromMap(map[string]any{
"template": map[string]any{
"type": "string",
"value": "vip-exclusive-offer",
},
"subject": map[string]any{
"type": "string",
"value": "尊贵的VIP会员,专属优惠来啦!",
},
"discount": map[string]any{
"type": "string",
"value": "30",
},
"valid_days": map[string]any{
"type": "string",
"value": "30",
},
"priority": map[string]any{
"type": "string",
"value": "high",
},
"attachments": map[string]any{
"type": "string",
"value": "vip-catalog.pdf",
},
})
vipCase := condflow.NewCondCase("vip-campaign", vipCond, "Example/ExampleExecutor@VIPEmail").
WithPriority(100).
WithActionConfig(vipConfig)
// Case 2: 新用户 - 发送欢迎邮件
newUserCond := condition.New(condition.Describe("新用户识别"))
newUserCond.SetExpr(condition.NewExpr("User", "created_at", condition.Operator(condition.GE), condition.Token("DATE_SUB(NOW(), INTERVAL 7 DAY)", condition.SQL))).
SetExpr(condition.NewExpr("User", "email_verified", condition.Token("true", condition.STRING)))
newUserConfig, _ := fieldx.SchemaFromMap(map[string]any{
"template": map[string]any{
"type": "string",
"value": "welcome-new-user",
},
"subject": map[string]any{
"type": "string",
"value": "欢迎加入我们!",
},
"coupon_code": map[string]any{
"type": "string",
"value": "WELCOME2024",
},
"discount": map[string]any{
"type": "string",
"value": "10",
},
"trigger_delay": map[string]any{
"type": "string",
"value": "1h",
},
})
newUserCase := condflow.NewCondCase("new-user-welcome", newUserCond, "Example/ExampleExecutor@WelcomeEmail").
WithPriority(90).
WithActionConfig(newUserConfig)
// Case 3: 普通活跃用户 - 发送常规营销邮件
regularCond := condition.New(condition.Describe("活跃普通用户"))
regularCond.SetExpr(condition.NewExpr("User", "last_login_at", condition.Operator(condition.GE), condition.Token("DATE_SUB(NOW(), INTERVAL 30 DAY)", condition.SQL))).
SetExpr(condition.NewExpr("User", "email", condition.Operator(condition.IS_NOT_NULL))).
SetExpr(condition.NewExpr("User", "preferences->'newsletter'", condition.Token("true", condition.STRING)))
regularConfig, _ := fieldx.SchemaFromMap(map[string]any{
"template": map[string]any{
"type": "string",
"value": "regular-promotion",
},
"subject": map[string]any{
"type": "string",
"value": "本月精选优惠",
},
"discount": map[string]any{
"type": "string",
"value": "15",
},
"valid_days": map[string]any{
"type": "string",
"value": "14",
},
})
regularCase := condflow.NewCondCase("regular-campaign", regularCond, "Example/ExampleExecutor@RegularEmail").
WithPriority(50).
WithActionConfig(regularConfig)
// Case 4: 未订阅用户 - 跳过
skipCond := condition.New(condition.Describe("未订阅用户"))
skipCond.SetExpr(condition.NewExpr("User", "preferences->'newsletter'", condition.Token("false", condition.STRING)))
skipConfig, _ := fieldx.SchemaFromMap(map[string]any{
"reason": map[string]any{
"type": "string",
"value": "用户未订阅营销邮件",
},
"log": map[string]any{
"type": "string",
"value": "true",
},
})
skipCase := condflow.NewCondCase("skip-unsubscribed", skipCond, "Example/ExampleExecutor@Skip").
WithPriority(200). // 最高优先级
WithActionConfig(skipConfig)
// 添加所有条件分支
flow.AddCase(skipCase).
AddCase(vipCase).
AddCase(newUserCase).
AddCase(regularCase)
fmt.Printf("条件流名称: %s\n", flow.Name())
fmt.Printf("条件分支数量: %d\n", flow.CaseCount())
// Output:
// 条件流名称: email-campaign-flow
// 条件分支数量: 4
}
// Example_orderProcessingFlow 订单处理流示例
func Example_orderProcessingFlow() {
// 初始化 DI 容器并注册 executor
app := do.New()
setupExampleExecutor(app)
// 创建订单处理条件流
flow := condflow.New("order-processing-flow")
// Case 1: 高价值订单 + VIP客户 -> 优先处理 + 包邮
premiumCond := condition.New(condition.Describe("高价值VIP订单"))
premiumCond.SetExpr(condition.NewExpr("Order", "total_amount", condition.Operator(condition.GE), condition.Token("5000", condition.STRING))).
SetExpr(condition.NewExpr("User", "level", condition.Operator(condition.GE), condition.Token("5", condition.STRING)))
premiumConfig, _ := fieldx.SchemaFromMap(map[string]any{
"shipping_method": map[string]any{
"type": "string",
"value": "express",
},
"shipping_fee": map[string]any{
"type": "string",
"value": "0",
},
"priority": map[string]any{
"type": "string",
"value": "high",
},
"gift_wrapping": map[string]any{
"type": "string",
"value": "true",
},
"insurance": map[string]any{
"type": "string",
"value": "true",
},
})
premiumCase := condflow.NewCondCase("premium-order", premiumCond, "Example/ExampleExecutor@ExpressShipping").
WithPriority(100).
WithActionConfig(premiumConfig).
WithNextCaseOnSuccess(true) // 继续执行下一个case(通知客户)
// Case 2: 紧急订单 -> 加急配送
urgentCond := condition.New(condition.Describe("紧急订单"))
urgentCond.SetExpr(condition.NewExpr("Order", "tags", condition.FieldSqlFn("json_contains", ""), condition.Token("urgent", condition.STRING)))
urgentConfig, _ := fieldx.SchemaFromMap(map[string]any{
"shipping_method": map[string]any{
"type": "string",
"value": "express",
},
"max_hours": map[string]any{
"type": "string",
"value": "24",
},
"priority": map[string]any{
"type": "string",
"value": "urgent",
},
})
urgentCase := condflow.NewCondCase("urgent-order", urgentCond, "Example/ExampleExecutor@ExpressShipping").
WithPriority(95).
WithActionConfig(urgentConfig)
// Case 3: 跨区域订单 -> 仓库调配
crossRegionCond := condition.New(condition.Describe("跨区域订单"))
crossRegionCond.SetExpr(condition.NewExpr("Order", "shipping_province", condition.Operator(condition.NE), condition.Token("warehouse_province", condition.PARAM)))
crossRegionConfig, _ := fieldx.SchemaFromMap(map[string]any{
"check_inventory": map[string]any{
"type": "string",
"value": "true",
},
"transfer_if_needed": map[string]any{
"type": "string",
"value": "true",
},
"estimated_days": map[string]any{
"type": "string",
"value": "5",
},
})
crossRegionCase := condflow.NewCondCase("cross-region-order", crossRegionCond, "Example/ExampleExecutor@WarehouseTransfer").
WithPriority(80).
WithActionConfig(crossRegionConfig)
// Case 4: 普通订单 -> 标准配送
standardCond := condition.New(condition.Describe("普通订单"))
standardCond.SetExpr(condition.NewExpr("Order", "total_amount", condition.Operator(condition.GT), condition.Token("0", condition.STRING)))
standardConfig, _ := fieldx.SchemaFromMap(map[string]any{
"shipping_method": map[string]any{
"type": "string",
"value": "standard",
},
"estimated_days": map[string]any{
"type": "string",
"value": "3",
},
})
standardCase := condflow.NewCondCase("standard-order", standardCond, "Example/ExampleExecutor@StandardShipping").
WithPriority(50).
WithActionConfig(standardConfig)
// Case 5: 通知客户(配合 premium case 的 nextCaseOnSuccess
notifyCond := condition.New(condition.Describe("需要通知的订单"))
notifyCond.SetExpr(condition.NewExpr("Order", "notify_customer", condition.Token("true", condition.STRING)))
notifyConfig, _ := fieldx.SchemaFromMap(map[string]any{
"channels": map[string]any{
"type": "string",
"value": "email,sms",
},
"template": map[string]any{
"type": "string",
"value": "order-confirmed",
},
"immediate": map[string]any{
"type": "string",
"value": "true",
},
})
notifyCase := condflow.NewCondCase("notify-customer", notifyCond, "Example/ExampleExecutor@NotifyCustomer").
WithPriority(40).
WithActionConfig(notifyConfig)
// 添加所有条件分支
flow.AddCase(premiumCase).
AddCase(urgentCase).
AddCase(crossRegionCase).
AddCase(standardCase).
AddCase(notifyCase)
fmt.Printf("条件流名称: %s\n", flow.Name())
fmt.Printf("条件分支数量: %d\n", flow.CaseCount())
// Output:
// 条件流名称: order-processing-flow
// 条件分支数量: 5
}
// Example_nestedFlow 嵌套流程示例 - 展示如何使用 NextFlowOnSuccess
func Example_nestedFlow() {
// 初始化 DI 容器并注册 executor
app := do.New()
setupExampleExecutor(app)
// 创建主流程:用户注册流程
mainFlow := condflow.New("user-registration-flow")
// 创建子流程:邮件验证流程
emailVerificationFlow := condflow.New("email-verification-flow")
verifyCond := condition.New(condition.Describe("发送验证邮件"))
verifyConfig, _ := fieldx.SchemaFromMap(map[string]any{
"template": map[string]any{
"type": "string",
"value": "email-verification",
},
"expire_in": map[string]any{
"type": "string",
"value": "24h",
},
})
verifyCase := condflow.NewCondCase("send-verify-email", verifyCond, "Example/ExampleExecutor@SendVerification").
WithActionConfig(verifyConfig)
emailVerificationFlow.AddCase(verifyCase)
// 创建子流程:欢迎礼包流程
welcomePackFlow := condflow.New("welcome-pack-flow")
couponCond := condition.New(condition.Describe("创建新用户优惠券"))
couponConfig, _ := fieldx.SchemaFromMap(map[string]any{
"coupon_type": map[string]any{
"type": "string",
"value": "new_user",
},
"discount": map[string]any{
"type": "string",
"value": "20",
},
"valid_days": map[string]any{
"type": "string",
"value": "30",
},
})
couponCase := condflow.NewCondCase("new-user-coupon", couponCond, "Example/ExampleExecutor@CreateCoupon").
WithActionConfig(couponConfig)
welcomePackFlow.AddCase(couponCase)
// 主流程 Case: 邮箱注册用户
emailRegCond := condition.New(condition.Describe("邮箱注册"))
emailRegCond.SetExpr(condition.NewExpr("User", "register_type", condition.Token("email", condition.STRING)))
emailRegConfig, _ := fieldx.SchemaFromMap(map[string]any{
"send_welcome": map[string]any{
"type": "string",
"value": "true",
},
})
emailRegCase := condflow.NewCondCase("email-registration", emailRegCond, "Example/ExampleExecutor@CreateUser").
WithPriority(100).
WithNextFlowOnSuccess(emailVerificationFlow, nil). // 成功后执行邮件验证流程
WithActionConfig(emailRegConfig)
// 主流程 Case: 手机号注册用户
phoneRegCond := condition.New(condition.Describe("手机号注册"))
phoneRegCond.SetExpr(condition.NewExpr("User", "register_type", condition.Token("phone", condition.STRING)))
phoneRegConfig, _ := fieldx.SchemaFromMap(map[string]any{
"send_sms": map[string]any{
"type": "string",
"value": "true",
},
})
phoneRegCase := condflow.NewCondCase("phone-registration", phoneRegCond, "Example/ExampleExecutor@CreateUser").
WithPriority(90).
WithNextFlowOnSuccess(welcomePackFlow, nil). // 成功后执行欢迎礼包流程
WithActionConfig(phoneRegConfig)
mainFlow.AddCase(emailRegCase).
AddCase(phoneRegCase)
fmt.Printf("主流程名称: %s\n", mainFlow.Name())
fmt.Printf("主流程分支数量: %d\n", mainFlow.CaseCount())
// Output:
// 主流程名称: user-registration-flow
// 主流程分支数量: 2
}