All skills
gogf avatar

/goframe-v2

@911705d
by GoFramegogf/skills81 stars
3

GoFrame v2 development skill. Use only when the target Go project uses or is explicitly adopting GoFrame v2: the nearest go.mod requires github.com/gogf/gf/v2, existing Go files import github.com/gogf/gf/v2 or any github.com/gogf/gf/v2/... component package, or the user asks to scaffold, migrate, or build with GoFrame. Trigger for GoFrame-backed Go work such as APIs/controllers/services, middleware, routing/config, ORM/DAO/DO/entity/database operations, gf CLI/codegen, HTTP/gRPC services, and microservice conventions. Do not trigger for generic Go projects without GoFrame evidence, frontend-only work, shell scripts, or unrelated infrastructure tasks.

Use this Skill: https://skilld.dev/gh/gogf/skills/goframe-v2

This session only. Nothing lands on disk.

references组件列表数据结构字典类型-gmap字典类型-基本使用.md

泛型映射(推荐使用)

从 v2.10 版本开始,gmap 提供了泛型映射 KVMap[K, V]、ListKVMap[K, V] 和 TreeKVMap[K, V],提供类型安全的映射操作。开发者可以使用 NewKVMap[string, int]() 等方式创建指定类型的映射,避免类型断言和运行时错误。

泛型哈希映射 KVMap

package main

import (
    "fmt"
    "github.com/gogf/gf/v2/container/gmap"
)

func main() {
    // 创建一个键为string,值为int的泛型哈希映射
    m := gmap.NewKVMap[string, int]()
    
    // 设置键值对
    m.Set("apple", 1)
    m.Set("banana", 2)
    m.Set("orange", 3)
    
    // 获取值
    if value, ok := m.Get("apple"); ok {
        fmt.Println("apple:", value) // 输出: apple: 1
    }
    
    // 获取所有键值
    fmt.Println("Keys:", m.Keys())
    fmt.Println("Values:", m.Values())
    
    // 遍历
    m.Iterator(func(k string, v int) bool {
        fmt.Printf("%s: %d\n", k, v)
        return true
    })
}

泛型有序映射 ListKVMap

ListKVMap 是按照插入顺序维护键值对的有序映射。

package main

import (
    "fmt"
    "github.com/gogf/gf/v2/container/gmap"
)

func main() {
    // 创建有序映射
    m := gmap.NewListKVMap[string, int]()
    
    // 按顺序插入
    m.Set("third", 3)
    m.Set("first", 1)
    m.Set("second", 2)
    
    // 遍历会按插入顺序输出
    m.Iterator(func(k string, v int) bool {
        fmt.Printf("%s: %d\n", k, v)
        return true
    })
    
    // Output:
    // third: 3
    // first: 1
    // second: 2
}

泛型树形映射 TreeKVMap

TreeKVMap 使用红黑树实现,键值对按照比较函数定义的顺序排序。

package main

import (
    "fmt"
    "github.com/gogf/gf/v2/container/gmap"
    "github.com/gogf/gf/v2/util/gutil"
)

func main() {
    // 创建树形映射,使用字符串比较函数
    m := gmap.NewTreeKVMap[string, int](gutil.ComparatorString)
    
    // 插入数据
    m.Set("banana", 2)
    m.Set("apple", 1)
    m.Set("orange", 3)
    
    // 遍历会按照键的字母序输出
    m.Iterator(func(k string, v int) bool {
        fmt.Printf("%s: %d\n", k, v)
        return true
    })
    
    // Output:
    // apple: 1
    // banana: 2
    // orange: 3
}

自定义 nil 值检查器

:::tip NilChecker 是一个可选参数,并非必须提供。它主要用于解决typed nil问题,并提供更好的nil判断性能。在默认情况下,组件使用反射来判断数据是否为nil。 :::

对于指针类型、接口类型等,可能需要自定义 nil 判断逻辑。NewKVMapWithChecker 系列函数支持自定义 nil 检查器。

package main

import (
    "fmt"
    "github.com/gogf/gf/v2/container/gmap"
)

