diff --git a/consul/consul.go b/consul/consul.go new file mode 100644 index 0000000..177a74b --- /dev/null +++ b/consul/consul.go @@ -0,0 +1,24 @@ +package consul + +import ( + "context" + _ "github.com/gogf/gf/contrib/drivers/mysql/v2" + _ "github.com/gogf/gf/contrib/nosql/redis/v2" + "github.com/gogf/gf/contrib/registry/consul/v2" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/net/gsvc" + _ "go.mongodb.org/mongo-driver/mongo" +) + +func init() { + consulCfg, err := g.Cfg().Get(context.Background(), "consul.address") + if err != nil { + panic(err) + } + consulAddr := consulCfg.String() + registry, err := consul.New(consul.WithAddress(consulAddr)) + if err != nil { + panic(err) + } + gsvc.SetRegistry(registry) +} diff --git a/http/http.go b/http/http.go new file mode 100644 index 0000000..f4e2efb --- /dev/null +++ b/http/http.go @@ -0,0 +1,77 @@ +package http + +import ( + "context" + "errors" + "gitee.com/red-future---jilin-g/common/utils" + "github.com/gogf/gf/contrib/registry/consul/v2" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/net/gclient" + "github.com/gogf/gf/v2/net/ghttp" + "github.com/gogf/gf/v2/net/gsel" + "github.com/gogf/gf/v2/net/gsvc" + "github.com/gogf/gf/v2/os/glog" + "github.com/gogf/gf/v2/util/gconv" + "net/http" +) + +type ResponseEmpty struct { +} + +const PageSize = 20 + +type Page struct { + PageNum int `p:"pageNum"` //当前页码 + PageSize int `p:"pageSize"` //每页数 + Total int //总页数 +} + +func getHttpClient(ctx context.Context) (client *gclient.Client, err error) { + consulCfg, _ := g.Cfg().Get(context.Background(), "consul.address") + consulAddr := consulCfg.String() + registry, err := consul.New(consul.WithAddress(consulAddr)) + if err != nil { + return + } + gsvc.SetRegistry(registry) + gsel.SetBuilder(gsel.NewBuilderRoundRobin()) + client = g.Client() + client.SetHeader("Authorization", g.RequestFromCtx(ctx).GetHeader("Authorization")) + client.SetDiscovery(gsvc.GetRegistry()) + return +} +func doRequest(ctx context.Context, method string, url string, target any, data ...any) (err error) { + err = utils.ValidStructPtr(target) + if err != nil { + return + } + client, err := getHttpClient(ctx) + if err != nil { + return + } + response, err := client.Get(ctx, method, url, data) + if err != nil { + return + } + defer func() { + if err = response.Close(); err != nil { + glog.Errorf(ctx, `%+v`, err) + } + }() + result := response.ReadAll() + resultStrut := &ghttp.DefaultHandlerResponse{} + if gconv.Struct(result, resultStrut); resultStrut.Code != 200 { + err = errors.New(resultStrut.Message) + } else { + gconv.Struct(resultStrut.Data, target) + } + return +} +func Get(ctx context.Context, url string, target any, data ...any) (err error) { + err = doRequest(ctx, http.MethodGet, url, target, data) + return +} +func Post(ctx context.Context, url string, target any, data ...any) (err error) { + err = doRequest(ctx, http.MethodPost, url, target, data) + return +} diff --git a/jaeger/jaeger.go b/jaeger/jaeger.go new file mode 100644 index 0000000..ee0c491 --- /dev/null +++ b/jaeger/jaeger.go @@ -0,0 +1,75 @@ +package jaeger + +import ( + "context" + "encoding/json" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/net/ghttp" + "github.com/gogf/gf/v2/util/gconv" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/exporters/jaeger" + "go.opentelemetry.io/otel/sdk/resource" + "go.opentelemetry.io/otel/sdk/trace" + semconv "go.opentelemetry.io/otel/semconv/v1.21.0" + "strconv" +) + +var Tp = new(trace.TracerProvider) + +func init() { + jaegerAgent, err := g.Cfg().Get(context.Background(), "jaeger.addr") + if err != nil { + panic(err) + } + + exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(gconv.String(jaegerAgent)))) + if err != nil { + panic(err) + } + serverName, err := g.Cfg().Get(context.Background(), "consul.Name") + if err != nil { + panic(err) + } + // 创建一个 TracerProvider,并将 Jaeger exporter 设置为其处理器 + Tp = trace.NewTracerProvider( + // 使用 BatchSpanProcessor 可以提高性能,它会批量发送 span + trace.WithBatcher(exp), + // 设置资源属性,这些属性会附加到所有导出的 span 上 + trace.WithResource(resource.NewWithAttributes( + semconv.SchemaURL, + semconv.ServiceName(gconv.String(serverName)), // 服务名称,在 Jaeger UI 中会显示 + )), + ) + // 将 TracerProvider 设置为全局,方便在应用的任何地方通过 otel.Tracer() 获取 + otel.SetTracerProvider(Tp) +} +func NewTracer(r *ghttp.Request) { + // 从传入的上下文中获取 Tracer,或者直接使用全局的 + tracer := otel.Tracer(r.GetServeHandler().GetMetaTag("summary")) + _, span := tracer.Start(r.Context(), r.GetServeHandler().GetMetaTag("summary")) + defer span.End() // 非常重要:确保 span 在函数结束时被关闭 + span.SetAttributes(attribute.String("request", getParams(r))) + r.Middleware.Next() + span.SetAttributes(attribute.String("response", r.Response.BufferString())) + //span.AddEvent("Saying hello is done") +} +func getParams(r *ghttp.Request) string { + params := map[string]interface{}{} + if r.Method == "POST" { + json.Unmarshal(r.GetBody(), ¶ms) //获取raw传参 + } + if r.Method == "GET" { + r.Request.ParseForm() + form := r.Form + for k, v := range form { + if vl, e := strconv.Atoi(v[0]); e == nil { + params[k] = vl + } else { + params[k] = v[0] + } + } + } + rp, _ := json.Marshal(¶ms) + return string(rp) +} diff --git a/mongo/mongo.go b/mongo/mongo.go new file mode 100644 index 0000000..41fc717 --- /dev/null +++ b/mongo/mongo.go @@ -0,0 +1,96 @@ +package mongo + +import ( + "context" + "gitee.com/red-future---jilin-g/common/utils" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/glog" + "github.com/gogf/gf/v2/text/gstr" + "go.mongodb.org/mongo-driver/bson/primitive" + "strings" + "time" + + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" +) + +var db = new(mongo.Database) + +func init() { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + link, _ := g.Cfg().Get(context.Background(), "mongo.address") + mongoAddr := link.String() + client, err := mongo.Connect(ctx, options.Client().ApplyURI(mongoAddr)) + if err != nil { + glog.Error(ctx, "mongodb连接失败") + } + dbName := gstr.SubStr(mongoAddr, strings.LastIndex(mongoAddr, "/")+1, len(mongoAddr)) + db = client.Database(dbName) +} + +// Find 查询多条记录 +func Find(ctx context.Context, filter *primitive.M, result interface{}, collection string, opts ...*options.FindOptions) (err error) { + if err = utils.ValidStructPtr(result); err != nil { + return + } + cur, err := db.Collection(collection).Find(ctx, filter, opts...) + if err != nil { + return + } + err = cur.All(ctx, result) + return +} + +// FindOne 查询1条记录 +func FindOne(ctx context.Context, filter *primitive.M, result interface{}, collection string, opts ...*options.FindOneOptions) (err error) { + if len(*filter) == 0 { + err = gerror.New("缺少查询条件") + return + } + if err = utils.ValidStructPtr(result); err != nil { + return + } + cur := db.Collection(collection).FindOne(ctx, filter, opts...) + if err != nil { + return + } + err = cur.Decode(result) + if err == mongo.ErrNoDocuments { + err = nil + } + return +} + +// Delete 删除记录 +func Delete(ctx context.Context, filter *primitive.M, collection string, opts ...*options.DeleteOptions) (count int64, err error) { + r, err := db.Collection(collection).DeleteMany(ctx, filter, opts...) + if err != nil { + return + } + count = r.DeletedCount + return +} + +// Update 修改记录 +func Update(ctx context.Context, filter *primitive.M, update interface{}, result *mongo.UpdateResult, collection string, opts ...*options.UpdateOptions) (err error) { + if err = utils.ValidStructPtr(result); err != nil { + return + } + result, err = db.Collection(collection).UpdateMany(ctx, filter, update, opts...) + if err != nil { + return + } + return +} + +// Insert 修改记录 +func Insert(ctx context.Context, documents []interface{}, collection string, opts ...*options.InsertManyOptions) (ids []interface{}, err error) { + r, err := db.Collection(collection).InsertMany(ctx, documents, opts...) + if err != nil { + return + } + ids = r.InsertedIDs + return +} diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 0000000..5c24731 --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,53 @@ +package utils + +import ( + "fmt" + "github.com/gogf/gf/v2/errors/gcode" + "github.com/gogf/gf/v2/errors/gerror" + "reflect" + "time" +) + +// ValidStructPtr 验证是否为结构体指针 +func ValidStructPtr(req any) (err error) { + //验证请求参数必须为指针 + var ( + reflectValue reflect.Value + reflectKind reflect.Kind + ) + if v, ok := req.(reflect.Value); ok { + reflectValue = v + } else { + reflectValue = reflect.ValueOf(req) + } + + reflectKind = reflectValue.Kind() + if reflectKind != reflect.Ptr { + err = gerror.NewCode(gcode.CodeInvalidParameter, `the parameter "req" for function Find should type of *struct/*[]struct`) + } + return +} + +// GetMonthToday 获取N个月前的某日 +func GetMonthToday(t time.Time, month int) time.Time { + // today + fmt.Printf("today: [%s]\n", t) + // 判断天数范围 小于等于28天的计算,覆盖大多数情况 + if t.Day() <= 28 { + return t.AddDate(0, -month, 0) + } + // 月份的天数数组 + monthDay := [13]int{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} + // 计算目标所在日期 + target := t.AddDate(0, 0, 1-t.Day()).AddDate(0, -month, 0) + // 计算当月最大天数 + targetDay := monthDay[target.Month()] + // 计算闰年 + if target.Month() == time.February && (target.Year()%400 == 0 || (target.Year()%100 != 0 && target.Year()%4 == 0)) { + targetDay++ + } + if t.Day() > targetDay { + return target.AddDate(0, 0, targetDay-1) + } + return target.AddDate(0, 0, t.Day()-1) +}