当前位置: 首页 > news >正文

[Go类库分享]Go template模版库

[Go类库分享]Go template模版库

官方地址:https://pkg.go.dev/text/template
文章所含代码Github地址:https://github.com/ziyifast/ziyifast-code_instruction/tree/main/go-demo/go-template

在这里插入图片描述

1. 概述

Go的template库主要包含两类,一类是html/template、另一类是text/template,其中html/template主要用于生成展示前端页面。本文主要会介绍text/template。

Go 的 text/template 包提供了一个强大的模板引擎,用于生成文本输出。它通过将数据应用于模板来工作,模板中包含特殊标记(称为"动作"),用于控制文档生成过程。

Go template 语言特点:

Go template 的设计使其成为流程编排和动态逻辑构建的理想选择。

  1. 声明式编程范式:通过模板定义处理逻辑,而非硬编码在程序中
  2. 动态性:运行时可以修改模板而无需重新编译和部署
  3. 安全性:内置沙箱机制,防止恶意代码执行
  4. 丰富的控制结构:支持条件判断、循环、变量定义等

模板执行流程:

  1. 解析模板: template.New().Parse()
  2. 注册函数: Funcs()
  3. 执行模板: Execute()
  4. 输出结果: 写入指定的 io.Writer

注意:函数必须在解析模板之前注册,否则模板中无法使用这些函数。

2. 基本语法

2.1 基本结构

// 模板中的基本动作由双大括号 {{}} 包围
{{.}}               // 当前上下文
{{.FieldName}}      // 访问结构体字段
{{.MethodName}}     // 调用方法
{{$variable}}       // 访问变量

案例:


// 基础使用
func case_basic_use() {type Person struct {Name stringAge  int}tmpl := `姓名: {{.Name}}, 年龄: {{.Age}}`t := template.Must(template.New("example").Parse(tmpl))person := Person{Name: "张三", Age: 30}err := t.Execute(os.Stdout, person)if err != nil {panic(err)}
}

2.2 注释

{{/* 这是一个注释 */}}

案例:


// 包含注释
func case_comment_use() {tmpl := `{{/* 这是一个注释 */}}
Hello, {{.Name}}!
{{/* 另一个注释 */}}`t := template.Must(template.New("example").Parse(tmpl))err := t.Execute(os.Stdout, map[string]string{"Name": "世界"})if err != nil {panic(err)}
}

2.3 输出值

{{.}}                    // 输出当前上下文
{{.Field}}               // 输出结构体字段
{{.Method}}              // 输出方法返回值
{{index .Array 0}}       // 输出数组第一个元素
{{.Map.key}}             // 输出map的值

案例:


// 获取结构体属性
func case_get_field() {type Data struct {Name    stringHobbies []stringInfo    map[string]string}data := Data{Name:    "张三",Hobbies: []string{"读书", "游泳", "跑步"},Info:    map[string]string{"city": "北京", "job": "工程师"},}tmpl := `姓名: {{.Name}}
第一个爱好: {{index .Hobbies 0}}
城市: {{.Info.city}}`t := template.Must(template.New("example").Parse(tmpl))err := t.Execute(os.Stdout, data)if err != nil {panic(err)}
}

2.4 控制结构

比较判断

  • eq: 等于 (==)
  • ne: 不等于 (!=)
  • lt: 小于 (<)
  • le: 小于等于 (<=)
  • gt: 大于 (>)
  • ge: 大于等于 (>=)

代码案例:

func case_comparison() {user := map[string]interface{}{"name": "curry","age":  20,}//比较判断//- eq: 等于 (==)//- ne: 不等于 (!=)//- lt: 小于 (<)//- le: 小于等于 (<=)//- gt: 大于 (>)//- ge: 大于等于 (>=)tmpl := `{{if ge .age 18}} 已成年{{else}}未成年{{end}}`t := template.Must(template.New("example").Parse(tmpl))err := t.Execute(os.Stdout, user)if err != nil {panic(err)}
}

条件判断

