chore: 并入 iotlcm-openapi 历史到 openapi/(原提交 3b05d5a)

This commit is contained in:
MiFaZhan
2026-11-02 13:28:02 +08:00
484 changed files with 34732 additions and 0 deletions
+92
View File
@@ -0,0 +1,92 @@
// package convert
// @Link https://github.com/xiujiecn/xiu-admin
// @Copyright Copyright (c) 2025 LiXiujie
// @Author Lxj <xxx@example.com>
// @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
package convert
import (
"reflect"
"slices"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/text/gstr"
)
var (
descTags = []string{"description", "dc", "json"} // 实体描述标签
fieldTags = []string{"json"} // 实体字段名称映射
)
// EscapeFieldsToSlice 将转义过的字段转换为字段集切片
func EscapeFieldsToSlice(s string) []string {
return gstr.Explode(",", gstr.Replace(gstr.Replace(s, "`,`", ","), "`", ""))
}
// GetEntityDescTags 获取实体中的描述标签
func GetEntityDescTags(entity interface{}) (tags []string, err error) {
var formRef = reflect.TypeOf(entity)
for i := 0; i < formRef.NumField(); i++ {
field := formRef.Field(i)
if field.Type.Kind() == reflect.Struct {
addTags, err := reflectTag(field.Type, descTags, []string{})
if err != nil {
return nil, err
}
tags = append(tags, addTags...)
continue
}
tags = append(tags, reflectTagName(field, descTags, true))
}
return
}
// reflectTag 层级递增解析tag
func reflectTag(reflectType reflect.Type, filterTags []string, tags []string) ([]string, error) {
if reflectType.Kind() == reflect.Ptr {
return nil, gerror.Newf("reflect type do not support reflect.Ptr yet, reflectType:%+v", reflectType)
}
if reflectType.Kind() != reflect.Struct {
return nil, nil
}
for i := 0; i < reflectType.NumField(); i++ {
tag := reflectTagName(reflectType.Field(i), filterTags, false)
if tag == "" {
addTags, err := reflectTag(reflectType.Field(i).Type, filterTags, tags)
if err != nil {
return nil, err
}
tags = append(tags, addTags...)
continue
}
tags = append(tags, tag)
}
return tags, nil
}
// reflectTagName 解析实体中的描述标签,优先级:description > dc > json > Name
func reflectTagName(field reflect.StructField, filterTags []string, isDef bool) string {
if slices.Contains(filterTags, "description") {
if description, ok := field.Tag.Lookup("description"); ok && description != "" {
return description
}
}
if slices.Contains(filterTags, "dc") {
if dc, ok := field.Tag.Lookup("dc"); ok && dc != "" {
return dc
}
}
if slices.Contains(filterTags, "json") {
if jsonName, ok := field.Tag.Lookup("json"); ok && jsonName != "" {
return jsonName
}
}
if !isDef {
return ""
}
return field.Name
}
+138
View File
@@ -0,0 +1,138 @@
// package excel
// @Link https://github.com/xiujiecn/xiu-admin
// @Copyright Copyright (c) 2025 LiXiujie
// @Author Lxj <xxx@example.com>
// @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
package excel
import (
"context"
"fmt"
"net/http"
"net/url"
"reflect"
"unicode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/util/gconv"
excelize "github.com/xuri/excelize/v2"
)
var (
// 单元格表头
char = []string{"", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}
// 默认行样式
// defaultRowStyle = `{"font":{"color":"#666666","size":13,"family":"arial"},"alignment":{"vertical":"center","horizontal":"center"}}`
defaultRowStyle = &excelize.Style{
Font: &excelize.Font{
Color: "#666666",
Size: 13,
Family: "arial",
},
Alignment: &excelize.Alignment{
Vertical: "center",
Horizontal: "center",
},
}
)
// ExportByStructs 导出切片结构体到excel表格
func ExportByStructs(ctx context.Context, tags []string, list interface{}, fileName string, sheetName string) (err error) {
f := excelize.NewFile()
f.SetSheetName("Sheet1", sheetName)
_ = f.SetRowHeight("Sheet1", 1, 30)
rowStyleID, err := f.NewStyle(defaultRowStyle)
if err != nil {
return
}
_ = f.SetSheetRow(sheetName, "A1", &tags)
var (
length = len(tags)
headStyle = letter(length)
lastRow string
widthRow string
)
for k, v := range headStyle {
if k == length-1 {
lastRow = fmt.Sprintf("%s1", v)
widthRow = v
}
}
if err = f.SetColWidth(sheetName, "A", widthRow, 30); err != nil {
return err
}
var rowNum = 1
for _, v := range gconv.Interfaces(list) {
t := reflect.TypeOf(v)
value := reflect.ValueOf(v)
row := make([]interface{}, 0)
for l := 0; l < t.NumField(); l++ {
val := value.Field(l).Interface()
row = append(row, val)
}
rowNum++
if err = f.SetSheetRow(sheetName, "A"+gconv.String(rowNum), &row); err != nil {
return
}
if err = f.SetCellStyle(sheetName, fmt.Sprintf("A%d", rowNum), lastRow, rowStyleID); err != nil {
return
}
}
r := ghttp.RequestFromCtx(ctx)
if r == nil {
err = gerror.New("ctx not http request")
return
}
writer := r.Response.Writer
writer.Header().Set("Content-Type", "application/octet-stream")
writer.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s.xlsx", url.QueryEscape(fileName)))
writer.Header().Set("Content-Transfer-Encoding", "binary")
writer.Header().Set("Access-Control-Expose-Headers", "Content-Disposition")
if err = f.Write(writer); err != nil {
return
}
r.Response.Status = http.StatusOK
f.Close()
writer.Flush()
return
}
// letter 生成完整的表头
func letter(length int) []string {
var str []string
for i := 1; i <= length; i++ {
str = append(str, numToChars(i))
}
return str
}
// numToChars 将数字转换为具体的表格表头名称
func numToChars(num int) string {
var cols string
v := num
for v > 0 {
k := v % 26
if k == 0 {
k = 26
}
v = (v - k) / 26
cols = char[k] + cols
}
return cols
}
// NextLetter 传入一个字母,获取下一个字母
func NextLetter(input string) string {
if len(input) == 0 {
return ""
}
upperInput := unicode.ToUpper(rune(input[0]))
if upperInput >= 'A' && upperInput < 'Z' {
return string(upperInput + 1)
}
return "A"
}
+11
View File
@@ -0,0 +1,11 @@
package gfredis
import (
"context"
"github.com/redis/go-redis/v9"
)
func (r *Redis) Conn(ctx context.Context) (*redis.Conn, error) {
return r.rdb.Conn(), nil
}
+150
View File
@@ -0,0 +1,150 @@
package gfredis
import (
"context"
"crypto/tls"
"sync"
"time"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
"github.com/redis/go-redis/v9"
)
const (
DefaultGroupName = "default" // Default configuration group name.
)
var (
lock sync.RWMutex
instances map[string]*Redis
)
func init() {
instances = make(map[string]*Redis)
}
type Redis struct {
config *Config
rdb *redis.Client
}
// Config is redis configuration.
type Config struct {
// Address It supports single and cluster redis server. Multiple addresses joined with char ','. Eg: 127.0.0.1:6379, 127.0.0.1:6379.
Address string `json:"address"`
Db int `json:"db"` // Redis db.
User string `json:"user"` // Username for AUTH.
Pass string `json:"pass"` // Password for AUTH.
SentinelUser string `json:"sentinel_user"` // Username for sentinel AUTH.
SentinelPass string `json:"sentinel_pass"` // Password for sentinel AUTH.
MinIdle int `json:"minIdle"` // Minimum number of connections allowed to be idle (default is 0)
MaxIdle int `json:"maxIdle"` // Maximum number of connections allowed to be idle (default is 10)
MaxActive int `json:"maxActive"` // Maximum number of connections limit (default is 0 means no limit).
MaxConnLifetime time.Duration `json:"maxConnLifetime"` // Maximum lifetime of the connection (default is 30 seconds, not allowed to be set to 0)
IdleTimeout time.Duration `json:"idleTimeout"` // Maximum idle time for connection (default is 10 seconds, not allowed to be set to 0)
WaitTimeout time.Duration `json:"waitTimeout"` // Timed out duration waiting to get a connection from the connection pool.
DialTimeout time.Duration `json:"dialTimeout"` // Dial connection timeout for TCP.
ReadTimeout time.Duration `json:"readTimeout"` // Read timeout for TCP. DO NOT set it if not necessary.
WriteTimeout time.Duration `json:"writeTimeout"` // Write timeout for TCP.
MasterName string `json:"masterName"` // Used in Redis Sentinel mode.
TLS bool `json:"tls"` // Specifies whether TLS should be used when connecting to the server.
TLSSkipVerify bool `json:"tlsSkipVerify"` // Disables server name verification when connecting over TLS.
TLSConfig *tls.Config `json:"-"` // TLS Config to use. When set TLS will be negotiated.
SlaveOnly bool `json:"slaveOnly"` // Route all commands to slave read-only nodes.
Cluster bool `json:"cluster"` // Specifies whether cluster mode be used.
Protocol int `json:"protocol"` // Specifies the RESP version (Protocol 2 or 3.)
}
func New(groupName string, config *Config) *Redis {
instance := &Redis{
config: config,
}
if config == nil {
instance.rdb = redis.NewClient(&redis.Options{})
return instance
}
poolSize := config.MaxActive
if poolSize == 0 {
poolSize = 10
config.MaxActive = 10
}
if config.IdleTimeout < 1*time.Minute {
config.IdleTimeout = 30 * time.Minute
}
if config.MaxConnLifetime <= 1 {
config.MaxConnLifetime = 24 * 365 * 10 * time.Hour
}
if config.WaitTimeout < 1*time.Second {
config.WaitTimeout = 6 * time.Second
}
if config.DialTimeout < 1*time.Second {
config.DialTimeout = 5 * time.Second
}
if config.ReadTimeout < 1*time.Second {
config.ReadTimeout = 5 * time.Second
}
if config.WriteTimeout < 1*time.Second {
config.WriteTimeout = 5 * time.Second
}
opt := &redis.Options{
Addr: config.Address,
Password: config.Pass,
DB: config.Db,
MinIdleConns: config.MinIdle,
MaxIdleConns: config.MaxIdle,
MaxActiveConns: config.MaxActive,
ConnMaxIdleTime: config.IdleTimeout,
PoolSize: poolSize,
PoolTimeout: config.WaitTimeout,
DialTimeout: config.DialTimeout,
ReadTimeout: config.ReadTimeout,
WriteTimeout: config.WriteTimeout,
TLSConfig: config.TLSConfig,
// IdentitySuffix: config.IdentitySuffix,
// ConnMaxLifetime: config.MaxConnLifetime,
}
instance.rdb = redis.NewClient(opt)
g.Log().Noticef(context.Background(), "groupName: %s, opt: %+v", groupName, opt)
return instance
}
func GetConfig(name string) *Config {
cfg, err := g.Cfg().Get(context.Background(), "redis."+name)
if err != nil {
panic(err)
}
if cfg == nil || cfg.IsNil() {
panic("redis config not found: " + name)
}
config := &Config{}
if err = gconv.Scan(cfg.Map(), config); err != nil {
panic(`invalid redis configuration: ` + cfg.String())
}
return config
}
func Instance(name ...string) *Redis {
groupName := DefaultGroupName
if len(name) > 0 {
groupName = name[0]
if groupName == "" {
groupName = DefaultGroupName
}
}
lock.RLock()
instance, ok := instances[groupName]
lock.RUnlock()
if !ok {
lock.Lock()
instance, ok = instances[groupName]
if !ok {
instance = New(groupName, GetConfig(groupName))
instances[groupName] = instance
}
lock.Unlock()
return instance
}
return instance
}
+51
View File
@@ -0,0 +1,51 @@
package gfredis
import (
"context"
"github.com/gogf/gf/v2/util/gconv"
"github.com/redis/go-redis/v9"
)
func (r *Redis) HMSet(ctx context.Context, key string, mp map[string]interface{}) error {
mp2 := make(map[string]string)
for k, v := range mp {
mp2[k] = gconv.String(v)
}
cmd := r.rdb.HMSet(ctx, key, mp2)
return cmd.Err()
}
func (r *Redis) HGetAll(ctx context.Context, key string) (map[string]string, error) {
cmd := r.rdb.HGetAll(ctx, key)
return cmd.Val(), cmd.Err()
}
func (r *Redis) HGet(ctx context.Context, key, field string) (string, error) {
cmd := r.rdb.HGet(ctx, key, field)
if cmd.Err() == redis.Nil {
return "", nil
}
return cmd.Val(), cmd.Err()
}
func (r *Redis) HSet(ctx context.Context, key string, mp map[string]interface{}) error {
mp2 := make(map[string]string)
for k, v := range mp {
mp2[k] = gconv.String(v)
}
cmd := r.rdb.HSet(ctx, key, mp2)
return cmd.Err()
}
func (r *Redis) HSetNX(ctx context.Context, key, field string, value interface{}) (bool, error) {
cmd := r.rdb.HSetNX(ctx, key, field, gconv.String(value))
return cmd.Val(), cmd.Err()
}
func (r *Redis) HDel(ctx context.Context, key string, field string) (int64, error) {
cmd := r.rdb.HDel(ctx, key, field)
return cmd.Val(), cmd.Err()
}
func (r *Redis) HLen(ctx context.Context, key string) (int64, error) {
cmd := r.rdb.HLen(ctx, key)
return cmd.Val(), cmd.Err()
}
+16
View File
@@ -0,0 +1,16 @@
package gfredis
import (
"context"
"github.com/gogf/gf/v2/frame/g"
"github.com/redis/go-redis/v9"
)
func (r *Redis) Do(ctx context.Context, args ...interface{}) (*g.Var, error) {
cmd := r.rdb.Do(ctx, args...)
if cmd.Err() == redis.Nil {
return g.NewVar(nil), nil
}
return g.NewVar(cmd.Val()), cmd.Err()
}
+37
View File
@@ -0,0 +1,37 @@
package gfredis
import (
"context"
"time"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
"github.com/redis/go-redis/v9"
)
func (r *Redis) LRange(ctx context.Context, key string, start, stop int64) (*g.Var, error) {
cmd := r.rdb.LRange(ctx, key, start, stop)
if cmd.Err() == redis.Nil {
return g.NewVar(nil), nil
}
return g.NewVar(cmd.Val()), cmd.Err()
}
func (r *Redis) RPush(ctx context.Context, key string, value interface{}) (int64, error) {
cmd := r.rdb.RPush(ctx, key, gconv.String(value))
return cmd.Val(), cmd.Err()
}
func (r *Redis) LLen(ctx context.Context, key string) (int64, error) {
cmd := r.rdb.LLen(ctx, key)
return cmd.Val(), cmd.Err()
}
func (r *Redis) LTrim(ctx context.Context, key string, start, stop int64) error {
cmd := r.rdb.LTrim(ctx, key, start, stop)
return cmd.Err()
}
func (r *Redis) BLPop(ctx context.Context, timeout time.Duration, keys ...string) (*g.Var, error) {
cmd := r.rdb.BLPop(ctx, timeout, keys...)
if cmd.Err() == redis.Nil {
return g.NewVar(nil), nil
}
return g.NewVar(cmd.Val()), cmd.Err()
}
+42
View File
@@ -0,0 +1,42 @@
package gfredis
import (
"context"
"time"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
"github.com/redis/go-redis/v9"
)
func (r *Redis) Set(ctx context.Context, key string, value interface{}, expiration time.Duration) error {
cmd := r.rdb.Set(ctx, key, gconv.String(value), expiration)
return cmd.Err()
}
func (r *Redis) SetNX(ctx context.Context, key string, value interface{}, expiration time.Duration) (bool, error) {
cmd := r.rdb.SetNX(ctx, key, gconv.String(value), expiration)
return cmd.Val(), cmd.Err()
}
func (r *Redis) Get(ctx context.Context, key string) (*g.Var, error) {
cmd := r.rdb.Get(ctx, key)
if cmd.Err() == redis.Nil {
return g.NewVar(nil), nil
}
return g.NewVar(cmd.Val()), cmd.Err()
}
func (r *Redis) UpdateExpire(ctx context.Context, key string, expiration time.Duration) (bool, error) {
cmd := r.rdb.Expire(ctx, key, expiration)
return cmd.Val(), cmd.Err()
}
func (r *Redis) Remove(ctx context.Context, key string) (bool, error) {
cmd := r.rdb.Del(ctx, key)
return cmd.Val() > 0, cmd.Err()
}
func (r *Redis) Del(ctx context.Context, key string) (int64, error) {
cmd := r.rdb.Del(ctx, key)
return cmd.Val(), cmd.Err()
}
+18
View File
@@ -0,0 +1,18 @@
package gfredis
import (
"context"
"github.com/gogf/gf/v2/util/gconv"
"github.com/redis/go-redis/v9"
)
func (r *Redis) Subscribe(ctx context.Context, channels ...string) (*redis.PubSub, error) {
cmd := r.rdb.Subscribe(ctx, channels...)
return cmd, nil
}
func (r *Redis) Publish(ctx context.Context, channel string, message interface{}) (int64, error) {
cmd := r.rdb.Publish(ctx, channel, gconv.String(message))
return cmd.Val(), cmd.Err()
}
+21
View File
@@ -0,0 +1,21 @@
package gfredis
import (
"context"
)
func (r *Redis) ZPopByScore(ctx context.Context, key string, min, max string, limit int64) (res []string, err error) {
var luaScript = `
local members = redis.call('ZRANGEBYSCORE', KEYS[1], ARGV[1], ARGV[2], 'LIMIT', 0, ARGV[3])
if #members > 0 then
redis.call('ZREM', KEYS[1], unpack(members))
end
return members
`
cmd := r.rdb.Eval(ctx, luaScript, []string{key}, min, max, limit)
if err = cmd.Err(); err != nil {
return nil, err
}
res, err = cmd.StringSlice()
return
}
+100
View File
@@ -0,0 +1,100 @@
package hash
import (
"os"
)
// Table of CRC values for high–order byte
var crcHighBytes = []byte{
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
}
// Table of CRC values for low-order byte
var crcLowBytes = []byte{
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04,
0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8,
0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC,
0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10,
0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38,
0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C,
0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0,
0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4,
0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C,
0xB4, 0x74, 0x75, 0xB5, 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0,
0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54,
0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98,
0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80, 0x40,
}
// Cyclical Redundancy Checking
type Crc16 struct {
high byte
low byte
}
func (crc *Crc16) Reset() *Crc16 {
crc.high = 0xFF
crc.low = 0xFF
return crc
}
func (crc *Crc16) PushBytes(bs []byte) *Crc16 {
var idx, b byte
for _, b = range bs {
idx = crc.low ^ b
crc.low = crc.high ^ crcHighBytes[idx]
crc.high = crcLowBytes[idx]
}
return crc
}
func (crc *Crc16) Value() uint16 {
return uint16(crc.high)<<8 | uint16(crc.low)
}
func Crc16ChecksumData(bs []byte) uint16 {
crc := &Crc16{}
crc.Reset()
crc.PushBytes(bs)
return crc.Value()
}
func Crc16ChecksumFile(filePath string) uint16 {
file, err := os.Open(filePath)
if err != nil {
return 0
}
defer file.Close()
crc := &Crc16{}
crc.Reset()
// 分块读取
blockSize := 1024 * 1024
buf := make([]byte, blockSize)
for {
n, err := file.Read(buf)
if err != nil {
break
}
crc.PushBytes(buf[:n])
}
return crc.Value()
}
+31
View File
@@ -0,0 +1,31 @@
package hash
import "os"
func Sum16ChecksumData(bs []byte) uint16 {
sum := uint16(0)
for _, b := range bs {
sum += uint16(b)
}
return sum
}
func Sum16ChecksumFile(filePath string) uint16 {
file, err := os.Open(filePath)
if err != nil {
return 0
}
defer file.Close()
// 分块读取
blockSize := 1024 * 1024
buf := make([]byte, blockSize)
sum := uint16(0)
for {
n, err := file.Read(buf)
if err != nil {
break
}
sum += Sum16ChecksumData(buf[:n])
}
return sum
}
+109
View File
@@ -0,0 +1,109 @@
// package tree
// @Link https://github.com/xiujiecn/xiu-admin
// @Copyright Copyright (c) 2025 LiXiujie
// @Author Lxj <xxx@example.com>
// @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
package tree
import (
"fmt"
"strings"
"github.com/gogf/gf/v2/util/gconv"
)
const (
treeDefaultId = "id" // 树ID
treeDefaultPid = "pid" // 上级树ID
treeDefaultChildren = "children" // 下级属性
treeBeginCut = "tr_" // 树标识开头
treeEndCut = " " // 树标识结尾
)
// GenOption 生成选项
type GenOption struct {
IdField string
PidField string
ChildrenField string
}
// GenLabel 生成关系树标识
func GenLabel(basic string, appendId int64) string {
return fmt.Sprintf("%v%v%v%v", basic, treeBeginCut, appendId, treeEndCut)
}
// GetIdLabel 获取指定Id的树标签
func GetIdLabel(id int64) string {
return fmt.Sprintf("%v%v%v", treeBeginCut, id, treeEndCut)
}
// GetIds 获取上级ID集合
func GetIds(tree string) (ids []int64) {
idsStr := strings.Split(tree, treeEndCut)
if len(idsStr) == 0 {
return
}
for _, v := range idsStr {
newId := gconv.Int64(strings.ReplaceAll(v, treeBeginCut, ""))
if newId > 0 {
ids = append(ids, newId)
}
}
return
}
// GenTree 生成关系树
func GenTree(menus []map[string]interface{}) (realMenu []map[string]interface{}) {
return GenTreeWithField(menus, GenOption{
IdField: treeDefaultId,
PidField: treeDefaultPid,
ChildrenField: treeDefaultChildren,
})
}
// GenTreeWithField 生成关系树 自定义生成属性
func GenTreeWithField(menus []map[string]interface{}, op GenOption) (realMenu []map[string]interface{}) {
if len(menus) < 1 {
return
}
minPid := GetMinPid(menus, op.PidField)
formatMenu := make(map[int]map[string]interface{})
for _, m := range menus {
formatMenu[gconv.Int(m[op.IdField])] = m
if gconv.Int(m[op.PidField]) == minPid {
realMenu = append(realMenu, m) // 需要返回的顶级菜单
}
}
// 得益于都是地址操作,可以直接往父级塞
for _, m := range formatMenu {
if formatMenu[gconv.Int(m[op.PidField])] == nil {
continue
}
if formatMenu[gconv.Int(m[op.PidField])][op.ChildrenField] == nil {
formatMenu[gconv.Int(m[op.PidField])][op.ChildrenField] = []map[string]interface{}{}
}
formatMenu[gconv.Int(m[op.PidField])][op.ChildrenField] = append(formatMenu[gconv.Int(m[op.PidField])][op.ChildrenField].([]map[string]interface{}), m)
}
return
}
func GetMinPid(menus []map[string]interface{}, pidField string) int {
index := -1
for _, m := range menus {
if index == -1 {
index = gconv.Int(m[pidField])
continue
}
if gconv.Int(m[pidField]) < index {
index = gconv.Int(m[pidField])
}
}
if index == -1 {
return 0
}
return index
}
+283
View File
@@ -0,0 +1,283 @@
// package utility
// @Link https://github.com/xiujiecn/xiu-admin
// @Copyright Copyright (c) 2025 LiXiujie
// @Author Lxj <xxx@example.com>
// @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
package utility
import (
"context"
"fmt"
"io"
"net"
"net/http"
"os"
"path/filepath"
"slices"
"strconv"
"strings"
"time"
"github.com/gogf/gf/v2/crypto/gmd5"
"github.com/gogf/gf/v2/encoding/gcharset"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/os/gtime"
"github.com/mileusna/useragent"
"golang.org/x/exp/rand"
)
func Md5(str string) string {
return gmd5.MustEncryptString(str)
}
// 密码加密
func PasswordEncrypt(password string, salt string) string {
return Md5(password + salt)
}
// 解析时间
func ParseDuration(d string) (time.Duration, error) {
d = strings.TrimSpace(d)
dr, err := time.ParseDuration(d)
if err == nil {
return dr, nil
}
if strings.Contains(d, "d") {
index := strings.Index(d, "d")
hour, _ := strconv.Atoi(d[:index])
dr = time.Hour * 24 * time.Duration(hour)
ndr, err := time.ParseDuration(d[index+1:])
if err != nil {
return dr, nil
}
return dr + ndr, nil
}
dv, err := strconv.ParseInt(d, 10, 64)
return time.Duration(dv), err
}
// 随机生成5位字符
func RandomString(n int) string {
letters := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
b := make([]byte, n)
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
return string(b)
}
// 获取客户端IP
func GetClientIp(ctx context.Context) string {
if ctx == nil {
return ""
}
if ghttp.RequestFromCtx(ctx) == nil {
return ""
}
return ghttp.RequestFromCtx(ctx).GetClientIp()
}
// 获取user-agent
func GetUserAgent(ctx context.Context) string {
return ghttp.RequestFromCtx(ctx).Header.Get("User-Agent")
}
// 获取操作系统
func GetOs(ctx context.Context) string {
userAgent := GetUserAgent(ctx)
ua := useragent.Parse(userAgent)
return ua.OS
}
// 获取浏览器
func GetBrowser(ctx context.Context) string {
userAgent := GetUserAgent(ctx)
ua := useragent.Parse(userAgent)
return ua.Name
}
// 获取ip所属城市
func GetCityByIp(ip string) string {
if ip == "" {
return ""
}
if ip == "[::1]" || ip == "127.0.0.1" {
return "内网IP"
}
url := "https://whois.pconline.com.cn/ipJson.jsp?json=true&ip=" + ip
bytes := g.Client().GetBytes(context.TODO(), url)
src := string(bytes)
srcCharset := "GBK"
tmp, _ := gcharset.ToUTF8(srcCharset, src)
json, err := gjson.DecodeToJson(tmp)
if err != nil {
return ""
}
if json.Get("code").Int() == 0 {
city := ""
if strings.EqualFold(json.Get("pro").String(), json.Get("city").String()) {
city = fmt.Sprintf("%s", json.Get("pro").String())
} else {
city = fmt.Sprintf("%s %s", json.Get("pro").String(), json.Get("city").String())
}
return city
} else {
return ""
}
}
// 时间戳转 yyyy-MM-dd HH:mm:ss
func TimeStampToDateTime(timeStamp int64) string {
tm := gtime.NewFromTimeStamp(timeStamp)
return tm.Format("Y-m-d H:i:s")
}
// 时间戳转 yyyy-MM-dd
func TimeStampToDate(timeStamp int64) string {
tm := gtime.NewFromTimeStamp(timeStamp)
return tm.Format("Y-m-d")
}
// 服务端ip
func GetLocalIP() (ip string, err error) {
addrs, err := net.InterfaceAddrs()
if err != nil {
return
}
for _, addr := range addrs {
ipAddr, ok := addr.(*net.IPNet)
if !ok {
continue
}
if ipAddr.IP.IsLoopback() {
continue
}
if !ipAddr.IP.IsGlobalUnicast() {
continue
}
return ipAddr.IP.String(), nil
}
return
}
func GetPublicIP() (ip string, err error) {
resp, err := http.Get("https://ifconfig.co/ip")
if err != nil {
fmt.Println("GetPublicIP", err)
return
}
defer func(Body io.ReadCloser) {
if err := Body.Close(); err != nil {
fmt.Println("GetPublicIP Body.Close", err)
}
}(resp.Body)
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("GetPublicIP ReadAll", err)
return
}
ip = string(body)
// 去除空格
ip = strings.Replace(ip, " ", "", -1)
// 去除换行符
ip = strings.Replace(ip, "\n", "", -1)
return
}
// 获取两个字符串之间的字符串
func GetBetweenStr(str, start, end string) (result string) {
startIndex := strings.Index(str, start)
if startIndex == -1 {
return ""
}
// 更新startIndex为开始字符串之后的位置
startIndex += len(start)
// 找到结束字符串的位置,从更新后的startIndex开始查找
endIndex := strings.Index(str[startIndex:], end)
if endIndex == -1 {
return ""
}
// 切片操作获取中间的字符串
result = str[startIndex : startIndex+endIndex]
return
}
// A和B两个数组,A数组中存在,B数组中不存在
func ArrayRightDiff(a, b []int64) []int64 {
diff := make([]int64, 0)
for _, v := range a {
if !slices.Contains(b, v) {
diff = append(diff, v)
}
}
return diff
}
type fileInfo struct {
name string
size int64
}
// WalkDir 获取目录下文件的名称和大小
func WalkDir(dirname string) ([]fileInfo, error) {
var fileInfos []fileInfo
err := filepath.Walk(dirname, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() {
fileInfos = append(fileInfos, fileInfo{name: path, size: info.Size()})
}
return nil
})
return fileInfos, err
}
func DirSize(dirname string) string {
var (
s int64
files, _ = WalkDir(dirname)
)
for _, n := range files {
s += n.size
}
return FileSize(s)
}
func MergeAbs(path string, fileName ...string) string {
var paths = []string{gfile.RealPath(path)}
paths = append(paths, fileName...)
return gfile.Join(paths...)
}
// FileSize 字节的单位转换 保留两位小数
func FileSize(fileSize int64) string {
units := []string{"B", "KB", "MB", "GB", "TB", "EB"}
var size = float64(fileSize)
var i int
for i = 0; size > 1024; i++ {
size /= 1024
}
return fmt.Sprintf("%.2f %s", size, units[i])
}
// UniqueSlice 切片去重
func UniqueSlice[K comparable](languages []K) []K {
result := make([]K, 0, len(languages))
temp := map[K]struct{}{}
for _, item := range languages {
if _, ok := temp[item]; !ok {
temp[item] = struct{}{}
result = append(result, item)
}
}
return result
}
+15
View File
@@ -0,0 +1,15 @@
package uuid32
import (
"strings"
"github.com/google/uuid"
)
func New() string {
return strings.ReplaceAll(uuid.NewString(), "-", "")
}
func S() string {
return New()
}
+39
View File
@@ -0,0 +1,39 @@
// package version
// @Link https://github.com/xiujiecn/xiu-admin
// @Copyright Copyright (c) 2025 LiXiujie
// @Author Lxj <xxx@example.com>
// @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
package version
import (
"context"
"fmt"
"github.com/gogf/gf/v2/frame/g"
)
var (
BuildTime string
BuildVersion string
CommitID string
)
func ShowLogo(buildVersion, buildTime, commitID string) {
BuildVersion = buildVersion
BuildTime = buildTime
CommitID = commitID
//版本号
fmt.Println(" __ __ _ _ _ _ \n \\ \\/ /(_) _ _ / \\ __| | _ __ ___ (_) _ __ \n \\ / | || | | | / _ \\ / _` || '_ ` _ \\ | || '_ \\ \n / \\ | || |_| | / ___ \\| (_| || | | | | || || | | |\n /_/\\_\\|_| \\__,_| /_/ \\_\\\\__,_||_| |_| |_||_||_| |_|")
fmt.Println("")
fmt.Println("Version :", buildVersion)
fmt.Println("BuildTime :", buildTime)
fmt.Println("CommitID :", commitID)
fmt.Println("")
fmt.Println("Copyright:", "XiuJieZhiLian Technology Co.,Ltd")
}
func AppName(ctx context.Context) string {
return g.Cfg().MustGet(ctx, "system.appName", "IotLCM").String()
}