type User struct {
    ID   int
    Name string
}

func main() {
    // 为指针类型创建映射,使用自定义nil检查器
    m := gmap.NewKVMapWithChecker[string, *User](func(u *User) bool {
        return u == nil || u.ID == 0
    })
    
    m.Set("user1", &User{ID: 1, Name: "John"})
    m.Set("user2", &User{ID: 0, Name: "Invalid"}) // 会被视为nil
    
    // GetOrSet在值为nil时会设置新值
    user := m.GetOrSet("user2", &User{ID: 2, Name: "Jane"})
    fmt.Printf("user2: %+v\n", user)
    
    // Output:
    // user2: &{ID:2 Name:Jane}
}

泛型映射特点

  1. 类型安全:在编译期就能发现类型错误,避免运行时类型断言失败。
  2. 自定义 nil 检查(可选):通过 NewXXXWithChecker 系列函数,可以为泛型类型自定义 nil 判断逻辑,解决类型化 nil 的问题。默认情况下使用反射判断。
  3. 性能优势:减少了类型断言和反射的开销。
  4. 更好的IDE支持:IDE能够提供更准确的代码补全和类型提示。

:::tip 推荐在新项目中使用泛型映射 KVMap[K, V]、ListKVMap[K, V] 和 TreeKVMap[K, V],它们提供了更好的类型安全性和开发体验。 :::

传统映射类型

下面介绍传统的映射类型,这些类型在旧版本中被广泛使用,仍然完全支持。

并发安全

gmap 支持并发安全选项开关,在默认情况下是 非并发安全 的,开发者可以选择开启 gmap 的并发安全特性(传递初始化开关参数 safe 参数值为 true, 必须在初始化时设定,不能运行时动态设定)。如:

m := gmap.New(true)

不仅仅是 gmap 模块, goframe 框架的其他并发安全数据结构也支持并发安全特性开关。

基本使用

package main

import (
    "fmt"
    "github.com/gogf/gf/v2/container/gmap"
)

func main() {
    // 创建一个默认的gmap对象,
    // 默认情况下该gmap对象不支持并发安全特性,
    // 初始化时可以给定true参数开启并发安全特性。
    m := gmap.New()

    // 设置键值对
    for i := 0; i < 10; i++ {
        m.Set(i, i)
    }
    // 查询大小
    fmt.Println(m.Size())
    // 批量设置键值对(不同的数据类型对象参数不同)
    m.Sets(map[interface{}]interface{}{
        10 : 10,
        11 : 11,
    })
    fmt.Println(m.Size())

    // 查询是否存在
    fmt.Println(m.Contains(1))

    // 查询键值
    fmt.Println(m.Get(1))

    // 删除数据项
    m.Remove(9)
    fmt.Println(m.Size())

    // 批量删除
    m.Removes([]interface{}{10, 11})
    fmt.Println(m.Size())

    // 当前键名列表(随机排序)
    fmt.Println(m.Keys())
    // 当前键值列表(随机排序)
    fmt.Println(m.Values())

    // 查询键名,当键值不存在时,写入给定的默认值
    fmt.Println(m.GetOrSet(100, 100))

    // 删除键值对,并返回对应的键值
    fmt.Println(m.Remove(100))

    // 遍历map
    m.Iterator(func(k interface{}, v interface{}) bool {
        fmt.Printf("%v:%v ", k, v)
        return true
    })

    // 自定义写锁操作
    m.LockFunc(func(m map[interface{}]interface{}) {
        m[99] = 99
    })

    // 自定义读锁操作
    m.RLockFunc(func(m map[interface{}]interface{}) {
        fmt.Println(m[99])
    })

    // 清空map
    m.Clear()

    // 判断map是否为空
    fmt.Println(m.IsEmpty())
}

执行后,输出结果为:

12
true
1
11
9
[0 1 2 4 6 7 3 5 8]
[3 5 8 0 1 2 4 6 7]
100
100
3:3 5:5 8:8 7:7 0:0 1:1 2:2 4:4 6:6 99
true

有序遍历

我们来看一下三种不同类型 map 的有序性遍历示例。

