第一次提交

This commit is contained in:
2026-01-30 15:51:42 +08:00
commit 7218ba6508
26 changed files with 3461 additions and 0 deletions
+102
View File
@@ -0,0 +1,102 @@
package utils
import (
"encoding/base64"
"fmt"
"image"
"image/jpeg"
"image/png"
"io/ioutil"
"os"
"strings"
)
// Base64ToImage 将Base64编码的数据转换为图片并保存到指定路径
func Base64ToImage(base64String, outputPath string) error {
// 移除Base64数据URI前缀(如果有)
if strings.Contains(base64String, ",") {
base64String = strings.Split(base64String, ",")[1]
}
// 解码Base64字符串
imageData, err := base64.StdEncoding.DecodeString(base64String)
if err != nil {
return fmt.Errorf("解码Base64数据时出错: %v", err)
}
// 将解码后的数据写入文件
err = ioutil.WriteFile(outputPath, imageData, 0644)
if err != nil {
return fmt.Errorf("保存图片文件时出错: %v", err)
}
return nil
}
// Base64ToImageWithFormat 将Base64编码的数据转换为指定格式的图片并保存
func Base64ToImageWithFormat(base64String, outputPath, format string) error {
// 移除Base64数据URI前缀(如果有)
if strings.Contains(base64String, ",") {
base64String = strings.Split(base64String, ",")[1]
}
// 解码Base64字符串
imageData, err := base64.StdEncoding.DecodeString(base64String)
if err != nil {
return fmt.Errorf("解码Base64数据时出错: %v", err)
}
// 创建文件
file, err := os.Create(outputPath)
if err != nil {
return fmt.Errorf("创建图片文件时出错: %v", err)
}
defer file.Close()
// 根据指定格式编码图片
switch strings.ToLower(format) {
case "jpeg", "jpg":
// 解码图片
img, _, err := image.Decode(strings.NewReader(string(imageData)))
if err != nil {
return fmt.Errorf("解码图片时出错: %v", err)
}
// 编码为JPEG格式
err = jpeg.Encode(file, img, &jpeg.Options{Quality: 90})
if err != nil {
return fmt.Errorf("编码JPEG图片时出错: %v", err)
}
case "png":
// 解码图片
img, _, err := image.Decode(strings.NewReader(string(imageData)))
if err != nil {
return fmt.Errorf("解码图片时出错: %v", err)
}
// 编码为PNG格式
err = png.Encode(file, img)
if err != nil {
return fmt.Errorf("编码PNG图片时出错: %v", err)
}
default:
// 直接写入原始数据
_, err = file.Write(imageData)
if err != nil {
return fmt.Errorf("写入图片数据时出错: %v", err)
}
}
return nil
}
// GetImageFormatFromBase64 从Base64数据中获取图片格式
func GetImageFormatFromBase64(base64String string) string {
// 检查数据URI前缀
if strings.HasPrefix(base64String, "data:image/") {
// 提取格式信息
format := strings.Split(strings.Split(base64String, ";")[0], "/")[1]
return format
}
return "unknown"
}
+32
View File
@@ -0,0 +1,32 @@
package utils
type NormalRes[T any] struct {
Code int `json:"code" dc:"code"`
Data T `json:"data" dc:"data 可null"`
Msg string `json:"msg" dc:"return msg"`
}
type ListRes[T any] struct {
Rows []T
Total int `json:"total"`
}
func NewNormalRes[T any](data T, msg ...string) *NormalRes[T] {
message := ""
if len(msg) == 0 {
message = "操作成功"
} else {
message = msg[0]
}
return &NormalRes[T]{
Code: 1,
Data: data,
Msg: message,
}
}
func NewListRes[T any](data []T, total int) *ListRes[T] {
return &ListRes[T]{
Rows: data,
Total: total,
}
}
+346
View File
@@ -0,0 +1,346 @@
package utils
import (
"context"
"github.com/gogf/gf/v2/container/gmap"
"github.com/gogf/gf/v2/container/gvar"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gctx"
"github.com/gogf/gf/v2/os/glog"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
)
type ctx = context.Context
type IDao interface {
DB() gdb.DB
Table() string
Group() string
Ctx(ctx context.Context) *gdb.Model
Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error)
}
type Curd[R any] struct {
Dao IDao
}
var pageInfo = []string{
"page",
"size",
"num",
"limit",
"pagesize",
"pageSize",
"page_size",
"pageNum",
"pagenum",
"page_num",
}
func (c Curd[R]) BuildWhere(req any, changeWhere any, subWhere any, removeFields []string, isSnake ...bool) map[string]any {
// 默认使用小写下划线方式
caseTypeValue := gstr.Snake
if len(isSnake) > 0 && isSnake[0] == false {
caseTypeValue = gstr.CamelLower
}
// 转换req为map
reqMap := gconv.Map(req)
// 清理空值和分页信息
ctx := gctx.New()
cleanedReq := make(map[string]any)
for k, v := range reqMap {
// 清理空值
if g.IsEmpty(v) {
glog.Debugf(ctx, "清理空值:%s", k)
continue
}
// 清理分页信息
if gstr.InArray(pageInfo, k) {
glog.Debugf(ctx, "清理分页信息:%s", k)
continue
}
if len(removeFields) > 0 && gstr.InArray(removeFields, k) {
glog.Debugf(ctx, "清理字段:%s", k)
continue
}
cleanedReq[gstr.CaseConvert(k, caseTypeValue)] = v
}
// 处理changeWhere
if changeWhere != nil {
changeMap := gconv.Map(changeWhere)
for k, v := range changeMap {
if _, hasKey := cleanedReq[k]; !hasKey {
glog.Debugf(ctx, "处理changeWhere:%s", k)
continue
}
if len(removeFields) > 0 && gstr.InArray(removeFields, k) {
glog.Debugf(ctx, "清理应删除字段:%s", k)
continue
}
// 转换v为map
vMap := gconv.Map(v)
value, hasValue := vMap["value"]
op, hasOp := vMap["op"]
if hasValue {
glog.Debugf(ctx, "变更字段存在value:%s", k)
// 构建新的键名
newKey := k
if hasOp && op != "" {
glog.Debugf(ctx, "变更字段存在op:%s", k)
newKey = k + " " + gconv.String(op)
delete(cleanedReq, k)
}
cleanedReq[newKey] = value
}
}
}
// 变量名切换
resultMap := make(map[string]any)
for k, v := range cleanedReq {
// 提取原始键名(去掉op部分)
originalKey := k
opStr := ""
if opIndex := gstr.Pos(k, " "); opIndex > 0 {
originalKey = k[:opIndex]
opStr = k[opIndex+1:]
}
// 转换键名
convertedKey := originalKey
convertedKey = gstr.CaseConvert(convertedKey, caseTypeValue)
// 如果有op,重新构建键名
if opStr != "" {
convertedKey = convertedKey + " " + opStr
}
resultMap[convertedKey] = v
}
// 合并subWhere
if subWhere != nil {
subMap := gconv.Map(subWhere)
resultM := gmap.NewStrAnyMapFrom(resultMap)
resultM.Merge(gmap.NewStrAnyMapFrom(subMap))
resultMap = resultM.Map()
}
return resultMap
}
func (c Curd[R]) BuildMap(op string, value any, field ...string) map[string]any {
if len(field) > 0 {
return map[string]any{
"op": op,
"field": field[0],
"value": value,
}
}
return map[string]any{
"op": op,
"field": "",
"value": value,
}
}
func (c Curd[R]) Builder(ctx context.Context) *gdb.WhereBuilder {
return c.Dao.Ctx(ctx).Builder()
}
func (c Curd[R]) ClearField(req any, delField []string, subField ...map[string]any) map[string]any {
m := gmap.NewStrAnyMapFrom(gconv.Map(req))
if delField != nil && len(delField) > 0 {
m.Iterator(func(k string, v any) bool {
if g.IsEmpty(v) {
m.Remove(k)
return true
}
if gstr.InArray(delField, k) {
m.Remove(k)
return true
}
if gstr.InArray(pageInfo, k) {
m.Remove(k)
return true
}
return true
})
}
if subField != nil && len(subField) > 0 {
m.Merge(gmap.NewStrAnyMapFrom(subField[0]))
}
return m.Map()
}
func (c Curd[R]) ClearFieldPage(ctx ctx, req any, delField []string, where any, page *Paginate, order any, with bool) (items []*R, total int, err error) {
db := c.Dao.Ctx(ctx)
m := c.ClearField(req, delField)
if with {
db = db.WithAll()
}
db = db.Where(m)
if !g.IsNil(where) {
db = db.Where(where)
}
if order != nil {
db = db.Order(order)
}
if !g.IsNil(page) {
db = db.Page(page.Page, page.Limit)
}
err = db.ScanAndCount(&items, &total, false)
return
}
func (c Curd[R]) ClearFieldList(ctx ctx, req any, delField []string, where any, order any, with bool) (items []*R, err error) {
db := c.Dao.Ctx(ctx)
m := c.ClearField(req, delField)
db = db.Where(m)
if !g.IsNil(where) {
db = db.Where(where)
}
if with {
db = db.WithAll()
}
if !g.IsNil(order) {
db = db.Order(order)
}
err = db.Scan(&items)
return
}
func (c Curd[R]) ClearFieldOne(ctx ctx, req any, delField []string, where any, order any, with bool) (items *R, err error) {
db := c.Dao.Ctx(ctx)
m := c.ClearField(req, delField)
db = db.Where(m)
if !g.IsNil(where) {
db = db.Where(where)
}
if with {
db = db.WithAll()
}
if !g.IsNil(order) {
db = db.Order(order)
}
err = db.Scan(&items)
return
}
func (c Curd[R]) Value(ctx ctx, where any, field any) (*gvar.Var, error) {
return c.Dao.Ctx(ctx).Where(where).Fields(field).Value()
}
func (c Curd[R]) DeletePri(ctx ctx, primaryKey any) error {
_, err := c.Dao.Ctx(ctx).WherePri(primaryKey).Delete()
return err
}
func (c Curd[R]) DeleteWhere(ctx ctx, where any) error {
_, err := c.Dao.Ctx(ctx).Where(where).Delete()
return err
}
func (c Curd[R]) Sum(ctx ctx, where any, field string) (float64, error) {
return c.Dao.Ctx(ctx).Where(where).Sum(field)
}
func (c Curd[R]) ArrayField(ctx ctx, where any, field any) ([]*gvar.Var, error) {
if field == nil {
field = "*"
}
return c.Dao.Ctx(ctx).Where(where).Fields(field).Array()
}
func (c Curd[R]) FindPri(ctx ctx, primaryKey any, with bool) (model *R, err error) {
db := c.Dao.Ctx(ctx).WherePri(primaryKey)
if with {
db = db.WithAll()
}
err = db.Scan(&model)
if err != nil {
return
}
return
}
func (c Curd[R]) First(ctx ctx, where any, order any, with bool) (model *R, err error) {
db := c.Dao.Ctx(ctx).Where(where)
if with {
db = db.WithAll()
}
if !g.IsNil(order) {
db = db.Order(order)
}
err = db.Scan(&model)
if err != nil {
return
}
return
}
func (c Curd[R]) Exists(ctx ctx, where any) (exists bool, err error) {
return c.Dao.Ctx(ctx).Where(where).Exist()
}
func (c Curd[R]) All(ctx ctx, where any, order any, with bool) (items []*R, err error) {
db := c.Dao.Ctx(ctx)
if with {
db = db.WithAll()
}
if !g.IsNil(order) {
db = db.Order(order)
}
err = db.Where(where).Scan(&items)
if err != nil {
return nil, err
}
return
}
func (c Curd[R]) Count(ctx ctx, where any) (count int, err error) {
count, err = c.Dao.Ctx(ctx).Where(where).Count()
return
}
func (c Curd[R]) Save(ctx ctx, data any) (id int64, err error) {
result, err := c.Dao.Ctx(ctx).Save(data)
if err != nil {
return
}
id, err = result.LastInsertId()
return
}
func (c Curd[R]) Update(ctx ctx, where any, data any) (count int64, err error) {
result, err := c.Dao.Ctx(ctx).Where(where).Data(data).Update()
if err != nil {
return
}
count, err = result.RowsAffected()
return
}
func (c Curd[R]) UpdatePri(ctx ctx, primaryKey any, data any) (count int64, err error) {
result, err := c.Dao.Ctx(ctx).WherePri(primaryKey).Data(data).Update()
if err != nil {
return
}
count, err = result.RowsAffected()
return
}
func (c Curd[R]) Paginate(ctx context.Context, where any, p Paginate, with bool, order any) (items []*R, total int, err error) {
query := c.Dao.Ctx(ctx)
if where != nil {
query = query.Where(where)
}
query = query.Page(p.Page, p.Limit)
if order != nil {
query = query.Order(order)
}
if with == true {
query = query.WithAll()
}
err = query.Order(order).ScanAndCount(&items, &total, false)
if err != nil {
return
}
return
}
+12
View File
@@ -0,0 +1,12 @@
package utils
type Paginator[I any] struct {
Items []I
Total int
IsSimple bool
}
type Paginate struct {
Limit int `d:"20" json:"limit" v:"max:50"`
Page int `d:"1" dc:"页码" json:"page"`
}
+102
View File
@@ -0,0 +1,102 @@
package utils
import (
"context"
_ "github.com/gogf/gf/contrib/drivers/mysql/v2"
"github.com/gogf/gf/v2/crypto/gmd5"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/gclient"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
)
// GetCapitalPass MD5化并转换为大写
func GetCapitalPass(val string) string {
md5, err := gmd5.Encrypt(val)
if err != nil {
panic(err.Error())
}
return gstr.CaseCamel(md5)
}
// Transaction 简单封装事务操作
func Transaction(function func() error) {
err := g.DB().Transaction(context.TODO(), func(ctx context.Context, tx gdb.TX) error {
return function()
})
if err != nil {
panic(err.Error())
}
}
type SClient[R any] struct {
client *gclient.Client
request any
header map[string]string
url string
}
func NewClient[R any](request any, url string, header map[string]string) *SClient[R] {
s := &SClient[R]{}
if header != nil {
s.client = g.Client().ContentJson().SetHeaderMap(header)
} else {
s.client = g.Client().ContentJson()
}
s.header = header
s.url = url
s.request = request
return s
}
func (w *SClient[R]) Post(ctx context.Context) (res *R, err error) {
g.Log().Infof(ctx, "请求Url:%s,请求头:%v,请求方法:%s,请求内容:%s", w.url, w.header, "post", w.request)
resp := w.client.PostVar(ctx, w.url, w.request)
err = gconv.Struct(resp, &res)
if err != nil {
g.Log().Errorf(ctx, "解析响应体异常:%s", err)
return nil, err
}
return
}
func (w *SClient[R]) Get(ctx context.Context) (res *R, err error) {
g.Log().Infof(ctx, "请求Url:%s,请求头:%v,请求方法:%s,请求内容:%s", w.url, w.header, "get", w.request)
resp := w.client.GetVar(ctx, w.url, w.request)
err = gconv.Struct(resp, &res)
if err != nil {
g.Log().Errorf(ctx, "解析响应体异常:%s", err)
return nil, err
}
return
}
func (w *SClient[R]) Put(ctx context.Context) (res *R, err error) {
g.Log().Infof(ctx, "请求Url:%s,请求头:%v,请求方法:%s,请求内容:%s", w.url, w.header, "put", w.request)
resp := w.client.PutVar(ctx, w.url, w.request)
err = gconv.Struct(resp, &res)
if err != nil {
g.Log().Errorf(ctx, "解析响应体异常:%s", err)
return nil, err
}
return
}
func (w *SClient[R]) Delete(ctx context.Context) (res *R, err error) {
g.Log().Infof(ctx, "请求Url:%s,请求头:%v,请求方法:%s,请求内容:%s", w.url, w.header, "delete", w.request)
resp := w.client.DeleteVar(ctx, w.url, w.request)
err = gconv.Struct(resp, &res)
if err != nil {
g.Log().Errorf(ctx, "解析响应体异常:%s", err)
return nil, err
}
return
}
func (w *SClient[R]) Patch(ctx context.Context) (res *R, err error) {
g.Log().Infof(ctx, "请求Url:%s,请求头:%v,请求方法:%s,请求内容:%s", w.url, w.header, "patch", w.request)
resp := w.client.PatchVar(ctx, w.url, w.request)
err = gconv.Struct(resp, &res)
if err != nil {
g.Log().Errorf(ctx, "解析响应体异常:%s", err)
return nil, err
}
return
}
+197
View File
@@ -0,0 +1,197 @@
package utils
import (
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"fmt"
"image/jpeg"
"math/big"
"os"
"github.com/gogf/gf/v2/container/garray"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gctx"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/os/gres"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
"github.com/nfnt/resize"
)
// Compress 图片压缩
/*
* @param filePath string 图片路径
* @return string
* 压缩图片
*/
func Compress(filePath string) string {
file, err := os.Open(filePath)
if err != nil {
panic(err.Error())
}
img, err := jpeg.Decode(file)
if err != nil {
panic(err.Error())
}
err = file.Close()
if err != nil {
panic(err.Error())
}
m := resize.Resize(960, 0, img, resize.Lanczos2)
str := gstr.Split(filePath, "/")
sta := gstr.Split(str[len(str)-1], ".")
paths := gfile.Pwd() + "/resource/public/upload/" + sta[0] + "-cop." + sta[1]
out, err := os.Create(paths)
defer out.Close()
err = jpeg.Encode(out, m, nil)
if err != nil {
panic(err.Error())
}
_ = gfile.RemoveFile(filePath)
return sta[0] + "-cop." + sta[1]
}
// Sha256 sha256加密
/*
* @param src string 被加密字符串
* @return string
* 将字符串进行hash加密
*/
func Sha256(src string) string {
m := sha256.New()
m.Write([]byte(src))
res := hex.EncodeToString(m.Sum(nil))
return res
}
// InStrArray 判断是否在数组中
/*
* @param ext string 要判断的字符串
* @param code int
* 判断是否在字符串数组中
*/
func InStrArray(ext string, code int) bool {
if code == 1 {
arr := garray.NewStrArrayFrom(g.SliceStr{".jpg", ".jpeg", ".png"})
return arr.Contains(ext)
} else {
arr := garray.NewStrArrayFrom(g.SliceStr{".xlsx"})
return arr.Contains(ext)
}
}
// ResAddFile 添加文件到资源包
/*
* @param onePath string
* 例:gf pack resource/dist internal/boot/boot_resource.go -n boot
* 需要在boot中 打包引入 并在main.go中引入boot
*/
func ResAddFile(onePath string) {
if gfile.Exists(gfile.Pwd() + gfile.Separator + onePath) {
err := gfile.RemoveFile(onePath)
if err != nil {
panic(err)
}
}
g.Log().Debug(gctx.GetInitCtx(), onePath)
gres.Dump()
if gres.IsEmpty() {
return
}
if gstr.Contains(onePath, "/") {
strs := gstr.Split(onePath, "/")
err := gres.Export(strs[1], strs[0])
if err != nil {
panic(err)
}
} else {
err := gres.Export(onePath, onePath)
if err != nil {
panic(err)
}
}
}
const (
CharsetLower = "abcdefghijklmnopqrstuvwxyz"
CharsetUpper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
CharsetNumber = "0123456789"
CharsetDefault = CharsetLower + CharsetUpper + CharsetNumber
)
// GenerateString 生成安全随机字符串
func GenerateString(length int) (str string) {
bytes := make([]byte, length)
charsetLen := big.NewInt(int64(len(CharsetDefault)))
for i := range bytes {
n, err := rand.Int(rand.Reader, charsetLen)
if err != nil {
panic(err)
}
bytes[i] = CharsetDefault[n.Int64()]
}
str = string(bytes)
return
}
// GetFileList 获取文件列表
/*
* 根据传入的路径返回该路径下的所有可访问的列表
* 要求必须将静态文件访问路径定义为static,且静态文件访问目录为resource
*/
func GetFileList(path string) []string {
if path == "" {
path = "/"
}
filePath := fmt.Sprintf("%s%sresource", gfile.Pwd(), gfile.Separator)
if path != "/" {
filePath += gfile.Separator + path + gfile.Separator
}
paths, _ := gfile.DirNames(filePath)
pathArr := garray.NewStrArray()
for _, v := range paths {
if gstr.Contains(v, ".") {
if path != "/" {
pathArr.Append("/static/" + path + "/" + v)
} else {
pathArr.Append("/static/" + v)
}
} else {
pathArr.Append(v)
}
}
return pathArr.Slice()
}
func Float64Trans(value string) *float64 {
if value == "" {
return nil
}
f := gconv.Float64(value)
return &f
}
func IntTrans(value string) *int {
if value == "" {
return nil
}
i := gconv.Int(value)
return &i
}
func StrTrans(value string) *string {
if value == "" {
return nil
}
i := gconv.String(value)
return &i
}
func TimeTrans(value string) *gtime.Time {
if value == "" {
return nil
}
return gconv.GTime(value)
}