4 Commits

6 changed files with 111 additions and 83 deletions

View File

@@ -41,6 +41,7 @@ type MongoBaseDO struct {
// SQLBaseDO SQL数据库基础实体 // SQLBaseDO SQL数据库基础实体
type SQLBaseDO struct { type SQLBaseDO struct {
Id int64 `orm:"id" json:"id"` // 主键ID Id int64 `orm:"id" json:"id"` // 主键ID
TenantId uint64 `orm:"tenant_id" json:"tenantId"` // 租户ID
Creator string `orm:"creator" json:"creator"` // 创建人 Creator string `orm:"creator" json:"creator"` // 创建人
CreatedAt *gtime.Time `orm:"created_at" json:"createdAt"` // 创建时间 CreatedAt *gtime.Time `orm:"created_at" json:"createdAt"` // 创建时间
Updater string `orm:"updater" json:"updater"` // 更新人 Updater string `orm:"updater" json:"updater"` // 更新人
@@ -51,6 +52,7 @@ type SQLBaseDO struct {
type SQLBaseCol struct { type SQLBaseCol struct {
Id string Id string
TenantId string
Creator string Creator string
CreatedAt string CreatedAt string
Updater string Updater string
@@ -61,6 +63,7 @@ type SQLBaseCol struct {
var DefSQLBaseCol = SQLBaseCol{ var DefSQLBaseCol = SQLBaseCol{
Id: "id", Id: "id",
TenantId: "tenant_id",
Creator: "creator", Creator: "creator",
CreatedAt: "created_at", CreatedAt: "created_at",
Updater: "updater", Updater: "updater",

View File

@@ -173,12 +173,25 @@ func insertHook(ctx context.Context, in *gdb.HookInsertInput) (result sql.Result
if _, ok := in.Data[i]["id"]; ok { if _, ok := in.Data[i]["id"]; ok {
in.Data[i]["id"] = node.Generate().Int64() in.Data[i]["id"] = node.Generate().Int64()
} }
if !g.IsEmpty(userInfo.UserName) { if _, ok := in.Data[i]["tenant_id"]; ok {
if _, ok := in.Data[i]["creator"]; ok { if !g.IsEmpty(userInfo.TenantId) {
in.Data[i]["creator"] = userInfo.UserName in.Data[i]["tenant_id"] = userInfo.TenantId
} else {
return nil, fmt.Errorf("tenantId cannot be empty")
} }
if _, ok := in.Data[i]["updater"]; ok { }
if _, ok := in.Data[i]["creator"]; ok {
if !g.IsEmpty(userInfo.UserName) {
in.Data[i]["creator"] = userInfo.UserName
} else {
return nil, fmt.Errorf("user info cannot be empty")
}
}
if _, ok := in.Data[i]["updater"]; ok {
if !g.IsEmpty(userInfo.UserName) {
in.Data[i]["updater"] = userInfo.UserName in.Data[i]["updater"] = userInfo.UserName
} else {
return nil, fmt.Errorf("user info cannot be empty")
} }
} }
} }
@@ -207,16 +220,22 @@ func updateHook(ctx context.Context, in *gdb.HookUpdateInput) (result sql.Result
switch data := in.Data.(type) { switch data := in.Data.(type) {
case gdb.Map: case gdb.Map:
if !g.IsEmpty(userInfo.UserName) { if _, ok := data["updater"]; ok {
if _, ok := data["updater"]; ok { if !g.IsEmpty(userInfo.UserName) {
data["updater"] = userInfo.UserName data["updater"] = userInfo.UserName
} else {
return nil, fmt.Errorf("user info cannot be empty")
} }
} }
case gdb.List: case gdb.List:
for i := range data { for i := range data {
if !g.IsEmpty(userInfo.UserName) { if !g.IsEmpty(userInfo.UserName) {
if _, ok := data[i]["updater"]; ok { if _, ok := data[i]["updater"]; ok {
data[i]["updater"] = userInfo.UserName if !g.IsEmpty(userInfo.UserName) {
data[i]["updater"] = userInfo.UserName
} else {
return nil, fmt.Errorf("user info cannot be empty")
}
} }
} }
} }
@@ -421,7 +440,7 @@ func (d *dataBase) Model(ctx context.Context, tableNameOrStruct ...any) *model {
m.Sharding(shardingConfig).ShardingValue(user.TenantId) m.Sharding(shardingConfig).ShardingValue(user.TenantId)
} }
m.OmitNilData().OmitNilWhere().Hook(catchSQLHook()) m.OmitNil().OmitEmpty().Hook(catchSQLHook())
return &model{ return &model{
Model: m, Model: m,
} }

View File

@@ -35,7 +35,7 @@ func init() {
} }
//s.Use(common.Cors) //中间件验证 //s.Use(common.Cors) //中间件验证
//s.EnablePProf() //启用性能分析 //s.EnablePProf() //启用性能分析
Httpserver.BindMiddlewareDefault(validateFilterPathMiddleware) //Httpserver.BindMiddlewareDefault(validateFilterPathMiddleware)
Httpserver.SetOpenApiPath("/api.json") Httpserver.SetOpenApiPath("/api.json")
Httpserver.SetDumpRouterMap(true) //关闭打印路由注册信息 Httpserver.SetDumpRouterMap(true) //关闭打印路由注册信息
Httpserver.BindMiddlewareDefault(ghttp.MiddlewareHandlerResponse) Httpserver.BindMiddlewareDefault(ghttp.MiddlewareHandlerResponse)

View File

@@ -3,15 +3,15 @@ package minio
import ( import (
"context" "context"
"fmt" "fmt"
"gitea.com/red-future/common/utils"
"github.com/gogf/gf/v2/os/glog"
"github.com/gogf/gf/v2/util/gconv"
"net/http" "net/http"
"path/filepath" "path/filepath"
"strings"
"time" "time"
"gitea.com/red-future/common/utils"
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp" "github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/glog"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials" "github.com/minio/minio-go/v7/pkg/credentials"
@@ -19,12 +19,11 @@ import (
// IoConfig 映射 YAML 中的 minio 配置节点 // IoConfig 映射 YAML 中的 minio 配置节点
type IoConfig struct { type IoConfig struct {
FilePrefix string `yaml:"filePrefix"` // 文件前缀 Endpoint string `yaml:"endpoint"` // MinIO API 地址
Endpoint string `yaml:"endpoint"` // MinIO API 地址 AccessKey string `yaml:"accessKey"` // AK
AccessKey string `yaml:"accessKey"` // AK SecretKey string `yaml:"secretKey"` // SK
SecretKey string `yaml:"secretKey"` // SK Secure bool `yaml:"secure"` // 是否启用 SSL
Secure bool `yaml:"secure"` // 是否启用 SSL Region string `yaml:"region"` // 区域
Region string `yaml:"region"` // 区域
} }
// 全局 MinIO 客户端(初始化一次,避免重复创建) // 全局 MinIO 客户端(初始化一次,避免重复创建)
@@ -37,12 +36,11 @@ func init() {
if !g.Cfg().MustGet(ctx, "minio").IsEmpty() { if !g.Cfg().MustGet(ctx, "minio").IsEmpty() {
// 加载 MinIO 配置(可从配置文件/环境变量读取,这里硬编码示例) // 加载 MinIO 配置(可从配置文件/环境变量读取,这里硬编码示例)
minioCfg = IoConfig{ minioCfg = IoConfig{
FilePrefix: g.Cfg().MustGet(ctx, "filePrefix").String(), Endpoint: g.Cfg().MustGet(ctx, "minio.endpoint").String(),
Endpoint: g.Cfg().MustGet(ctx, "minio.endpoint").String(), AccessKey: g.Cfg().MustGet(ctx, "minio.accessKey").String(),
AccessKey: g.Cfg().MustGet(ctx, "minio.accessKey").String(), SecretKey: g.Cfg().MustGet(ctx, "minio.secretKey").String(),
SecretKey: g.Cfg().MustGet(ctx, "minio.secretKey").String(), Secure: g.Cfg().MustGet(ctx, "minio.secure").Bool(),
Secure: g.Cfg().MustGet(ctx, "minio.secure").Bool(), Region: g.Cfg().MustGet(ctx, "minio.region").String(),
Region: g.Cfg().MustGet(ctx, "minio.region").String(),
} }
// 创建 MinIO 客户端 // 创建 MinIO 客户端
var err error var err error
@@ -56,11 +54,16 @@ func init() {
} }
} }
func UploadFile(ctx context.Context, fileHeader *ghttp.UploadFile) (imagesUrl string, err error) { func UploadFile(ctx context.Context, fileHeader *ghttp.UploadFile) (imagesUrl string, fileName string, fileFormat string, err error) {
return uploadFile(ctx, getBucketName(ctx), fileHeader) return uploadFile(ctx, fileHeader)
} }
func uploadFile(ctx context.Context, bucketName string, fileHeader *ghttp.UploadFile) (imagesUrl string, err error) { func uploadFile(ctx context.Context, fileHeader *ghttp.UploadFile) (imagesUrl string, fileName string, fileFormat string, err error) {
bucketName, err := utils.GetBucketName(ctx)
if err != nil {
glog.Errorf(ctx, "获取桶名称失败: %v", err)
return
}
// 检查/创建桶 // 检查/创建桶
exists, err := minioClient.BucketExists(ctx, bucketName) exists, err := minioClient.BucketExists(ctx, bucketName)
if err != nil { if err != nil {
@@ -122,25 +125,5 @@ func uploadFile(ctx context.Context, bucketName string, fileHeader *ghttp.Upload
glog.Errorf(ctx, "上传图片失败: %v", err) glog.Errorf(ctx, "上传图片失败: %v", err)
return return
} }
return objectName, err return objectName, fileHeader.Filename, strings.ReplaceAll(fileExt, ".", ""), err
}
// GetFileAddressPrefix 拼接图片前缀地址
func GetFileAddressPrefix(ctx context.Context) (imageUrl string) {
// 拼接图片前缀地址
var url = "http://"
if minioCfg.Secure {
url = "https://"
}
imgAddressPrefix := url + minioCfg.FilePrefix + "/" + getBucketName(ctx)
return imgAddressPrefix
}
func getBucketName(ctx context.Context) (bucketName string) {
user, err := utils.GetUserInfo(ctx)
if err != nil {
glog.Errorf(ctx, "获取用户信息失败: %v", err)
return
}
return "tenantid-" + gconv.String(user.TenantId)
} }

View File

@@ -2,7 +2,6 @@ package redis
import ( import (
"context" "context"
"errors"
"strings" "strings"
"sync" "sync"
"time" "time"
@@ -38,40 +37,6 @@ func RedisClient() *gredis.Redis {
return getClient() return getClient()
} }
// Lock 分布式锁
func Lock(ctx context.Context, key string, expireSeconds int64, fn func(ctx context.Context) error) (success bool, err error) {
limit := 3
LOOP:
if limit < 0 {
return false, errors.New("锁重试次数耗尽")
}
limit--
client := getClient()
if val, err := client.Set(ctx, key, true, gredis.SetOption{
TTLOption: gredis.TTLOption{
EX: &expireSeconds,
},
NX: true,
}); err != nil {
return false, err
} else {
if val.Bool() {
defer func(client *gredis.Redis, ctx context.Context, key string) {
if _, err = client.Del(ctx, key); err != nil {
glog.Errorf(ctx, "redis client Del error: %v", err)
}
}(client, ctx, key)
if err = fn(ctx); err != nil {
return false, err
}
return true, nil
} else {
time.Sleep(time.Second)
goto LOOP
}
}
}
func GetReadStream(ctx context.Context, msg ...QueueMessage) error { func GetReadStream(ctx context.Context, msg ...QueueMessage) error {
for _, t := range msg { for _, t := range msg {
err := GetReadFromStream(ctx, t.StreamKey, t.GroupName, t.ConsumerName, t.BatchSize, t.BlockMs, t.AutoAck, t.HandleFunc) err := GetReadFromStream(ctx, t.StreamKey, t.GroupName, t.ConsumerName, t.BatchSize, t.BlockMs, t.AutoAck, t.HandleFunc)

View File

@@ -3,6 +3,7 @@ package utils
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"net" "net"
"reflect" "reflect"
@@ -13,10 +14,12 @@ import (
"time" "time"
"gitea.com/red-future/common/beans" "gitea.com/red-future/common/beans"
"github.com/gogf/gf/v2/container/gvar"
"github.com/gogf/gf/v2/database/gredis" "github.com/gogf/gf/v2/database/gredis"
"github.com/gogf/gf/v2/errors/gcode" "github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/glog"
"github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gconv" "github.com/gogf/gf/v2/util/gconv"
"github.com/tiger1103/gfast-token/gftoken" "github.com/tiger1103/gfast-token/gftoken"
@@ -385,3 +388,58 @@ func intPow10(n int) int {
} }
return result return result
} }
// GetFileAddressPrefix 拼接图片前缀地址
func GetFileAddressPrefix(ctx context.Context) (imageUrl string, err error) {
// 拼接图片前缀地址
bucketName, err := GetBucketName(ctx)
if err != nil {
return
}
imageUrl = fmt.Sprintf("%s/%s", g.Cfg().MustGet(ctx, "filePrefix").String(), bucketName)
return
}
// GetBucketName 获取bucket名称
func GetBucketName(ctx context.Context) (bucketName string, err error) {
user, err := GetUserInfo(ctx)
if err != nil {
return
}
bucketName = fmt.Sprintf("tenantid-%d", user.TenantId)
return
}
// Lock 分布式锁
func Lock(ctx context.Context, key string, expireSeconds int64, fn func(ctx context.Context) error) (success bool, err error) {
limit := 3
LOOP:
if limit < 0 {
return false, errors.New("锁重试次数耗尽")
}
limit--
var val *gvar.Var
if val, err = g.Redis().Set(ctx, key, true, gredis.SetOption{
TTLOption: gredis.TTLOption{
EX: &expireSeconds,
},
NX: true,
}); err != nil {
return false, err
}
if val.Bool() {
defer func(ctx context.Context, key string) {
if _, err = g.Redis().Del(ctx, key); err != nil {
glog.Errorf(ctx, "redis client Del error: %v", err)
}
}(ctx, key)
if err = fn(ctx); err != nil {
return false, err
}
return true, nil
}
time.Sleep(time.Second)
goto LOOP
}