package main

import (
    "fmt"
    "github.com/gogf/gf/v2/frame/g"
    "github.com/gogf/gf/v2/container/gmap"
    "github.com/gogf/gf/v2/util/gutil"
)

func main() {
    array   := g.Slice{2, 3, 1, 5, 4, 6, 8, 7, 9}
    hashMap := gmap.New(true)
    listMap := gmap.NewListMap(true)
    treeMap := gmap.NewTreeMap(gutil.ComparatorInt, true)
    for _, v := range array {
        hashMap.Set(v, v)
    }
    for _, v := range array {
        listMap.Set(v, v)
    }
    for _, v := range array {
        treeMap.Set(v, v)
    }
    fmt.Println("HashMap   Keys:", hashMap.Keys())
    fmt.Println("HashMap Values:", hashMap.Values())
    fmt.Println("ListMap   Keys:", listMap.Keys())
    fmt.Println("ListMap Values:", listMap.Values())
    fmt.Println("TreeMap   Keys:", treeMap.Keys())
    fmt.Println("TreeMap Values:", treeMap.Values())
}

执行后,输出结果为:

HashMap   Keys: [4 6 8 7 9 2 3 1 5]
HashMap Values: [6 8 4 3 1 5 7 9 2]
ListMap   Keys: [2 3 1 5 4 6 8 7 9]
ListMap Values: [2 3 1 5 4 6 8 7 9]
TreeMap   Keys: [1 2 3 4 5 6 7 8 9]
TreeMap Values: [1 2 3 4 5 6 7 8 9]

FilterEmpty/FilterNil 空值过滤

package main

import (
    "fmt"
    "github.com/gogf/gf/v2/container/gmap"
    "github.com/gogf/gf/v2/frame/g"
)

func main() {
    m1 := gmap.NewFrom(g.MapAnyAny{
        "k1": "",
        "k2": nil,
        "k3": 0,
        "k4": 1,
    })
    m2 := gmap.NewFrom(g.MapAnyAny{
        "k1": "",
        "k2": nil,
        "k3": 0,
        "k4": 1,
    })
    m1.FilterEmpty()
    m2.FilterNil()
    fmt.Println(m1.Map())
    fmt.Println(m2.Map())

    // Output:
    // map[k4:1]
    // map[k1: k3:0 k4:1]
}

Flip 键值对反转

package main

import (
    "fmt"
    "github.com/gogf/gf/v2/container/gmap"
    "github.com/gogf/gf/v2/frame/g"
)

func main() {
    var m gmap.Map
    m.Sets(g.MapAnyAny{
        "k1": "v1",
        "k2": "v2",
    })
    m.Flip()
    fmt.Println(m.Map())

    // May Output:
    // map[v1:k1 v2:k2]
}

Keys/Values 键名/数值列表

package main

import (
    "fmt"
    "github.com/gogf/gf/v2/container/gmap"
    "github.com/gogf/gf/v2/frame/g"
)

func main() {
    var m gmap.Map
    m.Sets(g.MapAnyAny{
        "k1": "v1",
        "k2": "v2",
        "k3": "v3",
        "k4": "v4",
    })
    fmt.Println(m.Keys())
    fmt.Println(m.Values())

    // May Output:
    // [k1 k2 k3 k4]
    // [v2 v3 v4 v1]
}

Pop/Pops 随机出栈

package main

import (
    "fmt"
    "github.com/gogf/gf/v2/container/gmap"
    "github.com/gogf/gf/v2/frame/g"
)

func main() {
    var m gmap.Map
    m.Sets(g.MapAnyAny{
        "k1": "v1",
        "k2": "v2",
        "k3": "v3",
        "k4": "v4",
    })
    fmt.Println(m.Pop())
    fmt.Println(m.Pops(2))
    fmt.Println(m.Size())

    // May Output:
    // k1 v1
    // map[k2:v2 k4:v4]
    // 1
}

SetIfNotExist* 判断性写入

