Files
mayfly-go/server/pkg/base/repo.go
2025-07-27 21:02:48 +08:00

312 lines
9.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)
}
// 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)
}