mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-04 00:10:25 +08:00
323 lines
10 KiB
Go
323 lines
10 KiB
Go
package base
|
||
|
||
import (
|
||
"context"
|
||
"mayfly-go/pkg/contextx"
|
||
"mayfly-go/pkg/gormx"
|
||
"mayfly-go/pkg/model"
|
||
"mayfly-go/pkg/utils/collx"
|
||
"reflect"
|
||
"time"
|
||
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
// 基础repo接口
|
||
type Repo[T model.ModelI] interface {
|
||
|
||
// Insert 新增一个实体
|
||
Insert(ctx context.Context, e T) error
|
||
|
||
// InsertWithDb 使用指定gorm db执行,主要用于事务执行
|
||
InsertWithDb(ctx context.Context, db *gorm.DB, e T) error
|
||
|
||
// BatchInsert 批量新增实体
|
||
BatchInsert(ctx context.Context, models []T) error
|
||
|
||
// BatchInsertWithDb 使用指定gorm db执行,主要用于事务执行
|
||
BatchInsertWithDb(ctx context.Context, db *gorm.DB, models []T) error
|
||
|
||
// 根据实体id更新实体信息
|
||
UpdateById(ctx context.Context, e T, columns ...string) error
|
||
|
||
// 使用指定gorm db执行,主要用于事务执行
|
||
UpdateByIdWithDb(ctx context.Context, db *gorm.DB, e T, columns ...string) error
|
||
|
||
// UpdateByCond 更新满足条件的数据
|
||
// @param values 需要模型结构体或map
|
||
// @param cond 条件
|
||
UpdateByCond(ctx context.Context, values any, cond any) error
|
||
|
||
// UpdateByCondWithDb 更新满足条件的数据
|
||
// @param values 需要模型结构体或map
|
||
UpdateByCondWithDb(ctx context.Context, db *gorm.DB, values any, cond any) error
|
||
|
||
// Save 保存实体,实体IsCreate返回true则新增,否则更新
|
||
Save(ctx context.Context, e T) error
|
||
|
||
// SaveWithDb 保存实体,实体IsCreate返回true则新增,否则更新。
|
||
// 使用指定gorm db执行,主要用于事务执行
|
||
SaveWithDb(ctx context.Context, db *gorm.DB, e T) error
|
||
|
||
// DeleteById 根据实体主键删除实体
|
||
DeleteById(ctx context.Context, id ...uint64) error
|
||
|
||
// DeleteByIdWithDb 使用指定gorm db执行,主要用于事务执行
|
||
DeleteByIdWithDb(ctx context.Context, db *gorm.DB, id ...uint64) error
|
||
|
||
// DeleteByCond 根据实体条件删除实体
|
||
DeleteByCond(ctx context.Context, cond any) error
|
||
|
||
// DeleteByCondWithDb 使用指定gorm db执行,主要用于事务执行
|
||
DeleteByCondWithDb(ctx context.Context, db *gorm.DB, cond any) error
|
||
|
||
// ExecBySql 执行原生sql
|
||
ExecBySql(sql string, params ...any) error
|
||
|
||
// GetById 根据实体id查询
|
||
GetById(id uint64, cols ...string) (T, error)
|
||
|
||
// GetByIds 根据实体ids查询
|
||
GetByIds(ids []uint64, cols ...string) ([]T, error)
|
||
|
||
// GetByCond 根据实体条件查询实体信息(单个结果集)
|
||
// @param cond 支持普通结构体或*model.QueryCond。如果cond为model.QueryCond,则需要使用Dest方法绑定值的指针
|
||
GetByCond(cond any) error
|
||
|
||
// SelectByCondToAny 根据条件查询数据映射至res
|
||
SelectByCondToAny(cond any, res any) error
|
||
|
||
// SelectByCond 根据条件查询模型实例数组
|
||
SelectByCond(cond any, cols ...string) ([]T, error)
|
||
|
||
// PageByCond 根据查询条件分页查询
|
||
PageByCond(cond any, pageParam model.PageParam, cols ...string) (*model.PageResult[T], error)
|
||
|
||
// SelectBySql 根据sql语句查询数据
|
||
SelectBySql(sql string, res any, params ...any) error
|
||
|
||
// CountByCond 根据指定条件统计model表的数量
|
||
CountByCond(cond any) int64
|
||
|
||
// SelectByCondWithOffset 根据条件查询数据并支持 offset + limit 分页
|
||
SelectByCondWithOffset(cond any, limit int, offset int) ([]T, error)
|
||
}
|
||
|
||
var _ (Repo[*model.Model]) = (*RepoImpl[*model.Model])(nil)
|
||
|
||
// 基础repo接口
|
||
type RepoImpl[T model.ModelI] struct {
|
||
model any // 模型实例
|
||
|
||
modelType reflect.Type // 模型类型
|
||
}
|
||
|
||
func (br *RepoImpl[T]) Insert(ctx context.Context, e T) error {
|
||
if db := GetDbFromCtx(ctx); db != nil {
|
||
return br.InsertWithDb(ctx, db, e)
|
||
}
|
||
return gormx.Insert(br.fillBaseInfo(ctx, e))
|
||
}
|
||
|
||
func (br *RepoImpl[T]) InsertWithDb(ctx context.Context, db *gorm.DB, e T) error {
|
||
return gormx.InsertWithDb(db, br.fillBaseInfo(ctx, e))
|
||
}
|
||
|
||
func (br *RepoImpl[T]) BatchInsert(ctx context.Context, es []T) error {
|
||
if db := GetDbFromCtx(ctx); db != nil {
|
||
return br.BatchInsertWithDb(ctx, db, es)
|
||
}
|
||
for _, e := range es {
|
||
br.fillBaseInfo(ctx, e)
|
||
}
|
||
return gormx.BatchInsert[T](es)
|
||
}
|
||
|
||
func (br *RepoImpl[T]) BatchInsertWithDb(ctx context.Context, db *gorm.DB, es []T) error {
|
||
for _, e := range es {
|
||
br.fillBaseInfo(ctx, e)
|
||
}
|
||
return gormx.BatchInsertWithDb[T](db, es)
|
||
}
|
||
|
||
func (br *RepoImpl[T]) UpdateById(ctx context.Context, e T, columns ...string) error {
|
||
return br.UpdateByIdWithDb(ctx, GetDbFromCtx(ctx), e, columns...)
|
||
}
|
||
|
||
func (br *RepoImpl[T]) UpdateByIdWithDb(ctx context.Context, db *gorm.DB, e T, columns ...string) error {
|
||
if db == nil {
|
||
return gormx.UpdateById(br.fillBaseInfo(ctx, e), columns...)
|
||
}
|
||
return gormx.UpdateByIdWithDb(db, br.fillBaseInfo(ctx, e), columns...)
|
||
}
|
||
|
||
func (br *RepoImpl[T]) UpdateByCond(ctx context.Context, values any, cond any) error {
|
||
return br.UpdateByCondWithDb(ctx, GetDbFromCtx(ctx), values, cond)
|
||
}
|
||
|
||
func (br *RepoImpl[T]) UpdateByCondWithDb(ctx context.Context, db *gorm.DB, values any, cond any) error {
|
||
if e, ok := values.(T); ok {
|
||
// 先随机设置一个id,让fillBaseInfo不填充create信息
|
||
e.SetId(1)
|
||
e = br.fillBaseInfo(ctx, e)
|
||
// model的主键值需为空,否则会带上主键条件
|
||
e.SetId(0)
|
||
values = e
|
||
} else {
|
||
var mapValues map[string]any
|
||
// 非model实体,则为map
|
||
if m, ok := values.(map[string]any); ok {
|
||
mapValues = m
|
||
} else if collxm, ok := values.(collx.M); ok {
|
||
mapValues = map[string]any(collxm)
|
||
}
|
||
if len(mapValues) > 0 {
|
||
mapValues[model.UpdateTimeColumn] = time.Now()
|
||
if la := contextx.GetLoginAccount(ctx); la != nil {
|
||
mapValues[model.ModifierColumn] = la.Username
|
||
mapValues[model.ModifierIdColumn] = la.Id
|
||
}
|
||
values = mapValues
|
||
}
|
||
}
|
||
|
||
if db == nil {
|
||
return gormx.UpdateByCond(br.GetModel(), values, toQueryCond(cond))
|
||
}
|
||
return gormx.UpdateByCondWithDb(db, br.GetModel(), values, toQueryCond(cond))
|
||
}
|
||
|
||
func (br *RepoImpl[T]) Save(ctx context.Context, e T) error {
|
||
if e.IsCreate() {
|
||
return br.Insert(ctx, e)
|
||
}
|
||
return br.UpdateById(ctx, e)
|
||
}
|
||
|
||
func (br *RepoImpl[T]) SaveWithDb(ctx context.Context, db *gorm.DB, e T) error {
|
||
if e.IsCreate() {
|
||
return br.InsertWithDb(ctx, db, e)
|
||
}
|
||
return br.UpdateByIdWithDb(ctx, db, e)
|
||
}
|
||
|
||
func (br *RepoImpl[T]) DeleteById(ctx context.Context, id ...uint64) error {
|
||
if db := GetDbFromCtx(ctx); db != nil {
|
||
return br.DeleteByIdWithDb(ctx, db, id...)
|
||
}
|
||
return gormx.DeleteById(br.GetModel(), id...)
|
||
}
|
||
|
||
func (br *RepoImpl[T]) DeleteByIdWithDb(ctx context.Context, db *gorm.DB, id ...uint64) error {
|
||
return gormx.DeleteByIdWithDb(db, br.GetModel(), id...)
|
||
}
|
||
|
||
func (br *RepoImpl[T]) DeleteByCond(ctx context.Context, cond any) error {
|
||
if db := GetDbFromCtx(ctx); db != nil {
|
||
return br.DeleteByCondWithDb(ctx, db, cond)
|
||
}
|
||
return gormx.DeleteByCond(br.GetModel(), toQueryCond(cond))
|
||
}
|
||
|
||
func (br *RepoImpl[T]) DeleteByCondWithDb(ctx context.Context, db *gorm.DB, cond any) error {
|
||
return gormx.DeleteByCondWithDb(db, br.GetModel(), toQueryCond(cond))
|
||
}
|
||
|
||
func (br *RepoImpl[T]) ExecBySql(sql string, params ...any) error {
|
||
return gormx.ExecSql(sql, params...)
|
||
}
|
||
|
||
func (br *RepoImpl[T]) GetById(id uint64, cols ...string) (T, error) {
|
||
e := br.NewModel()
|
||
return e, gormx.GetById(e, id, cols...)
|
||
}
|
||
|
||
func (br *RepoImpl[T]) GetByIds(ids []uint64, cols ...string) ([]T, error) {
|
||
var models []T
|
||
return models, br.SelectByCondToAny(model.NewCond().In(model.IdColumn, ids).Columns(cols...), &models)
|
||
}
|
||
|
||
func (br *RepoImpl[T]) GetByCond(cond any) error {
|
||
return gormx.GetByCond(br.GetModel(), toQueryCond(cond))
|
||
}
|
||
|
||
func (br *RepoImpl[T]) SelectByCondToAny(cond any, res any) error {
|
||
return gormx.SelectByCond(br.GetModel(), toQueryCond(cond).Dest(res))
|
||
}
|
||
|
||
func (br *RepoImpl[T]) SelectByCond(cond any, cols ...string) ([]T, error) {
|
||
var models []T
|
||
return models, br.SelectByCondToAny(toQueryCond(cond).Dest(models).Columns(cols...), &models)
|
||
}
|
||
|
||
func (br *RepoImpl[T]) PageByCond(cond any, pageParam model.PageParam, cols ...string) (*model.PageResult[T], error) {
|
||
var models []T
|
||
return gormx.PageByCond(br.GetModel(), toQueryCond(cond).Columns(cols...), pageParam, models)
|
||
}
|
||
|
||
func (br *RepoImpl[T]) SelectBySql(sql string, res any, params ...any) error {
|
||
return gormx.SelectBySql(sql, res, params...)
|
||
}
|
||
|
||
func (br *RepoImpl[T]) CountByCond(cond any) int64 {
|
||
return gormx.CountByCond(br.GetModel(), toQueryCond(cond))
|
||
}
|
||
|
||
func (br *RepoImpl[T]) SelectByCondWithOffset(cond any, limit int, offset int) ([]T, error) {
|
||
var models []T
|
||
err := gormx.NewQuery(br.GetModel(), toQueryCond(cond)).GenGdb().Limit(limit).Offset(offset).Find(&models).Error
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return models, nil
|
||
}
|
||
|
||
// NewModel 新建模型实例
|
||
func (br *RepoImpl[T]) NewModel() T {
|
||
newModel := reflect.New(br.getModelType()).Interface()
|
||
return newModel.(T)
|
||
}
|
||
|
||
// func (br *RepoImpl[T]) NewModes() *[]T {
|
||
// // 创建一个空的切片
|
||
// slice := reflect.MakeSlice(reflect.SliceOf(reflect.PointerTo(br.getModelType())), 0, 0)
|
||
// // 创建指向切片的指针
|
||
// ptrToSlice := reflect.New(slice.Type())
|
||
// // 设置指向切片的指针为创建的空切片
|
||
// ptrToSlice.Elem().Set(slice)
|
||
// // 转换指向切片的指针
|
||
// return ptrToSlice.Interface().(*[]T)
|
||
// }
|
||
|
||
// getModel 获取表的模型实例
|
||
func (br *RepoImpl[T]) GetModel() T {
|
||
if br.model != nil {
|
||
return br.model.(T)
|
||
}
|
||
|
||
br.model = br.NewModel()
|
||
return br.model.(T)
|
||
}
|
||
|
||
// getModelType 获取模型类型(非指针模型)
|
||
func (br *RepoImpl[T]) getModelType() reflect.Type {
|
||
if br.modelType != nil {
|
||
return br.modelType
|
||
}
|
||
|
||
var model T
|
||
modelType := reflect.TypeOf(model)
|
||
// 检查 model 是否为指针类型
|
||
if modelType.Kind() == reflect.Ptr {
|
||
// 获取指针指向的类型
|
||
modelType = modelType.Elem()
|
||
}
|
||
br.modelType = modelType
|
||
return modelType
|
||
}
|
||
|
||
// 从上下文获取登录账号信息,并赋值至实体
|
||
func (br *RepoImpl[T]) fillBaseInfo(ctx context.Context, e T) T {
|
||
// 默认使用数据库id策略, 若要改变则实体结构体自行覆盖FillBaseInfo方法。可参考 sys/entity.Resource
|
||
e.FillBaseInfo(model.IdGenTypeNone, contextx.GetLoginAccount(ctx))
|
||
return e
|
||
}
|
||
|
||
// toQueryCond 统一转为*model.QueryCond
|
||
func toQueryCond(cond any) *model.QueryCond {
|
||
if qc, ok := cond.(*model.QueryCond); ok {
|
||
return qc
|
||
}
|
||
return model.NewModelCond(cond)
|
||
}
|