{{if .Condition}}Content when condition is true
{{else if .OtherCondition}}Other content
{{else}}Content when condition is false
{{end}}

案例:

// 条件判断
func case_condition() {data := map[string]interface{}{"Age":    25,"Name":   "张三","Active": true,}tmpl := `{{if .Active}}
用户 {{.Name}} 处于活跃状态
{{else}}
用户 {{.Name}} 处于非活跃状态
{{end}}
{{if gt .Age 18}}
用户已成年
{{else}}
用户未成年
{{end}}`t := template.Must(template.New("example").Parse(tmpl))err := t.Execute(os.Stdout, data)if err != nil {panic(err)}
}

循环

{{range .Items}}{{.}} // 当前元素
{{end}}{{range $index, $element := .Items}}{{$index}}: {{$element}}
{{end}}// 使用 else 处理空情况
{{range .Items}}{{.}}
{{else}}No items found
{{end}}

案例:


// 循环
func case5_loop() {data := map[string][]string{"Fruits": {"苹果", "香蕉", "橙子"},}tmpl := `水果列表:
{{range .Fruits}}
- {{.}}
{{end}}带索引的列表:
{{range $index, $fruit := .Fruits}}
{{add $index 1}}. {{$fruit}}
{{end}}空列表处理:
{{range .Vegetables}}
- {{.}}
{{else}}
没有蔬菜
{{end}}`funcMap := template.FuncMap{"add": func(a, b int) int { return a + b },}t := template.Must(template.New("example").Funcs(funcMap).Parse(tmpl))err := t.Execute(os.Stdout, data)if err != nil {panic(err)}
}

定义变量

{{$name := .UserName}}
{{$count := len .Items}}

案例:

// 定义变量
func case_define_variable() {data := map[string]interface{}{"Items": []string{"item1", "item2", "item3"},"Name":  "张三",}tmpl := `{{$name := .Name}}
{{$count := len .Items}}
用户: {{$name}}
项目数量: {{$count}}{{range $index, $item := .Items}}
项目 {{$index}}: {{$item}}
{{end}}`t := template.Must(template.New("example").Parse(tmpl))err := t.Execute(os.Stdout, data)if err != nil {panic(err)}
}

2.5 函数和管道

Go 模板支持函数调用和管道操作:

// 内置函数
{{printf "Hello, %s!" .Name}}
{{len .Items}}
{{index .Map "key"}}// 管道操作
{{.Name | printf "%s"}}
{{.Value | multiply 2 | add 10}}

案例:

// 管道
func case_pipe() {data := map[string]interface{}{"Name":  "zhangsan","Text":  "hello world","Items": []string{"item1", "item2", "item3"},}funcMap := template.FuncMap{"upper": strings.ToUpper,"join": func(sep string, s []string) string {return strings.Join(s, sep)},}tmpl := `原始名称: {{.Name}}
大写名称: {{.Name | upper}}
项目数量: {{len .Items}}
项目连接: {{join "," .Items}}格式化: {{printf "Hello %s, you have %d items" .Name (len .Items)}}`t := template.Must(template.New("example").Funcs(funcMap).Parse(tmpl))err := t.Execute(os.Stdout, data)if err != nil {panic(err)}
}

2.6 错误处理

我们可以自定义一个函数,当不满足条件时,我们可以抛出自己的业务错误。

package mainimport ("fmt""os""text/template"
)// 错误结构
type BusinessError struct {Code    intMessage string
}func (e BusinessError) Error() string {return fmt.Sprintf("[%d] %s", e.Code, e.Message)
}// 在模板中抛出错误的核心函数
func throwError(code int, message string) (interface{}, error) {return nil, BusinessError{Code:    code,Message: message,}
}func main() {tmpl := `
{{/* 条件性抛出错误 */}}
{{$age := 15}}
{{ if gt $age 18}}已成年
{{else}}{{throwError 1001 "年龄不满足"}}
{{end}}
`funcMap := template.FuncMap{"throwError": throwError,}parsedTmpl, err := template.New("simple_error").Funcs(funcMap).Parse(tmpl)if err != nil {fmt.Printf("模板解析错误: %v\n", err)return}err = parsedTmpl.Execute(os.Stdout, nil)if err != nil {fmt.Printf("执行模板错误: %v\n", err)}
}