判断性写入是指当指定的键名不存在时则写入并且方法返回 true,否则忽略写入并且方法返回 false。相关方法如下:

  • SetIfNotExist
  • SetIfNotExistFunc
  • SetIfNotExistFuncLock

方法具体描述请查看接口文档或源码注释。

package main

import (
    "fmt"
    "github.com/gogf/gf/v2/container/gmap"
)

func main() {
    var m gmap.Map
    fmt.Println(m.SetIfNotExist("k1", "v1"))
    fmt.Println(m.SetIfNotExist("k1", "v1"))
    fmt.Println(m.Map())

    // Output:
    // true
    // false
    // map[k1:v1]
}

Merge 字典表合并

package main

import (
    "fmt"
    "github.com/gogf/gf/v2/container/gmap"
)

func main() {
    var m1, m2 gmap.Map
    m1.Set("key1", "val1")
    m2.Set("key2", "val2")
    m1.Merge(&m2)
    fmt.Println(m1.Map())

    // May Output:
    // map[key1:val1 key2:val2]
}

JSON 序列化/反序列

gmap 模块下的所有容器类型均实现了标准库 json 数据格式的序列化/反序列化接口。

1. Marshal

package main

import (
    "encoding/json"
    "fmt"
    "github.com/gogf/gf/v2/frame/g"

    "github.com/gogf/gf/v2/container/gmap"
)

func main() {
    m := gmap.New()
    m.Sets(g.MapAnyAny{
        "name":  "john",
        "score": 100,
    })
    b, _ := json.Marshal(m)
    fmt.Println(string(b))
}

执行后,输出结果:

{"name":"john","score":100}
  1. Unmarshal
package main

import (
    "encoding/json"
    "fmt"
    "github.com/gogf/gf/v2/container/gmap"
)

func main() {
    m := gmap.Map{}
    s := []byte(`{"name":"john","score":100}`)
    json.Unmarshal(s, &m)
    fmt.Println(m.Map())
}

执行后,输出结果:

map[name:john score:100]

Source: SKILL.md on GitHub

1 alert16d5 checks · Risk MEDIUM
  • Gen Agent Trust Hub16d

    The skill provides a comprehensive development guide and code examples for the GoFrame v2 framework. While highly informative for Go developers, it contains several examples with hardcoded placeholder secrets, tokens, and specific local file paths to sensitive configuration files (like Kubernetes credentials). These elements are provided for demonstration purposes but represent insecure practices if used in production without modification.

  • Socket16d

    5 alerts: gptSecurity, gptAnomaly

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer6mo

    227/949 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 911705d. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 4 months ago.

Steadyupdated 4 months ago
  • Go
  • Database
  • goframe
  • orm
  • http
  • grpc
  • microservices
  • crud
  • scaffolding
  • middleware

README badge

README badge for gogf/skills

Provides instruction and conventions for developing with GoFrame v2, a modular Go framework for building HTTP and gRPC services, microservices, and database operations using its ORM, CLI tooling, and project scaffolding. Use this skill when the target project has GoFrame v2 as a dependency or when the user requests scaffolding, migrations, or service development with GoFrame conventions.

Generated from the current SKILL.md.

When should I use this skill?
Use this skill only when your Go project uses GoFrame v2, indicated by github.com/gogf/gf/v2 in go.mod or imports. Do not use for generic Go projects, frontend work, or infrastructure tasks unrelated to GoFrame.
Should I manually set created_at and updated_at fields?
No. GoFrame automatically writes created_at on insert and updated_at on insert/update/save. Manually setting these fields is redundant and violates project conventions.
What should I use for database operations instead of g.Map?
Always use DO objects from internal/model/do/. DO struct fields are interface{}, unset fields remain nil and are automatically ignored by the ORM.
How does soft delete work in GoFrame?
Call Delete() on the DAO; GoFrame automatically converts it to UPDATE SET deleted_at = NOW(). Queries automatically filter out soft-deleted rows without requiring manual WhereNull conditions.
Where should I implement business logic?
Implement business logic directly in the service/ directory. Do not use the logic/ directory unless explicitly requested.

Generated from the current SKILL.md. These answers refresh after source changes.