contrib

package module
v0.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 3, 2025 License: Apache-2.0 Imports: 35 Imported by: 0

README

contrib

golang pkg.go.dev Apache 2.0 license

Go 开发实用库

获取

go get -u bitbucket.org/yiigo/contrib

包含

  • array - 数组的常用方法
  • hashes - 封装便于使用
  • crypts - 封装便于使用(支持 AES & RSA)
  • validator - 支持汉化和自定义规则
  • sqls - 基于 sqlx 的轻量SQLBuilder
  • linklist - 一个并发安全的双向列表
  • errgroup - 基于官方版本改良,支持并发协程数量控制
  • values - 用于处理 k-v 格式化的场景,如:生成签名串等
  • coord - 距离、方位角、经纬度与平面直角坐标系的相互转化
  • timewheel - 简单实用的单层时间轮(支持一次性和多次重试任务)
  • images - 图片处理,如:缩略图、裁切、标注等
  • 基于 Redis 的分布式锁
  • 基于泛型的无限菜单分类层级树
  • 实用的辅助方法:IP、file、time、slice、string、version compare 等

⚠️ 注意:如需支持协程并发复用的 errgrouptimewheel,请使用 👉 nightfall

SQL Builder

⚠️ 目前支持的特性有限,复杂的SQL(如:子查询等)还需自己手写

builder := sqls.New(*sqlx.DB, func(ctx context.Context, query string, args ...any) {
    fmt.Println(query, args)
})
👉 Query
ctx := context.Background()

type User struct {
    ID     int    `db:"id"`
    Name   string `db:"name"`
    Age    int    `db:"age"`
    Phone  string `db:"phone,omitempty"`
}

var (
    record User
    records []User
)

builder.Wrap(
    sqls.Table("user"),
    sqls.Where("id = ?", 1),
).One(ctx, &record)
// SELECT * FROM user WHERE (id = ?)
// [1]

builder.Wrap(
    sqls.Table("user"),
    sqls.Where("name = ? AND age > ?", "yiigo", 20),
).All(ctx, &records)
// SELECT * FROM user WHERE (name = ? AND age > ?)
// [yiigo 20]

builder.Wrap(
    sqls.Table("user"),
    sqls.Where("name = ?", "yiigo"),
    sqls.Where("age > ?", 20),
).All(ctx, &records)
// SELECT * FROM user WHERE (name = ?) AND (age > ?)
// [yiigo 20]

builder.Wrap(
    sqls.Table("user"),
    sqls.WhereIn("age IN (?)", []int{20, 30}),
).All(ctx, &records)
// SELECT * FROM user WHERE (age IN (?, ?))
// [20 30]

builder.Wrap(
    sqls.Table("user"),
    sqls.Select("id", "name", "age"),
    sqls.Where("id = ?", 1),
).One(ctx, &record)
// SELECT id, name, age FROM user WHERE (id = ?)
// [1]

builder.Wrap(
    sqls.Table("user"),
    sqls.Distinct("name"),
    sqls.Where("id = ?", 1),
).One(ctx, &record)
// SELECT DISTINCT name FROM user WHERE (id = ?)
// [1]

builder.Wrap(
    sqls.Table("user"),
    sqls.LeftJoin("address", "user.id = address.user_id"),
    sqls.Where("user.id = ?", 1),
).One(ctx, &record)
// SELECT * FROM user LEFT JOIN address ON user.id = address.user_id WHERE (user.id = ?)
// [1]

builder.Wrap(
    sqls.Table("address"),
    sqls.Select("user_id", "COUNT(*) AS total"),
    sqls.GroupBy("user_id"),
    sqls.Having("user_id = ?", 1),
).All(ctx, &records)
// SELECT user_id, COUNT(*) AS total FROM address GROUP BY user_id HAVING (user_id = ?)
// [1]

builder.Wrap(
    sqls.Table("user"),
    sqls.Where("age > ?", 20),
    sqls.OrderBy("age ASC", "id DESC"),
    sqls.Offset(5),
    sqls.Limit(10),
).All(ctx, &records)
// SELECT * FROM user WHERE (age > ?) ORDER BY age ASC, id DESC LIMIT ? OFFSET ?
// [20, 10, 5]

