feat: sql解析器替换、工单统一由‘我的流程’发起、流程定义支持自定义条件触发审批、资源隐藏编号、model支持物理删除等

This commit is contained in:
meilin.huang
2024-10-16 17:24:50 +08:00
parent 43edef412c
commit e135e4ce64
170 changed files with 397197 additions and 1251 deletions

View File

@@ -4,7 +4,7 @@ import "fmt"
const (
AppName = "mayfly-go"
Version = "v1.8.8"
Version = "v1.8.9"
)
func GetAppInfo() string {

View File

@@ -9,7 +9,7 @@ import (
"gorm.io/gorm"
)
// 伪删除之删除过滤条件
// 伪删除之删除过滤条件
func UndeleteScope(db *gorm.DB) *gorm.DB {
return db.Where(model.DeletedColumn, model.ModelUndeleted)
}
@@ -17,30 +17,29 @@ func UndeleteScope(db *gorm.DB) *gorm.DB {
// 根据id获取实体对象。model需为指针类型需要将查询出来的值赋值给model
//
// 若error不为nil则为不存在该记录
// @param modelP 数据库映射实体模型
func GetById(dbModel any, id uint64, cols ...string) error {
return NewQuery(dbModel, model.NewCond().Columns(cols...).Eq(model.IdColumn, id)).GenGdb().Scopes(UndeleteScope).First(dbModel).Error
// @param dbModel 数据库映射实体模型
func GetById(dbModel model.ModelI, id uint64, cols ...string) error {
return NewQuery(dbModel, model.NewCond().Columns(cols...).Eq(model.IdColumn, id)).GenGdb().First(dbModel).Error
}
// 根据model获取单个实体对象
//
// 若 error不为nil则为不存在该记录
// @param cond 查询条件
func GetByCond(dbModel any, cond *model.QueryCond) error {
return NewQuery(dbModel, cond).GenGdb().Scopes(UndeleteScope).First(cond.GetDest()).Error
func GetByCond(dbModel model.ModelI, cond *model.QueryCond) error {
return NewQuery(dbModel, cond).GenGdb().First(cond.GetDest()).Error
}
// 根据条件cond获取指定model表统计数量
func CountByCond(dbModel any, cond *model.QueryCond) int64 {
func CountByCond(dbModel model.ModelI, cond *model.QueryCond) int64 {
var count int64
NewQuery(dbModel, cond).GenGdb().Scopes(UndeleteScope).Count(&count)
NewQuery(dbModel, cond).GenGdb().Count(&count)
return count
}
// PageQuery 根据查询条件分页查询数据
// PageQuery 根据查询条件分页查询数据,若需要伪删除过滤,则自行过滤-调用q.Undeleted()
// 若未指定查询列则查询列以toModels字段为准
func PageQuery[T any](q *Query, pageParam *model.PageParam, toModels T) (*model.PageResult[T], error) {
q.Undeleted()
gdb := q.GenGdb()
var count int64
err := gdb.Count(&count).Error
@@ -61,13 +60,13 @@ func PageQuery[T any](q *Query, pageParam *model.PageParam, toModels T) (*model.
}
// PageByCond 根据指定查询条件分页查询数据
func PageByCond[T any](dbModel any, cond *model.QueryCond, pageParam *model.PageParam, toModels T) (*model.PageResult[T], error) {
func PageByCond[T any](dbModel model.ModelI, cond *model.QueryCond, pageParam *model.PageParam, toModels T) (*model.PageResult[T], error) {
return PageQuery(NewQuery(dbModel, cond), pageParam, toModels)
}
// SelectByCond 根据条件查询结果集
func SelectByCond(dbModel any, cond *model.QueryCond) error {
return NewQuery(dbModel, cond).GenGdb().Scopes(UndeleteScope).Find(cond.GetDest()).Error
func SelectByCond(dbModel model.ModelI, cond *model.QueryCond) error {
return NewQuery(dbModel, cond).GenGdb().Find(cond.GetDest()).Error
}
// SelectBySql 根据sql查询数据
@@ -81,43 +80,43 @@ func ExecSql(sql string, params ...any) error {
// 插入model
// @param model 数据库映射实体模型
func Insert(model any) error {
func Insert(model model.ModelI) error {
return InsertWithDb(global.Db, model)
}
// 使用指定gormDb插入model
func InsertWithDb(db *gorm.DB, model any) error {
func InsertWithDb(db *gorm.DB, model model.ModelI) error {
return db.Create(model).Error
}
// 批量插入
func BatchInsert[T any](models []T) error {
func BatchInsert[T model.ModelI](models []T) error {
return BatchInsertWithDb[T](global.Db, models)
}
// 批量插入
func BatchInsertWithDb[T any](db *gorm.DB, models []T) error {
func BatchInsertWithDb[T model.ModelI](db *gorm.DB, models []T) error {
return db.CreateInBatches(models, len(models)).Error
}
// 根据id更新model更新字段为model中不为空的值即int类型不为0ptr类型不为nil这类字段值
// @param model 数据库映射实体模型
func UpdateById(model any, columns ...string) error {
func UpdateById(model model.ModelI, columns ...string) error {
return UpdateByIdWithDb(global.Db, model, columns...)
}
func UpdateByIdWithDb(db *gorm.DB, model any, columns ...string) error {
func UpdateByIdWithDb(db *gorm.DB, model model.ModelI, columns ...string) error {
return db.Model(model).Select(columns).Updates(model).Error
}
// UpdateByCondWithDb 使用指定gorm.DB更新满足条件的数据(model的主键值需为空否则会带上主键条件)
func UpdateByCond(dbModel any, values any, cond *model.QueryCond) error {
func UpdateByCond(dbModel model.ModelI, values any, cond *model.QueryCond) error {
return UpdateByCondWithDb(global.Db, dbModel, values, cond)
}
// UpdateByCondWithDb 使用指定gorm.DB更新满足条件的数据(model的主键值需为空否则会带上主键条件)
// @values values must be a struct or map.
func UpdateByCondWithDb(db *gorm.DB, dbModel any, values any, cond *model.QueryCond) error {
func UpdateByCondWithDb(db *gorm.DB, dbModel model.ModelI, values any, cond *model.QueryCond) error {
gormDb := db.Model(dbModel).Select(cond.GetSelectColumns())
setGdbWhere(gormDb, cond)
return gormDb.Updates(values).Error
@@ -125,12 +124,12 @@ func UpdateByCondWithDb(db *gorm.DB, dbModel any, values any, cond *model.QueryC
// 根据id删除model
// @param model 数据库映射实体模型
func DeleteById(model_ any, id ...uint64) error {
func DeleteById(model_ model.ModelI, id ...uint64) error {
return DeleteByIdWithDb(global.Db, model_, id...)
}
// 根据id使用指定gromDb删除
func DeleteByIdWithDb(db *gorm.DB, model_ any, id ...uint64) error {
func DeleteByIdWithDb(db *gorm.DB, model_ model.ModelI, id ...uint64) error {
return DeleteByCondWithDb(db, model_, model.NewCond().In(model.IdColumn, id))
}
@@ -138,7 +137,7 @@ func DeleteByIdWithDb(db *gorm.DB, model_ any, id ...uint64) error {
//
// @param dbModel 数据库映射实体模型
// @param cond 条件
func DeleteByCond(dbModel any, cond *model.QueryCond) error {
func DeleteByCond(dbModel model.ModelI, cond *model.QueryCond) error {
return DeleteByCondWithDb(global.Db, dbModel, cond)
}
@@ -146,8 +145,12 @@ func DeleteByCond(dbModel any, cond *model.QueryCond) error {
//
// @param dbModel 数据库映射实体模型
// @param cond 条件
func DeleteByCondWithDb(db *gorm.DB, dbModel any, cond *model.QueryCond) error {
return setGdbWhere(db.Model(dbModel), cond).Updates(getDeleteColumnValue()).Error
func DeleteByCondWithDb(db *gorm.DB, dbModel model.ModelI, cond *model.QueryCond) error {
gdb := setGdbWhere(db.Model(dbModel), cond)
if !dbModel.LogicDelete() {
return gdb.Delete(dbModel).Error
}
return gdb.Updates(getDeleteColumnValue()).Error
}
func getDeleteColumnValue() map[string]any {

View File

@@ -9,7 +9,7 @@ import (
)
type Query struct {
dbModel any // 数据库模型
dbModel model.ModelI // 数据库模型
table string
joins string // join 类似 left join emails on emails.user_id = users.id
@@ -17,8 +17,13 @@ type Query struct {
}
// NewQuery 构建查询条件
func NewQuery(dbModel any, cond *model.QueryCond) *Query {
return &Query{dbModel: dbModel, cond: cond}
func NewQuery(dbModel model.ModelI, cond *model.QueryCond) *Query {
q := &Query{dbModel: dbModel, cond: cond}
// 如果是逻辑删除,默认加上伪删除过滤条件
if dbModel.LogicDelete() {
q.Undeleted()
}
return q
}
func NewQueryWithTableName(tableName string) *Query {
@@ -84,5 +89,6 @@ func setGdbWhere(gdb *gorm.DB, cond *model.QueryCond) *gorm.DB {
for i, v := range cond.GetWheres() {
gdb.Where(i, v...)
}
return gdb
}

View File

@@ -62,7 +62,7 @@ func DebugWithFields(ctx context.Context, msg string, mapFields map[string]any)
// debug记录并将堆栈信息添加至msg里默认记录10个堆栈信息
func DebugTrace(msg string, err error) {
Log(context.Background(), slog.LevelDebug, fmt.Sprintf(msg+" %s\n%s", err.Error(), runtimex.StatckStr(2, 10)))
Log(context.Background(), slog.LevelDebug, fmt.Sprintf(msg+" %s\n%s", err.Error(), runtimex.StackStr(2, 10)))
}
func Info(msg string, args ...any) {
@@ -132,7 +132,7 @@ func ErrorTrace(msg string, err any) {
default:
errMsg = fmt.Sprintf("%v", t)
}
Log(context.Background(), slog.LevelError, fmt.Sprintf(msg+"\n%s\n%s", errMsg, runtimex.StatckStr(2, 20)))
Log(context.Background(), slog.LevelError, fmt.Sprintf(msg+"\n%s\n%s", errMsg, runtimex.StackStr(2, 20)))
}
func ErrorWithFields(ctx context.Context, msg string, mapFields map[string]any) {

View File

@@ -5,3 +5,7 @@ type LoginAccount struct {
Username string
Token string
}
func (la *LoginAccount) GetAesKey() string {
return la.Token[:24]
}

View File

@@ -4,6 +4,8 @@ import (
"database/sql/driver"
"encoding/json"
"time"
"github.com/may-fly/cast"
)
type IdGenType int
@@ -35,6 +37,9 @@ type ModelI interface {
//
// 如创建时间,修改时间,创建者,修改者信息等
FillBaseInfo(idGenType IdGenType, account *LoginAccount)
// LogicDelete 是否为逻辑删除
LogicDelete() bool
}
type IdModel struct {
@@ -57,6 +62,10 @@ func (m *IdModel) FillBaseInfo(idGenType IdGenType, account *LoginAccount) {
m.SetId(GetIdByGenType(idGenType))
}
func (m *IdModel) LogicDelete() bool {
return false
}
// 含有删除字段模型
type DeletedModel struct {
IdModel
@@ -71,6 +80,32 @@ func (m *DeletedModel) FillBaseInfo(idGenType IdGenType, account *LoginAccount)
}
}
func (m *DeletedModel) LogicDelete() bool {
return true
}
// CreateModelNLD 含有创建等信息,但不包含逻辑删除信息
type CreateModelNLD struct {
IdModel
CreateTime *time.Time `json:"createTime"`
CreatorId uint64 `json:"creatorId"`
Creator string `json:"creator"`
}
func (m *CreateModelNLD) FillBaseInfo(idGenType IdGenType, account *LoginAccount) {
if !m.IsCreate() {
return
}
m.IdModel.FillBaseInfo(idGenType, account)
nowTime := time.Now()
m.CreateTime = &nowTime
if account != nil {
m.CreatorId = account.Id
m.Creator = account.Username
}
}
// 含有删除、创建字段模型
type CreateModel struct {
DeletedModel
@@ -93,6 +128,38 @@ func (m *CreateModel) FillBaseInfo(idGenType IdGenType, account *LoginAccount) {
}
}
// 基础实体模型,数据表最基础字段,不包含逻辑删除
type ModelNLD struct {
CreateModelNLD
UpdateTime *time.Time `json:"updateTime"`
ModifierId uint64 `json:"modifierId"`
Modifier string `json:"modifier"`
}
// 设置基础信息. 如创建时间,修改时间,创建者,修改者信息
func (m *ModelNLD) FillBaseInfo(idGenType IdGenType, account *LoginAccount) {
nowTime := time.Now()
isCreate := m.IsCreate()
if isCreate {
m.CreateTime = &nowTime
m.IdModel.FillBaseInfo(idGenType, account)
}
m.UpdateTime = &nowTime
if account == nil {
return
}
id := account.Id
name := account.Username
if isCreate {
m.CreatorId = id
m.Creator = name
}
m.Modifier = name
m.ModifierId = id
}
// 基础实体模型,数据表最基础字段,尽量每张表都包含这些字段
type Model struct {
CreateModel
@@ -153,3 +220,25 @@ func (s *Slice[T]) Scan(value any) error {
func (s Slice[T]) Value() (driver.Value, error) {
return json.Marshal(s)
}
// 带有额外其他信息字段的结构体
type ExtraData struct {
Extra Map[string, any] `json:"extra"`
}
// SetExtraValue 设置额外信息字段值
func (m *ExtraData) SetExtraValue(key string, val any) {
if m.Extra != nil {
m.Extra[key] = val
} else {
m.Extra = Map[string, any]{key: val}
}
}
// GetExtraString 获取额外信息中的string类型字段值
func (e ExtraData) GetExtraString(key string) string {
if e.Extra == nil {
return ""
}
return cast.ToString(e.Extra[key])
}

View File

@@ -10,7 +10,8 @@ type QueryCond struct {
selectColumns []string // 查询的列信息
condModel any // 条件模型
wheres map[string][]any
wheres map[string][]any
orderBy []string
dest any // 结果集指针
@@ -120,11 +121,11 @@ func (q *QueryCond) Le(column string, val any) *QueryCond {
}
// And条件
func (q *QueryCond) And(column string, val ...any) *QueryCond {
func (q *QueryCond) And(columnOrQuery string, val ...any) *QueryCond {
if q.wheres == nil {
q.wheres = make(map[string][]any)
}
q.wheres[column] = val
q.wheres[columnOrQuery] = val
return q
}

View File

@@ -83,7 +83,7 @@ func LogHandler(rc *Ctx) error {
}
attrMap["error"] = rc.Err
// 跳过log_handler等相关堆栈
attrMap["stacktrace"] = runtimex.StatckStr(5, nFrames)
attrMap["stacktrace"] = runtimex.StackStr(5, nFrames)
}
} else {
// 处理文本格式日志信息
@@ -134,6 +134,6 @@ func getErrMsg(rc *Ctx, err any) string {
errMsg = fmt.Sprintf("\n<-e errMsg: %s", t)
}
// 加上堆栈信息
errMsg += fmt.Sprintf("\n<-stacktrace: %s", runtimex.StatckStr(5, nFrames))
errMsg += fmt.Sprintf("\n<-stacktrace: %s", runtimex.StackStr(5, nFrames))
return (msg + errMsg)
}

View File

@@ -4,7 +4,6 @@ import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/des"
"crypto/md5"
"crypto/rand"
"crypto/rsa"
@@ -13,9 +12,9 @@ import (
"encoding/hex"
"encoding/pem"
"errors"
"fmt"
"mayfly-go/pkg/cache"
"mayfly-go/pkg/logx"
"mayfly-go/pkg/model"
"os"
"golang.org/x/crypto/bcrypt"
@@ -247,6 +246,7 @@ func AesDecrypt(data []byte, key []byte) ([]byte, error) {
if err != nil {
return nil, err
}
//获取块的大小
blockSize := block.BlockSize()
//使用cbc
@@ -281,65 +281,9 @@ func AesDecryptBase64(data string, key []byte) ([]byte, error) {
return AesDecrypt(dataByte, key)
}
// DES加密函数
func DesEncrypt(data, key []byte) (string, error) {
block, err := des.NewTripleDESCipher(key)
if err != nil {
return "", err
}
// 对数据进行填充
data = pkcs7Padding(data, des.BlockSize)
// 创建一个初始化向量
iv := make([]byte, des.BlockSize)
if _, err := rand.Read(iv); err != nil {
return "", err
}
// 创建加密器
mode := cipher.NewCBCEncrypter(block, iv)
// 加密
encrypted := make([]byte, len(data))
mode.CryptBlocks(encrypted, data)
// 将IV和加密数据组合
result := append(iv, encrypted...)
// 使用Base64编码
return base64.StdEncoding.EncodeToString(result), nil
}
func DesDecryptByToken(data string, token string) (string, error) {
key := []byte(token[:24])
return DesDecrypt(data, key)
}
func DesDecrypt(data string, key []byte) (string, error) {
// Base64解码
ciphertext, err := base64.StdEncoding.DecodeString(data)
if err != nil {
return "", err
}
block, err := des.NewTripleDESCipher(key)
if err != nil {
return "", err
}
// 确保密文长度正确
if len(ciphertext) < des.BlockSize {
return "", fmt.Errorf("ciphertext too short")
}
// 提取IV
iv := ciphertext[:des.BlockSize]
ciphertext = ciphertext[des.BlockSize:]
// 创建解密器
mode := cipher.NewCBCDecrypter(block, iv)
// 解密仍然用已存在的切片接收结果,无需重新创建切片
mode.CryptBlocks(ciphertext, ciphertext)
// 去除填充
res, err := pkcs7UnPadding(ciphertext)
func AesDecryptByLa(data string, la *model.LoginAccount) (string, error) {
key := []byte(la.GetAesKey())
res, err := AesDecryptBase64(data, key)
return string(res), err
}

View File

@@ -18,18 +18,3 @@ func TestAesEncrypt(t *testing.T) {
t.Log(string(decrypt))
}
func TestDes(t *testing.T) {
key := []byte("eyJhbGciOiJIUzI1NiIsInR5")
data := []byte("SELECT * FROM \"instruct\" OFFSET 0 LIMIT 25;")
encrypt, err := DesEncrypt(data, key)
if err != nil {
t.Error(err)
}
t.Log("encrypt", encrypt)
decrypt, err := DesDecrypt(encrypt, key)
if err != nil {
t.Error(err)
}
t.Log("decrypt", decrypt)
}

View File

@@ -7,11 +7,11 @@ import (
"strings"
)
// 获取指定堆栈描述信息
// StackStr 获取指定堆栈描述信息
//
// @param skip: 跳过堆栈个数
// @param nFrames: 需要描述的堆栈个数
func StatckStr(skip, nFrames int) string {
func StackStr(skip, nFrames int) string {
pcs := make([]uintptr, nFrames+1)
n := runtime.Callers(skip+1, pcs)
if n == 0 {

View File

@@ -2,7 +2,10 @@ package stringx
import (
"math/rand"
"strings"
"time"
"github.com/google/uuid"
)
const Nums = "0123456789"
@@ -14,6 +17,11 @@ func Rand(l int) string {
return RandByChars(l, Nums+LowerChars+UpperChars)
}
// RandUUID
func RandUUID() string {
return strings.Replace(uuid.New().String(), "-", "", -1)
}
// 根据传入的chars随机生成指定位数的字符串
func RandByChars(l int, chars string) string {
strList := []byte(chars)