效果:
在这里插入图片描述

3. 高级功能

3.1 自定义函数

要向模板中添加自定义函数,需要使用 Funcs 方法:

// 自定义函数
func case_custom_function() {funcMap := template.FuncMap{"formatTime": formatTime,"multiply":   multiply,"upper":      strings.ToUpper,}data := map[string]interface{}{"Name":     "张三","Time":     time.Now(),"Price":    100,"Quantity": 3,}tmpl := `用户: {{.Name | upper}}
当前时间: {{.Time | formatTime}}
商品总价: {{multiply .Price .Quantity}} 元`t := template.Must(template.New("example").Funcs(funcMap).Parse(tmpl))err := t.Execute(os.Stdout, data)if err != nil {panic(err)}
}func formatTime(t time.Time) string {return t.Format("2006-01-02 15:04:05")
}func multiply(a, b int) int {return a * b
}

3.2 模板嵌套

主要用于展示固定结构的页面,比如:html/template会用来展示固定前端页面,text/template会用来展示邮件、消息模版等。

// 模版嵌套
func case_template_nesting() {tmpl := `
{{define "header"}}
================================
{{.Title}}
================================
{{end}}{{define "content"}}
用户信息:
姓名: {{.User.Name}}
邮箱: {{.User.Email}}
{{end}}{{define "footer"}}
--------------------------------
报告生成时间: {{.Time}}
--------------------------------
{{end}}{{template "header" .}}
{{template "content" .}}
{{template "footer" .}}`data := map[string]interface{}{"Title": "用户报告","User": map[string]string{"Name":  "张三","Email": "zhangsan@example.com",},"Time": "2023-01-01 12:00:00",}t := template.Must(template.New("example").Parse(tmpl))err := t.Execute(os.Stdout, data)if err != nil {panic(err)}
}

3.3 去除空行

有些时候我们想要去除模版中的换行、空白符等,这个时候就需要使用类似{{- -}}语法来去除。

Go模板提供空白符控制功能,用于精确控制输出格式:

  • {{- : 去除标记左侧的空白符(包括空格、制表符、换行符)
  • -}} : 去除标记右侧的空白符
  • {{- -}} : 去除标记两侧的空白符

TIPS:如果后续脚本需要在业务上频繁使用,那么为了编写方便,我们也可以进行适配,比如通过前置处理器的方式,自动添加{{ }},让用户编写时,更关注流程,减少因编写格式带来的时间损耗。

// 移除空行
// {{-:去除左侧的空白符(包括空格、制表符和换行符)
// -}}:去除右侧的空白符
// {{- -}}:去除两侧的空白符
func case_remove_empty_line() {//不去除空行的模版tmpl := `
{{range .Lines}}
{{.}}
{{end}}`//去除空行的模版tmpl = `
{{- range .Lines -}}
{{- . -}}
{{- end -}}`data := map[string]interface{}{"Lines": []string{"", "line1", "", "line2", ""},}t := template.Must(template.New("example").Parse(tmpl))err := t.Execute(os.Stdout, data)if err != nil {panic(err)}
}

4. 实战应用:服务编排

go的这种模版语言,包含流程处理、函数定义等,很适合用来做流程编排,动态构建一些简单逻辑,避免频繁调整代码、发版等,比如在处理消息队列的消息时,就很适合,可以用来配合消息总线+数据处理,实现下游服务只用关心自己需要的结构,将整个解析逻辑放在脚本服务编排里。这种架构的优势:

  1. 解耦性:下游服务只需关注自己的数据结构
  2. 灵活性:通过修改模板即可调整处理逻辑
  3. 可维护性:业务逻辑集中管理,无需频繁发版
  4. 可观测性:模板执行过程可以被完整记录和追踪

整体流程:

[消息生产者][消息队列][模板编排引擎][下游服务][模板配置中心]

这里主要给大家演示最核心的部分,服务编排,其他部分大家可根据文中代码自行补充实现:

package mainimport ("encoding/json""fmt""log""net/http""os""text/template""time"
)// 服务响应结构
type ServiceResponse struct {Data interface{} `json:"data"`
}// 用户信息
type User struct {ID       string `json:"id"`Name     string `json:"name"`Email    string `json:"email"`Level    string `json:"level"`Region   string `json:"region"`Language string `json:"language"`
}// 订单信息
type Order struct {ID         string            `json:"id"`Amount     float64           `json:"amount"`Status     string            `json:"status"`Currency   string            `json:"currency"`Items      []OrderItem       `json:"items"`Metadata   map[string]string `json:"metadata"`CreatedAt  string            `json:"created_at"`CustomerID string            `json:"customer_id"`
}// 订单项
type OrderItem struct {ProductID   string  `json:"product_id"`ProductName string  `json:"product_name"`Quantity    int     `json:"quantity"`Price       float64 `json:"price"`
}// 支付信息
type Payment struct {ID          string  `json:"id"`OrderID     string  `json:"order_id"`Method      string  `json:"method"`Status      string  `json:"status"`Amount      float64 `json:"amount"`Currency    string  `json:"currency"`CreatedAt   string  `json:"created_at"`Transaction string  `json:"transaction_id"`
}// 库存信息
type Inventory struct {ProductID string `json:"product_id"`Stock     int    `json:"stock"`Reserved  int    `json:"reserved"`
}// 通知配置
type NotificationConfig struct {Channels []string `json:"channels"`Template string   `json:"template"`
}// 模拟数据
var (users = map[string]User{"cust_12345": {ID: "cust_12345", Name: "张三", Email: "zhangsan@example.com", Level: "VIP", Region: "CN", Language: "zh-CN"},"cust_67890": {ID: "cust_67890", Name: "李四", Email: "lisi@example.com", Level: "普通", Region: "US", Language: "en-US"},}orders = map[string]Order{"ord_001": {ID:         "ord_001",Amount:     299.99,Status:     "completed",Currency:   "CNY",CreatedAt:  "2023-01-01T10:30:00Z",CustomerID: "cust_12345",Items: []OrderItem{{ProductID: "prod_001", ProductName: "iPhone 14", Quantity: 1, Price: 299.99},},Metadata: map[string]string{"source": "web","promo":  "NEWYEAR2023",},},"ord_002": {ID:         "ord_002",Amount:     199.50,Status:     "processing",Currency:   "CNY",CreatedAt:  "2023-01-02T14:15:00Z",CustomerID: "cust_12345",Items: []OrderItem{{ProductID: "prod_002", ProductName: "AirPods", Quantity: 1, Price: 199.50},},Metadata: map[string]string{"source": "mobile",},},}payments = map[string]Payment{"pay_001": {ID: "pay_001", OrderID: "ord_001", Method: "credit_card", Status: "success", Amount: 299.99, Currency: "CNY", CreatedAt: "2023-01-01T10:30:00Z", Transaction: "txn_abc123"},"pay_002": {ID: "pay_002", OrderID: "ord_002", Method: "alipay", Status: "pending", Amount: 199.50, Currency: "CNY", CreatedAt: "2023-01-02T14:15:00Z", Transaction: "txn_def456"},}inventories = map[string]Inventory{"prod_001": {ProductID: "prod_001", Stock: 100, Reserved: 10},"prod_002": {ProductID: "prod_002", Stock: 50, Reserved: 5},}notificationConfigs = map[string]NotificationConfig{"order_completed":  {Channels: []string{"email", "sms"}, Template: "order_completion"},"order_processing": {Channels: []string{"email"}, Template: "order_processing"},}
)// 模拟用户服务
func userHandler(w http.ResponseWriter, r *http.Request) {userID := r.URL.Query().Get("id")if userID == "" {http.Error(w, "缺少用户ID参数", http.StatusBadRequest)return}user, exists := users[userID]if !exists {http.Error(w, "用户不存在", http.StatusNotFound)return}response := ServiceResponse{Data: user}w.Header().Set("Content-Type", "application/json")json.NewEncoder(w).Encode(response)
}// 模拟订单服务
func orderHandler(w http.ResponseWriter, r *http.Request) {orderID := r.URL.Query().Get("id")if orderID == "" {http.Error(w, "缺少订单ID参数", http.StatusBadRequest)return}order, exists := orders[orderID]if !exists {http.Error(w, "订单不存在", http.StatusNotFound)return}response := ServiceResponse{Data: order}w.Header().Set("Content-Type", "application/json")json.NewEncoder(w).Encode(response)
}// 模拟支付服务
func paymentHandler(w http.ResponseWriter, r *http.Request) {orderID := r.URL.Query().Get("order_id")if orderID == "" {http.Error(w, "缺少订单ID参数", http.StatusBadRequest)return}// 查找与订单关联的支付信息var payment *Paymentfor _, p := range payments {if p.OrderID == orderID {payment = &pbreak}}if payment == nil {http.Error(w, "支付信息不存在", http.StatusNotFound)return}response := ServiceResponse{Data: *payment}w.Header().Set("Content-Type", "application/json")json.NewEncoder(w).Encode(response)
}// 模拟库存服务
func inventoryHandler(w http.ResponseWriter, r *http.Request) {productID := r.URL.Query().Get("product_id")if productID == "" {http.Error(w, "缺少产品ID参数", http.StatusBadRequest)return}inventory, exists := inventories[productID]if !exists {http.Error(w, "库存信息不存在", http.StatusNotFound)return}response := ServiceResponse{Data: inventory}w.Header().Set("Content-Type", "application/json")json.NewEncoder(w).Encode(response)
}// 模拟通知配置服务
func notificationConfigHandler(w http.ResponseWriter, r *http.Request) {eventType := r.URL.Query().Get("event_type")if eventType == "" {http.Error(w, "缺少事件类型参数", http.StatusBadRequest)return}config, exists := notificationConfigs[eventType]if !exists {// 返回默认配置config = NotificationConfig{Channels: []string{"email"}, Template: "default"}}response := ServiceResponse{Data: config}w.Header().Set("Content-Type", "application/json")json.NewEncoder(w).Encode(response)
}// 启动模拟服务
func startMockServices() {http.HandleFunc("/users", userHandler)http.HandleFunc("/orders", orderHandler)http.HandleFunc("/payments", paymentHandler)http.HandleFunc("/inventory", inventoryHandler)http.HandleFunc("/notification-config", notificationConfigHandler)fmt.Println("模拟服务已启动在 :8080 端口")fmt.Println("用户服务: http://localhost:8080/users?id=cust_12345")fmt.Println("订单服务: http://localhost:8080/orders?id=ord_001")fmt.Println("支付服务: http://localhost:8080/payments?order_id=ord_001")fmt.Println("库存服务: http://localhost:8080/inventory?product_id=prod_001")fmt.Println("通知配置服务: http://localhost:8080/notification-config?event_type=order_completed")fmt.Println()go func() {log.Fatal(http.ListenAndServe(":8080", nil))}()// 等待服务启动time.Sleep(100 * time.Millisecond)
}// 自定义函数:调用用户服务
func getUserService(userID string) (map[string]interface{}, error) {resp, err := http.Get("http://localhost:8080/users?id=" + userID)if err != nil {return nil, err}defer resp.Body.Close()var result ServiceResponseif err := json.NewDecoder(resp.Body).Decode(&result); err != nil {return nil, err}// 转换为 map[string]interface{}userData, ok := result.Data.(map[string]interface{})if !ok {return nil, fmt.Errorf("用户数据格式错误")}return userData, nil
}// 自定义函数:调用订单服务
func getOrderService(orderID string) (map[string]interface{}, error) {resp, err := http.Get("http://localhost:8080/orders?id=" + orderID)if err != nil {return nil, err}defer resp.Body.Close()var result ServiceResponseif err := json.NewDecoder(resp.Body).Decode(&result); err != nil {return nil, err}// 转换为 map[string]interface{}orderData, ok := result.Data.(map[string]interface{})if !ok {return nil, fmt.Errorf("订单数据格式错误")}return orderData, nil
}// 自定义函数:调用支付服务
func getPaymentService(orderID string) (map[string]interface{}, error) {resp, err := http.Get("http://localhost:8080/payments?order_id=" + orderID)if err != nil {return nil, err}defer resp.Body.Close()var result ServiceResponseif err := json.NewDecoder(resp.Body).Decode(&result); err != nil {return nil, err}// 转换为 map[string]interface{}paymentData, ok := result.Data.(map[string]interface{})if !ok {return nil, fmt.Errorf("支付数据格式错误")}return paymentData, nil
}// 自定义函数:调用库存服务
func getInventoryService(productID string) (map[string]interface{}, error) {resp, err := http.Get("http://localhost:8080/inventory?product_id=" + productID)if err != nil {return nil, err}defer resp.Body.Close()var result ServiceResponseif err := json.NewDecoder(resp.Body).Decode(&result); err != nil {return nil, err}// 转换为 map[string]interface{}inventoryData, ok := result.Data.(map[string]interface{})if !ok {return nil, fmt.Errorf("库存数据格式错误")}return inventoryData, nil
}// 自定义函数:获取通知配置
func getNotificationConfig(eventType string) (map[string]interface{}, error) {resp, err := http.Get("http://localhost:8080/notification-config?event_type=" + eventType)if err != nil {return nil, err}defer resp.Body.Close()var result ServiceResponseif err := json.NewDecoder(resp.Body).Decode(&result); err != nil {return nil, err}// 转换为 map[string]interface{}configData, ok := result.Data.(map[string]interface{})if !ok {return nil, fmt.Errorf("通知配置数据格式错误")}return configData, nil
}// 自定义函数:格式化货币
func formatCurrency(amount float64, currency string) string {switch currency {case "CNY":return fmt.Sprintf("¥%.2f", amount)case "USD":return fmt.Sprintf("$%.2f", amount)default:return fmt.Sprintf("%.2f %s", amount, currency)}
}// 自定义函数:获取订单状态描述
func getOrderStatusDescription(status string) string {statusMap := map[string]string{"completed":  "已完成","processing": "处理中","cancelled":  "已取消","pending":    "待处理",}if desc, ok := statusMap[status]; ok {return desc}return status
}// 辅助函数:加法
func add(a, b int) int {return a + b
}// 辅助函数:乘法
func multiply(a, b float64) float64 {return a * b
}// 辅助函数:连接字符串
func join(sep string, s []interface{}) string {if len(s) == 0 {return ""}result := fmt.Sprintf("%v", s[0])for _, item := range s[1:] {result += sep + fmt.Sprintf("%v", item)}return result
}// 辅助函数:转换为JSON
func toJson(v interface{}) string {b, _ := json.Marshal(v)return string(b)
}func main() {// 启动模拟服务startMockServices()// 定义所有函数funcMap := template.FuncMap{"getUser":               getUserService,"getOrder":              getOrderService,"getPayment":            getPaymentService,"getInventory":          getInventoryService,"getNotificationConfig": getNotificationConfig,"formatCurrency":        formatCurrency,"getOrderStatus":        getOrderStatusDescription,"add":                   add,"multiply":              multiply,"join":                  join,"toJson":                toJson,}// 创建复杂的业务流程编排模板tmplText := `
{{/* 获取订单信息 */}}
{{$order := getOrder "ord_001"}}
{{$customer := getUser $order.customer_id}}订单处理报告
================================
订单ID: {{$order.id}}
订单状态: {{getOrderStatus $order.status}}
创建时间: {{$order.created_at}}
订单金额: {{formatCurrency $order.amount $order.currency}}客户信息:姓名: {{$customer.name}}邮箱: {{$customer.email}}等级: {{$customer.level}}地区: {{$customer.region}}订单项目:
{{range $index, $item := $order.items}}{{$index | add 1}}. {{$item.product_name}}产品ID: {{$item.product_id}}数量: {{$item.quantity}}单价: {{formatCurrency $item.price $order.currency}}小计: {{formatCurrency (multiply $item.quantity $item.price) $order.currency}}
{{end}}{{/* 获取支付信息 */}}
{{$payment := getPayment $order.id}}
支付信息:支付ID: {{$payment.id}}支付方式: {{$payment.method}}支付状态: {{$payment.status}}交易号: {{$payment.transaction_id}}支付时间: {{$payment.created_at}}{{/* 检查库存信息 */}}
{{$inventoryIssues := false}}
库存检查:
{{range $item := $order.items}}{{$inventory := getInventory $item.product_id}}产品 {{$item.product_name}}:可用库存: {{$inventory.stock}}已预留: {{$inventory.reserved}}{{if lt $inventory.stock $item.quantity}}警告: 库存不足!{{$inventoryIssues = true}}{{end}}
{{end}}{{/* 获取通知配置 */}}
{{$notificationConfig := getNotificationConfig (printf "order_%s" $order.status)}}
通知配置:渠道: {{join ", " $notificationConfig.channels}}模板: {{$notificationConfig.template}}{{/* 业务决策 */}}
{{if eq $order.status "completed"}}{{if eq $payment.status "success"}}{{if not $inventoryIssues}}
处理结论: 订单处理成功,可以进行发货准备{{else}}
处理结论: 订单支付成功但库存不足,请人工处理{{end}}{{else}}
处理结论: 订单状态异常,请检查支付状态{{end}}
{{else if eq $order.status "processing"}}
处理结论: 订单正在处理中,等待后续状态更新
{{else}}
处理结论: 订单状态{{$order.status}},按相应流程处理
{{end}}{{/* 生成下游服务调用 */}}
下游服务调用:
{{if eq $order.status "completed"}}1. 发货服务: prepareShipment({{toJson $order}})2. 通知服务: sendNotification({{toJson $notificationConfig}}, {{$customer.email}})3. 积分服务: addPoints({{$customer.id}}, {{$order.amount}})
{{end}}
`// 创建模板并添加所有函数tmpl := template.Must(template.New("business_process").Funcs(funcMap).Parse(tmplText))// 执行模板fmt.Println("开始执行业务流程编排...")fmt.Println("========================")err := tmpl.Execute(os.Stdout, nil)if err != nil {log.Fatal("执行模板错误: ", err)}
}

运行效果:
在这里插入图片描述

5. 拓展

与go expr混合使用

Go expr可以放在每个节点,控制是否需要条件执行、不执行、循环执行(迭代)等。go template关注每个节点自身的逻辑。

  • Go expr教程地址

通用流程编排:

  • Go template:基于Go标准库构建,主要承担业务逻辑表达、数据处理和文本生成功能
  • Go expr:作为轻量级表达式引擎,用于DAG节点的条件判断、路由决策和循环控制(满足什么样的条件才执行该节点等)

职责分工:

  • Go template:关注节点内部的业务逻辑实现和数据处理
  • Go expr:专注条件判断和流程控制逻辑

应用场景:

  • 节点执行条件判断:customer.level == ‘VIP’ && order.amount > 1000
  • 边路由条件:result.status == ‘success’
  • 循环控制条件:loop.index < array.length
  • 数据验证:input.field != null && input.field.length > 0

DAG概念

DAG(Directed Acyclic Graph):有向无环图,是工作流编排的核心数据结构。

与传统流程控制的区别:

  • 传统流程:通常是线性的或简单的分支结构
  • DAG流程:支持复杂的多分支、汇聚、并行等结构

1. DAG的基本特征:

  • 有向性:节点之间的连接有明确的方向性
  • 无环性:不存在从某个节点出发经过若干条边后能回到该节点的路径
  • 节点:代表工作流中的任务或决策点
  • 边:表示节点间的依赖关系和执行流向

2. 在流程编排中的应用:

  • 节点(Nodes):对应工作流中的各个步骤(任务、决策、循环等)
  • 边(Edges):定义执行顺序和依赖关系
  • 入度:指向某个节点的边的数量,入度为0的节点是起始节点
  • 出度:从某个节点指出的边的数量,出度为0的节点是结束节点

3. DAG的优势:

  • 清晰的依赖关系:明确表达任务间的前后依赖
  • 并行执行:无依赖关系的节点可以并行执行
  • 易于分析:可以进行拓扑排序,确定执行顺序
  • 避免循环依赖:防止工作流陷入无限循环
  • 可视化友好:便于图形化展示和理解

4. 实际应用场景
电商订单处理:

  • 订单验证 → 库存检查 → 支付处理 → 发货通知
  • VIP客户特殊处理流程

数据ETL管道:

  • 数据提取 → 数据转换 → 数据验证 → 数据加载
  • 异常数据重试机制

审批流程:

  • 提交申请 → 多级审批 → 结果通知
  • 条件性审批路径

消息处理(消息总线):

  • 消息接收 → 内容解析 → 业务处理 → 结果分发
  • 基于消息类型的路由处理

流程编排最佳实践

可以通过go template + go expr + DAG实现整体流程编排。
1. 模板设计:

  • 保持模板简洁,复杂逻辑封装在Go函数中
  • 合理使用模板缓存提高性能
  • 统一错误处理机制

2. 表达式使用:

  • 表达式应尽量简单明确
  • 避免在表达式中进行复杂计算
  • 合理使用表达式缓存

3. DAG设计:

  • 避免过度复杂的节点依赖关系
  • 合理划分节点粒度
  • 考虑并行执行的可能性
  1. 性能优化:
  • 实施模板和表达式缓存
  • 监控执行性能
  • 优化节点执行顺序

参考文档:

  • https://pkg.go.dev/text/template
http://www.dtcms.com/a/391291.html

相关文章:

  • 辅助搜题系统-基于模糊搜索,上传word题库后,可搜索答案
  • 【完整源码+数据集+部署教程】遥感农田森林岩石图像分割系统: yolov8-seg-C2f-DCNV2
  • RTX 4090助力深度学习:从PyTorch到生产环境的完整实践指南
  • AWS中国云中的调用链监控(EC2版)
  • CI/CD到底是什么?
  • 3dmax三维动画渲染很慢怎么办?
  • ASIS CTF 2025 SatoNote
  • BasicForm的使用
  • CSP初赛——STL中的函数整理
  • 小杰机器学习高级(two)——极大似然估计、交叉熵损失函数
  • 关于px4 1.15.0电机控制有效矩阵的更新
  • 【设计模式】职责链模式
  • 22届考研(华为oD)-Java面经
  • 轻松实践:用Python实现“名字大作战”游戏,表白Zulu
  • EasyDSS视频直播点播平台如何为游戏直播提供超强技术底座?
  • MySQL----MVCC机制
  • 设计|str增量法|计算贡献
  • Spring中Controller层中容易搞混的注解
  • Git GitHub 个人账户创建教程
  • Python学习系统计划:从零到精通的科学路径
  • 解锁 JavaScript 的数学魔法:深入探索 Math 对象
  • dcm4che系列主要开源项目概述
  • 枚举深入解析
  • Qt中delete与deleteLater()的使用
  • AD5621(单通道缓冲电压输出DAC)芯片的详细用法
  • vLLM的面试题
  • 最优控制3 -- 动态规划-一个解析解的例子
  • 深入分析大众点评 Ajax 接口:直接请求 JSON 数据高效获取评论
  • 京东零售张科:DataAI Infra会成为驱动未来的技术基石
  • Java 运维中的一些常用命令