wrap1 := builder.Wrap(
    sqls.Table("user_1"),
    sqls.Where("id = ?", 2),
)

builder.Wrap(
    sqls.Table("user_0"),
    sqls.Where("id = ?", 1),
    sqls.Union(wrap1),
).All(ctx, &records)
// (SELECT * FROM user_0 WHERE (id = ?)) UNION (SELECT * FROM user_1 WHERE (id = ?))
// [1, 2]

builder.Wrap(
    sqls.Table("user_0"),
    sqls.Where("id = ?", 1),
    sqls.UnionAll(wrap1),
).All(ctx, &records)
// (SELECT * FROM user_0 WHERE (id = ?)) UNION ALL (SELECT * FROM user_1 WHERE (id = ?))
// [1, 2]

builder.Wrap(
    sqls.Table("user_0"),
    sqls.WhereIn("age IN (?)", []int{10, 20}),
    sqls.Limit(5),
    sqls.Union(
        builder.Wrap(
            sqls.Table("user_1"),
            sqls.Where("age IN (?)", []int{30, 40}),
            sqls.Limit(5),
        ),
    ),
).All(ctx, &records)
// (SELECT * FROM user_0 WHERE (age IN (?, ?)) LIMIT ?) UNION (SELECT * FROM user_1 WHERE (age IN (?, ?)) LIMIT ?)
// [10, 20, 5, 30, 40, 5]
👉 Insert
ctx := context.Background()

type User struct {
    ID     int64  `db:"-"`
    Name   string `db:"name"`
    Age    int    `db:"age"`
    Phone  string `db:"phone,omitempty"`
}

builder.Wrap(Table("user")).Insert(ctx, &User{
    Name: "yiigo",
    Age:  29,
})
// INSERT INTO user (name, age) VALUES (?, ?)
// [yiigo 29]

builder.Wrap(sqls.Table("user")).Insert(ctx, map[string]any{
    "name": "yiigo",
    "age":  29,
})
// INSERT INTO user (name, age) VALUES (?, ?)
// [yiigo 29]
👉 Batch Insert
ctx := context.Background()

type User struct {
    ID     int64  `db:"-"`
    Name   string `db:"name"`
    Age    int    `db:"age"`
    Phone  string `db:"phone,omitempty"`
}

builder.Wrap(Table("user")).BatchInsert(ctx, []*User{
    {
        Name: "yiigo",
        Age:  20,
    },
    {
        Name: "yiigo",
        Age:  29,
    },
})
// INSERT INTO user (name, age) VALUES (?, ?), (?, ?)
// [yiigo 20 yiigo 29]

builder.Wrap(sqls.Table("user")).BatchInsert(ctx, []map[string]any{
    {
        "name": "yiigo",
        "age":  20,
    },
    {
        "name": "yiigo",
        "age":  29,
    },
})
// INSERT INTO user (name, age) VALUES (?, ?), (?, ?)
// [yiigo 20 yiigo 29]
👉 Update
ctx := context.Background()

type User struct {
    Name   string `db:"name"`
    Age    int    `db:"age"`
    Phone  string `db:"phone,omitempty"`
}

builder.Wrap(
    sqls.Table("user"),
    sqls.Where("id = ?", 1),
).Update(ctx, &User{
    Name: "yiigo",
    Age:  29,
})
// UPDATE user SET name = ?, age = ? WHERE (id = ?)
// [yiigo 29 1]

builder.Wrap(
    sqls.Table("user"),
    sqls.Where("id = ?", 1),
).Update(ctx, map[string]any{
    "name": "yiigo",
    "age":  29,
})
// UPDATE user SET name = ?, age = ? WHERE (id = ?)
// [yiigo 29 1]

builder.Wrap(
    sqls.Table("product"),
    sqls.Where("id = ?", 1),
).Update(ctx, map[string]any{
    "price": sqls.SQLExpr("price * ? + ?", 2, 100),
})
// UPDATE product SET price = price * ? + ? WHERE (id = ?)
// [2 100 1]
👉 Delete
ctx := context.Background()

