feat: 机器列表新增运行状态 & refactor: 登录账号信息存储与context

This commit is contained in:
meilin.huang
2023-11-07 21:05:21 +08:00
parent d9adf0fd25
commit eddda41291
74 changed files with 915 additions and 652 deletions

View File

@@ -1,44 +1,47 @@
package base
import (
"context"
"mayfly-go/pkg/model"
"gorm.io/gorm"
)
// 基础application接口
type App[T any] interface {
type App[T model.ModelI] interface {
// 新增一个实体
Insert(e T) error
Insert(ctx context.Context, e T) error
// 使用指定gorm db执行主要用于事务执行
InsertWithDb(db *gorm.DB, e T) error
InsertWithDb(ctx context.Context, db *gorm.DB, e T) error
// 批量新增实体
BatchInsert(models []T) error
BatchInsert(ctx context.Context, models []T) error
// 使用指定gorm db执行主要用于事务执行
BatchInsertWithDb(db *gorm.DB, es []T) error
BatchInsertWithDb(ctx context.Context, db *gorm.DB, es []T) error
// 根据实体id更新实体信息
UpdateById(e T) error
UpdateById(ctx context.Context, e T) error
// 使用指定gorm db执行主要用于事务执行
UpdateByIdWithDb(db *gorm.DB, e T) error
UpdateByIdWithDb(ctx context.Context, db *gorm.DB, e T) error
// 根据实体主键删除实体
DeleteById(id uint64) error
DeleteById(ctx context.Context, id uint64) error
// 使用指定gorm db执行主要用于事务执行
DeleteByIdWithDb(db *gorm.DB, id uint64) error
DeleteByIdWithDb(ctx context.Context, db *gorm.DB, id uint64) error
// 根据实体条件更新参数udpateFields指定字段
Updates(cond any, udpateFields map[string]any) error
Updates(ctx context.Context, cond any, udpateFields map[string]any) error
// 根据实体条件删除实体
DeleteByCond(cond any) error
DeleteByCond(ctx context.Context, cond any) error
// 使用指定gorm db执行主要用于事务执行
DeleteByCondWithDb(db *gorm.DB, cond any) error
DeleteByCondWithDb(ctx context.Context, db *gorm.DB, cond any) error
// 根据实体id查询
GetById(e T, id uint64, cols ...string) (T, error)
@@ -62,7 +65,7 @@ type App[T any] interface {
}
// 基础application接口实现
type AppImpl[T any, R Repo[T]] struct {
type AppImpl[T model.ModelI, R Repo[T]] struct {
Repo R // repo接口
}
@@ -72,57 +75,57 @@ func (ai *AppImpl[T, R]) GetRepo() R {
}
// 新增一个实体 (单纯新增,不做其他业务逻辑处理)
func (ai *AppImpl[T, R]) Insert(e T) error {
return ai.GetRepo().Insert(e)
func (ai *AppImpl[T, R]) Insert(ctx context.Context, e T) error {
return ai.GetRepo().Insert(ctx, e)
}
// 使用指定gorm db执行主要用于事务执行
func (ai *AppImpl[T, R]) InsertWithDb(db *gorm.DB, e T) error {
return ai.GetRepo().InsertWithDb(db, e)
func (ai *AppImpl[T, R]) InsertWithDb(ctx context.Context, db *gorm.DB, e T) error {
return ai.GetRepo().InsertWithDb(ctx, db, e)
}
// 批量新增实体 (单纯新增,不做其他业务逻辑处理)
func (ai *AppImpl[T, R]) BatchInsert(es []T) error {
return ai.GetRepo().BatchInsert(es)
func (ai *AppImpl[T, R]) BatchInsert(ctx context.Context, es []T) error {
return ai.GetRepo().BatchInsert(ctx, es)
}
// 使用指定gorm db执行主要用于事务执行
func (ai *AppImpl[T, R]) BatchInsertWithDb(db *gorm.DB, es []T) error {
return ai.GetRepo().BatchInsertWithDb(db, es)
func (ai *AppImpl[T, R]) BatchInsertWithDb(ctx context.Context, db *gorm.DB, es []T) error {
return ai.GetRepo().BatchInsertWithDb(ctx, db, es)
}
// 根据实体id更新实体信息 (单纯更新,不做其他业务逻辑处理)
func (ai *AppImpl[T, R]) UpdateById(e T) error {
return ai.GetRepo().UpdateById(e)
func (ai *AppImpl[T, R]) UpdateById(ctx context.Context, e T) error {
return ai.GetRepo().UpdateById(ctx, e)
}
// 使用指定gorm db执行主要用于事务执行
func (ai *AppImpl[T, R]) UpdateByIdWithDb(db *gorm.DB, e T) error {
return ai.GetRepo().UpdateByIdWithDb(db, e)
func (ai *AppImpl[T, R]) UpdateByIdWithDb(ctx context.Context, db *gorm.DB, e T) error {
return ai.GetRepo().UpdateByIdWithDb(ctx, db, e)
}
// 根据实体条件更新参数udpateFields指定字段 (单纯更新,不做其他业务逻辑处理)
func (ai *AppImpl[T, R]) Updates(cond any, udpateFields map[string]any) error {
func (ai *AppImpl[T, R]) Updates(ctx context.Context, cond any, udpateFields map[string]any) error {
return ai.GetRepo().Updates(cond, udpateFields)
}
// 根据实体主键删除实体 (单纯删除实体,不做其他业务逻辑处理)
func (ai *AppImpl[T, R]) DeleteById(id uint64) error {
return ai.GetRepo().DeleteById(id)
func (ai *AppImpl[T, R]) DeleteById(ctx context.Context, id uint64) error {
return ai.GetRepo().DeleteById(ctx, id)
}
func (ai *AppImpl[T, R]) DeleteByIdWithDb(db *gorm.DB, id uint64) error {
return ai.GetRepo().DeleteByCondWithDb(db, id)
func (ai *AppImpl[T, R]) DeleteByIdWithDb(ctx context.Context, db *gorm.DB, id uint64) error {
return ai.GetRepo().DeleteByCondWithDb(ctx, db, id)
}
// 根据指定条件删除实体 (单纯删除实体,不做其他业务逻辑处理)
func (ai *AppImpl[T, R]) DeleteByCond(cond any) error {
return ai.GetRepo().DeleteByCond(cond)
func (ai *AppImpl[T, R]) DeleteByCond(ctx context.Context, cond any) error {
return ai.GetRepo().DeleteByCond(ctx, cond)
}
// 使用指定gorm db执行主要用于事务执行
func (ai *AppImpl[T, R]) DeleteByCondWithDb(db *gorm.DB, cond any) error {
return ai.GetRepo().DeleteByCondWithDb(db, cond)
func (ai *AppImpl[T, R]) DeleteByCondWithDb(ctx context.Context, db *gorm.DB, cond any) error {
return ai.GetRepo().DeleteByCondWithDb(ctx, db, cond)
}
// 根据实体id查询

View File

@@ -1,47 +1,50 @@
package base
import (
"context"
"mayfly-go/pkg/biz"
"mayfly-go/pkg/contextx"
"mayfly-go/pkg/gormx"
"mayfly-go/pkg/model"
"gorm.io/gorm"
)
// 基础repo接口
type Repo[T any] interface {
type Repo[T model.ModelI] interface {
// 新增一个实体
Insert(e T) error
Insert(ctx context.Context, e T) error
// 使用指定gorm db执行主要用于事务执行
InsertWithDb(db *gorm.DB, e T) error
InsertWithDb(ctx context.Context, db *gorm.DB, e T) error
// 批量新增实体
BatchInsert(models []T) error
BatchInsert(ctx context.Context, models []T) error
// 使用指定gorm db执行主要用于事务执行
BatchInsertWithDb(db *gorm.DB, es []T) error
BatchInsertWithDb(ctx context.Context, db *gorm.DB, es []T) error
// 根据实体id更新实体信息
UpdateById(e T) error
UpdateById(ctx context.Context, e T) error
// 使用指定gorm db执行主要用于事务执行
UpdateByIdWithDb(db *gorm.DB, e T) error
UpdateByIdWithDb(ctx context.Context, db *gorm.DB, e T) error
// 根据实体主键删除实体
DeleteById(id uint64) error
DeleteById(ctx context.Context, id uint64) error
// 使用指定gorm db执行主要用于事务执行
DeleteByIdWithDb(db *gorm.DB, id uint64) error
DeleteByIdWithDb(ctx context.Context, db *gorm.DB, id uint64) error
// 根据实体条件更新参数udpateFields指定字段
Updates(cond any, udpateFields map[string]any) error
// 根据实体条件删除实体
DeleteByCond(cond any) error
DeleteByCond(ctx context.Context, cond any) error
// 使用指定gorm db执行主要用于事务执行
DeleteByCondWithDb(db *gorm.DB, cond any) error
DeleteByCondWithDb(ctx context.Context, db *gorm.DB, cond any) error
// 根据实体id查询
GetById(e T, id uint64, cols ...string) error
@@ -66,52 +69,58 @@ type Repo[T any] interface {
}
// 基础repo接口
type RepoImpl[T any] struct {
type RepoImpl[T model.ModelI] struct {
M any // 模型实例
}
func (br *RepoImpl[T]) Insert(e T) error {
return gormx.Insert(e)
func (br *RepoImpl[T]) Insert(ctx context.Context, e T) error {
return gormx.Insert(br.setBaseInfo(ctx, e))
}
func (br *RepoImpl[T]) InsertWithDb(db *gorm.DB, e T) error {
return gormx.InsertWithDb(db, e)
func (br *RepoImpl[T]) InsertWithDb(ctx context.Context, db *gorm.DB, e T) error {
return gormx.InsertWithDb(db, br.setBaseInfo(ctx, e))
}
func (br *RepoImpl[T]) BatchInsert(es []T) error {
func (br *RepoImpl[T]) BatchInsert(ctx context.Context, es []T) error {
for _, e := range es {
br.setBaseInfo(ctx, e)
}
return gormx.BatchInsert(es)
}
// 使用指定gorm db执行主要用于事务执行
func (br *RepoImpl[T]) BatchInsertWithDb(db *gorm.DB, es []T) error {
func (br *RepoImpl[T]) BatchInsertWithDb(ctx context.Context, db *gorm.DB, es []T) error {
for _, e := range es {
br.setBaseInfo(ctx, e)
}
return gormx.BatchInsertWithDb(db, es)
}
func (br *RepoImpl[T]) UpdateById(e T) error {
return gormx.UpdateById(e)
func (br *RepoImpl[T]) UpdateById(ctx context.Context, e T) error {
return gormx.UpdateById(br.setBaseInfo(ctx, e))
}
func (br *RepoImpl[T]) UpdateByIdWithDb(db *gorm.DB, e T) error {
return gormx.UpdateByIdWithDb(db, e)
func (br *RepoImpl[T]) UpdateByIdWithDb(ctx context.Context, db *gorm.DB, e T) error {
return gormx.UpdateByIdWithDb(db, br.setBaseInfo(ctx, e))
}
func (br *RepoImpl[T]) Updates(cond any, udpateFields map[string]any) error {
return gormx.Updates(cond, udpateFields)
}
func (br *RepoImpl[T]) DeleteById(id uint64) error {
func (br *RepoImpl[T]) DeleteById(ctx context.Context, id uint64) error {
return gormx.DeleteById(br.getModel(), id)
}
func (br *RepoImpl[T]) DeleteByIdWithDb(db *gorm.DB, id uint64) error {
func (br *RepoImpl[T]) DeleteByIdWithDb(ctx context.Context, db *gorm.DB, id uint64) error {
return gormx.DeleteByCondWithDb(db, br.getModel(), id)
}
func (br *RepoImpl[T]) DeleteByCond(cond any) error {
func (br *RepoImpl[T]) DeleteByCond(ctx context.Context, cond any) error {
return gormx.DeleteByCond(br.getModel(), cond)
}
func (br *RepoImpl[T]) DeleteByCondWithDb(db *gorm.DB, cond any) error {
func (br *RepoImpl[T]) DeleteByCondWithDb(ctx context.Context, db *gorm.DB, cond any) error {
return gormx.DeleteByCondWithDb(db, br.getModel(), cond)
}
@@ -147,3 +156,11 @@ func (br *RepoImpl[T]) getModel() any {
biz.IsTrue(br.M != nil, "base.RepoImpl的M字段不能为空")
return br.M
}
// 从上下文获取登录账号信息,并赋值至实体
func (br *RepoImpl[T]) setBaseInfo(ctx context.Context, e T) T {
if la := contextx.GetLoginAccount(ctx); la != nil {
e.SetBaseInfo(la)
}
return e
}

View File

@@ -21,9 +21,12 @@ func WithLoginAccount(ctx context.Context, la *model.LoginAccount) context.Conte
return context.WithValue(ctx, LoginAccountKey, la)
}
// 从context中获取登录账号信息
// 从context中获取登录账号信息不存在返回nil
func GetLoginAccount(ctx context.Context) *model.LoginAccount {
return ctx.Value(LoginAccountKey).(*model.LoginAccount)
if la, ok := ctx.Value(LoginAccountKey).(*model.LoginAccount); ok {
return la
}
return nil
}
func NewTraceId() context.Context {
@@ -31,10 +34,13 @@ func NewTraceId() context.Context {
}
func WithTraceId(ctx context.Context) context.Context {
return context.WithValue(ctx, TraceIdKey, stringx.Rand(16))
return context.WithValue(ctx, TraceIdKey, stringx.RandByChars(16, stringx.Nums+stringx.LowerChars))
}
// 从context中获取traceId
func GetTraceId(ctx context.Context) string {
return ctx.Value(TraceIdKey).(string)
if val, ok := ctx.Value(TraceIdKey).(string); ok {
return val
}
return ""
}

View File

@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"log/slog"
"mayfly-go/pkg/contextx"
"mayfly-go/pkg/utils/runtimex"
"path/filepath"
"runtime"
@@ -47,7 +48,7 @@ func Debugf(format string, args ...any) {
Log(context.Background(), slog.LevelDebug, fmt.Sprintf(format, args...))
}
func DebugWithFields(msg string, mapFields map[string]any) {
func DebugWithFields(ctx context.Context, msg string, mapFields map[string]any) {
Log(context.Background(), slog.LevelDebug, msg, map2Attrs(mapFields)...)
}
@@ -68,8 +69,8 @@ func Infof(format string, args ...any) {
Log(context.Background(), slog.LevelInfo, fmt.Sprintf(format, args...))
}
func InfoWithFields(msg string, mapFields map[string]any) {
Log(context.Background(), slog.LevelInfo, msg, map2Attrs(mapFields)...)
func InfoWithFields(ctx context.Context, msg string, mapFields map[string]any) {
Log(ctx, slog.LevelInfo, msg, map2Attrs(mapFields)...)
}
func Warn(msg string, args ...any) {
@@ -97,8 +98,8 @@ func ErrorTrace(msg string, err error) {
Log(context.Background(), slog.LevelError, fmt.Sprintf(msg+" %s\n%s", err.Error(), runtimex.StatckStr(2, 10)))
}
func ErrorWithFields(msg string, mapFields map[string]any) {
Log(context.Background(), slog.LevelError, msg, map2Attrs(mapFields)...)
func ErrorWithFields(ctx context.Context, msg string, mapFields map[string]any) {
Log(ctx, slog.LevelError, msg, map2Attrs(mapFields)...)
}
func Panic(msg string, args ...any) {
@@ -120,6 +121,10 @@ func Log(ctx context.Context, level slog.Level, msg string, args ...any) {
func getCommonAttr(ctx context.Context, level slog.Level) []any {
commonAttrs := make([]any, 0)
// 尝试从上下文获取traceId若存在则记录
if traceId := contextx.GetTraceId(ctx); traceId != "" {
commonAttrs = append(commonAttrs, "tid", traceId)
}
// 如果系统配置添加方法信息或者为错误级别时则 记录方法信息及行号
if GetConfig().AddSource || level == slog.LevelError {
// skip [runtime.Callers, getCommonAttr, appendCommonAttr, logx.Log, logx.Info|Debug|Warn|Error..]

View File

@@ -12,6 +12,15 @@ const (
ModelUndeleted int8 = 0
)
// 实体接口
type ModelI interface {
// 使用当前登录账号信息设置实体结构体的基础信息
//
// 如创建时间,修改时间,创建者,修改者信息
SetBaseInfo(account *LoginAccount)
}
// 含有删除字段模型
type DeletedModel struct {
Id uint64 `json:"id"`
@@ -19,6 +28,13 @@ type DeletedModel struct {
DeleteTime *time.Time `json:"-"`
}
func (m *DeletedModel) SetBaseInfo(account *LoginAccount) {
isCreate := m.Id == 0
if isCreate {
m.IsDeleted = ModelUndeleted
}
}
// 基础实体模型,数据表最基础字段,每张表必备字段
type Model struct {
DeletedModel

View File

@@ -2,6 +2,7 @@ package req
import (
"fmt"
"mayfly-go/pkg/contextx"
"mayfly-go/pkg/errorx"
"mayfly-go/pkg/logx"
"mayfly-go/pkg/utils/anyx"
@@ -54,7 +55,7 @@ func LogHandler(rc *Ctx) error {
req := rc.GinCtx.Request
attrMap[req.Method] = req.URL.Path
if la := rc.LoginAccount; la != nil {
if la := contextx.GetLoginAccount(rc.MetaCtx); la != nil {
attrMap["uid"] = la.Id
attrMap["uname"] = la.Username
}
@@ -93,10 +94,10 @@ func LogHandler(rc *Ctx) error {
}
if err := rc.Err; err != nil {
logx.ErrorWithFields(logMsg, attrMap)
logx.ErrorWithFields(rc.MetaCtx, logMsg, attrMap)
return nil
}
logx.InfoWithFields(logMsg, attrMap)
logx.InfoWithFields(rc.MetaCtx, logMsg, attrMap)
return nil
}

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"mayfly-go/pkg/cache"
"mayfly-go/pkg/config"
"mayfly-go/pkg/contextx"
"mayfly-go/pkg/errorx"
"mayfly-go/pkg/model"
"mayfly-go/pkg/rediscli"
@@ -60,12 +61,10 @@ func PermissionHandler(rc *Ctx) error {
return errorx.PermissionErr
}
}
if rc.LoginAccount == nil {
rc.LoginAccount = &model.LoginAccount{
Id: userId,
Username: userName,
}
}
rc.MetaCtx = contextx.WithLoginAccount(rc.MetaCtx, &model.LoginAccount{
Id: userId,
Username: userName,
})
return nil
}

View File

@@ -1,7 +1,10 @@
package req
import (
"context"
"io"
"mayfly-go/pkg/biz"
"mayfly-go/pkg/contextx"
"mayfly-go/pkg/ginx"
"mayfly-go/pkg/model"
"mayfly-go/pkg/utils/assert"
@@ -16,12 +19,13 @@ type HandlerFunc func(*Ctx)
type Ctx struct {
Conf *Conf // 请求配置
GinCtx *gin.Context // gin context
LoginAccount *model.LoginAccount // 登录账号信息只有校验token后才会有值
ReqParam any // 请求参数,主要用于记录日志
ResData any // 响应结果
Err any // 请求错误
timed int64 // 执行时间
GinCtx *gin.Context // gin context
ReqParam any // 请求参数,主要用于记录日志
ResData any // 响应结果
Err any // 请求错误
timed int64 // 执行时间
MetaCtx context.Context // 元数据上下文信息,如登录账号(只有校验token后才会有值)traceId等
}
func (rc *Ctx) Handle(handler HandlerFunc) {
@@ -59,6 +63,15 @@ func (rc *Ctx) Download(reader io.Reader, filename string) {
ginx.Download(rc.GinCtx, reader, filename)
}
// 获取当前登录账号信息,不存在则会报错。
//
// 若不需要报错则使用contextx.GetLoginAccount方法
func (rc *Ctx) GetLoginAccount() *model.LoginAccount {
la := contextx.GetLoginAccount(rc.MetaCtx)
biz.IsTrue(la != nil, "获取登录账号信息失败, 请确认该接口是否通过鉴权")
return la
}
func (rc *Ctx) WithConf(conf *Conf) *Ctx {
rc.Conf = conf
return rc
@@ -87,7 +100,7 @@ func (rc *Ctx) GetLogInfo() *LogInfo {
}
func NewCtxWithGin(g *gin.Context) *Ctx {
return &Ctx{GinCtx: g}
return &Ctx{GinCtx: g, MetaCtx: contextx.NewTraceId()}
}
// 处理器拦截器函数

View File

@@ -13,6 +13,11 @@ func ToMap(jsonStr string) map[string]any {
return ToMapByBytes([]byte(jsonStr))
}
// json字符串转结构体
func To[T any](jsonStr string, res T) (T, error) {
return res, json.Unmarshal([]byte(jsonStr), &res)
}
// json字节数组转map
func ToMapByBytes(bytes []byte) map[string]any {
var res map[string]any

View File

@@ -5,11 +5,18 @@ import (
"time"
)
const randChar = "0123456789abcdefghigklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
const Nums = "0123456789"
const LowerChars = "abcdefghigklmnopqrstuvwxyz"
const UpperChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
// 生成随机字符串
func Rand(l int) string {
strList := []byte(randChar)
return RandByChars(l, Nums+LowerChars+UpperChars)
}
// 根据传入的chars随机生成指定位数的字符串
func RandByChars(l int, chars string) string {
strList := []byte(chars)
result := []byte{}
i := 0

View File

@@ -0,0 +1,26 @@
package stringx
import (
"fmt"
"mayfly-go/pkg/utils/collx"
"strings"
"testing"
)
func TestTemplateParse(t *testing.T) {
tmpl := `
{{if gt .cpu 10*5}}
当前服务器[{{.asset.host}}]cpu使用率为{{.cpu}}
{{end}}
`
vars := collx.M{
"cpu": 60,
"asset": collx.M{
"host": "localhost:121",
},
}
res, _ := TemplateParse(tmpl, vars)
res2 := strings.TrimSpace(res)
fmt.Println(res2)
}