builder.Wrap(
    sqls.Table("user"),
    sqls.Where("id = ?", 1),
).Delete(ctx)
// DELETE FROM user WHERE id = ?
// [1]

builder.Wrap(sqls.Table("user")).Truncate(ctx)
// TRUNCATE user
👉 Transaction
builder.Transaction(context.Background(), func(ctx context.Context, tx sqls.TXBuilder) error {
    _, err := tx.Wrap(
        sqls.Table("address"),
        sqls.Where("user_id = ?", 1),
    ).Update(ctx, map[string]any{"default": 0})
    if err != nil {
        return err
    }

    _, err = tx.Wrap(
        sqls.Table("address"),
        sqls.Where("id = ?", 1),
    ).Update(ctx, map[string]any{"default": 1})

    return err
})

Enjoy 😊

Documentation

Index

Constants

View Source
const (
	HeaderAccept        = "Accept"
	HeaderAuthorization = "Authorization"
	HeaderContentType   = "Content-Type"
)
View Source
const (
	ContentText          = "text/plain; charset=utf-8"
	ContentJSON          = "application/json"
	ContentXML           = "application/xml"
	ContentForm          = "application/x-www-form-urlencoded"
	ContentStream        = "application/octet-stream"
	ContentMultipartForm = "multipart/form-data"
)
View Source
const (
	// B - Byte size
	B Quantity = 1
	// KiB - KibiByte size
	KiB = 1024 * B
	// MiB - MebiByte size
	MiB = 1024 * KiB
	// GiB - GibiByte size
	GiB = 1024 * MiB
	// TiB - TebiByte size
	TiB = 1024 * GiB
)
View Source
const MaxFormMemory = 32 << 20

Variables

View Source
var GMT8 = time.FixedZone("CST", 8*3600)

GMT8 东八区时区

View Source
var RestyClient = resty.NewWithClient(NewHttpClient())

RestyClient default client for http request

Functions

func AddSlashes

func AddSlashes(s string) string

AddSlashes 在字符串的每个引号前添加反斜杠

func BindForm

func BindForm(r *http.Request, obj any) error

BindForm 解析Form表单并校验

func BindJSON

func BindJSON(r *http.Request, obj any) error

BindJSON 解析JSON请求体并校验

func BindProto

func BindProto(r *http.Request, msg proto.Message) error

BindProto 解析Proto请求体并校验

func ContentType

func ContentType(h http.Header) string

func CreateFile

func CreateFile(filename string) (*os.File, error)

CreateFile 创建或清空指定的文件 文件已存在,则清空;文件或目录不存在,则以0775权限创建

func DebugLogger

func DebugLogger(options ...zap.Option) *zap.Logger

func ExcelColumnIndex

func ExcelColumnIndex(name string) int

ExcelColumnIndex 返回Excel列名对应的序号,如:A=0,B=1,AA=26,AB=27

func IP2Long

func IP2Long(ip string) uint32

IP2Long IP地址转整数

func IsUniqueDuplicateError

func IsUniqueDuplicateError(err error) bool

IsUniqueDuplicateError 判断是否「唯一索引冲突」错误

func Long2IP

func Long2IP(ip uint32) string

Long2IP 整数转IP地址

func MapForm

func MapForm(ptr any, form map[string][]string) error

func MapFormByTag

func MapFormByTag(ptr any, form map[string][]string, tag string) error

func MapQuery

func MapQuery(ptr any, m map[string][]string) error

func MappingByPtr

func MappingByPtr(ptr any, setter setter, tag string) error

func MarshalNoEscapeHTML

func MarshalNoEscapeHTML(v any) ([]byte, error)

MarshalNoEscapeHTML 不带HTML转义的JSON序列化

func MyTimeEncoder

func MyTimeEncoder(t time.Time, e zapcore.PrimitiveArrayEncoder)

MyTimeEncoder 自定义时间格式化

func NewDB

func NewDB(cfg *DBConfig) (*sql.DB, error)

NewDB sql.DB

func NewDBx

func NewDBx(cfg *DBConfig) (*sqlx.DB, error)

NewDBx sqlx.DB

func NewHttpClient

func NewHttpClient() *http.Client

NewHttpClient returns a http client

func NewLogger

func NewLogger(cfg *LogConfig) *zap.Logger

func Nonce

func Nonce(size uint8) string

Nonce 生成随机串(size应为偶数)

func OpenFile

func OpenFile(filename string) (*os.File, error)

OpenFile 打开指定的文件 文件已存在,则追加内容;文件或目录不存在,则以0775权限创建

func QuoteMeta

func QuoteMeta(s string) string

QuoteMeta 在字符串预定义的字符前添加反斜杠

func StrToTime

func StrToTime(layout, datetime string, loc *time.Location) time.Time

StrToTime 时间字符串解析为时间戳

func StripSlashes

func StripSlashes(s string) string

StripSlashes 删除字符串中的反斜杠

func TimeToStr

func TimeToStr(layout string, timestamp int64, loc *time.Location) string

TimeToStr 时间戳格式化为时间字符串 若 timestamp < 0,则使用 `time.Now()`

func Transaction

func Transaction(ctx context.Context, db *sqlx.DB, fn func(ctx context.Context, tx *sqlx.Tx) error) (err error)

Transaction 执行数据库事物

func VersionCompare

func VersionCompare(rangeVer, curVer string) (bool, error)

VersionCompare 语义化的版本比较,支持:>, >=, =, !=, <, <=, | (or), & (and). 参数 `rangeVer` 示例:1.0.0, =1.0.0, >2.0.0, >=1.0.0&<2.0.0, <2.0.0|>3.0.0, !=4.0.4

func WeekAround

func WeekAround(layout string, now time.Time) (monday, sunday string)

WeekAround 返回给定时间戳所在周的「周一」和「周日」时间字符串

Types

type DBConfig

type DBConfig struct {
	// Driver 驱动名称
	Driver string
	// DSN 数据源名称
	//
	//  [-- MySQL] username:password@tcp(localhost:3306)/dbname?timeout=10s&charset=utf8mb4&collation=utf8mb4_general_ci&parseTime=True&loc=Local
	//  [Postgres] host=localhost port=5432 user=root password=secret dbname=test search_path=schema connect_timeout=10 sslmode=disable
	//  [- SQLite] file::memory:?cache=shared
	DSN string
	// MaxOpenConns 设置最大可打开的连接数
	MaxOpenConns int
	// MaxIdleConns 连接池最大闲置连接数
	MaxIdleConns int
	// ConnMaxLifetime 连接的最大生命时长
	ConnMaxLifetime time.Duration
	// ConnMaxIdleTime 连接最大闲置时间
	ConnMaxIdleTime time.Duration
}

DBConfig 数据库初始化配置

type LogConfig

type LogConfig struct {
	// Filename 日志名称
	Filename string
	// Level 日志级别
	Level zapcore.Level
	// MaxSize 当前文件多大时轮替;默认:100MB
	MaxSize int
	// MaxAge 轮替的旧文件最大保留时长;默认:不限
	MaxAge int
	// MaxBackups 轮替的旧文件最大保留数量;默认:不限
	MaxBackups int
	// Compress 轮替的旧文件是否压缩;默认:不压缩
	Compress bool
	// Stderr 是否输出到控制台
	Stderr bool
	// Options Zap日志选项
	Options []zap.Option
}

LogConfig 日志初始化配置

type Mutex

type Mutex interface {
	// Lock 获取锁
	Lock(ctx context.Context) (bool, error)
	// TryLock 尝试获取锁
	TryLock(ctx context.Context, attempts int, interval time.Duration) (bool, error)
	// UnLock 释放锁
	UnLock(ctx context.Context) error
}

Mutex 分布式锁

func RedLock

func RedLock(cli *redis.Client, key string, ttl time.Duration) Mutex

RedLock 基于Redis实现的分布式锁实例

type Quantity

type Quantity int64

Quantity 字节大小

func (Quantity) String

func (q Quantity) String() string

String 实现 Stringer 接口

type X

type X map[string]any

X 类型别名

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL