repository.go 4.1 KB
package domain

import (
	"context"
	"fmt"
	"github.com/pkg/errors"
	"github.com/zeromicro/go-zero/core/stores/sqlx"
	"gitlab.fjmaimaimai.com/allied-creation/su-micro/pkg/transaction"
	"reflect"
)

func OffsetLimit(page, size int) (offset int, limit int) {
	if page == 0 {
		page = 1
	}
	if size == 0 {
		size = 20
	}
	offset = (page - 1) * size
	limit = size
	return
}

type QueryOptions map[string]interface{}

func NewQueryOptions() QueryOptions {
	options := make(map[string]interface{})
	return options
}
func (options QueryOptions) WithOffsetLimit(page, size int) QueryOptions {
	offset, limit := OffsetLimit(page, size)
	options["offset"] = offset
	options["limit"] = limit
	return options
}

func (options QueryOptions) WithKV(key string, value interface{}) QueryOptions {
	if isEmptyOrZeroValue(value) {
		return options
	}
	options[key] = value
	return options
}

func (options QueryOptions) LikeKV(key string, value interface{}) QueryOptions {
	if isEmptyOrZeroValue(value) {
		return options
	}
	options[key] = fmt.Sprintf("%%%v%%", value)
	return options
}

func isEmptyOrZeroValue(i interface{}) bool {
	if i == nil {
		return true // 如果接口为空,返回true
	}
	// 使用反射判断接口的值是否为零值
	v := reflect.ValueOf(i)
	switch v.Kind() {
	case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String:
		return v.Len() == 0
	case reflect.Bool:
		return !v.Bool()
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return v.Int() == 0
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return v.Uint() == 0
	case reflect.Float32, reflect.Float64:
		return v.Float() == 0
	case reflect.Interface, reflect.Ptr:
		return v.IsNil()
	default:
		return false // 其他类型默认不为空
	}
}

func (options QueryOptions) MustWithKV(key string, value interface{}) QueryOptions {
	options[key] = value
	return options
}

func (options QueryOptions) Copy() QueryOptions {
	newOptions := NewQueryOptions()
	for k, v := range options {
		newOptions[k] = v
	}
	return newOptions
}

type IndexQueryOptionFunc func() QueryOptions

// 自定义的一些查询条件

func (options QueryOptions) WithCountOnly() QueryOptions {
	options["countOnly"] = true
	return options
}
func (options QueryOptions) WithFindOnly() QueryOptions {
	options["findOnly"] = true
	return options
}

func LazyLoad[K comparable, T any](source map[K]T, ctx context.Context, conn transaction.Conn, k K, load func(context.Context, transaction.Conn, K) (T, error)) (T, error) {
	if isEmptyOrZeroValue(k) {
		var t T
		return t, fmt.Errorf("empty key ‘%v’", k)
	}
	if v, ok := source[k]; ok {
		return v, nil
	}
	if v, err := load(ctx, conn, k); err != nil {
		return v, err
	} else {
		source[k] = v
		return v, nil
	}
}

func Values[T any, V any](list []T, each func(item T) V) []V {
	var result []V
	for _, item := range list {
		value := each(item)
		result = append(result, value)
	}
	return result
}

func MustValues[T any, V any](list []T, each func(item T) V) []V {
	var result []V
	for _, item := range list {
		value := each(item)
		if isEmptyOrZeroValue(value) {
			continue
		}
		result = append(result, value)
	}
	return result
}

type LazyLoadService[K comparable, T any] struct {
	FoundMap    map[K]T // 已找到对象Map
	NotFoundMap map[K]T // 未找到对象Map
	load        func(context.Context, transaction.Conn, K) (T, error)
}

func NewLazyLoadService[K comparable, T any](load func(context.Context, transaction.Conn, K) (T, error)) *LazyLoadService[K, T] {
	return &LazyLoadService[K, T]{
		FoundMap:    map[K]T{},
		NotFoundMap: map[K]T{},
		load:        load,
	}
}

func (s *LazyLoadService[K, T]) Load(ctx context.Context, conn transaction.Conn, k K) (T, error) {
	if isEmptyOrZeroValue(k) {
		var t T
		return t, fmt.Errorf("empty key ‘%v’", k)
	}
	if v, ok := s.NotFoundMap[k]; ok {
		return v, sqlx.ErrNotFound
	}
	if v, ok := s.FoundMap[k]; ok {
		return v, nil
	}
	if v, err := s.load(ctx, conn, k); err != nil {
		if errors.Is(err, sqlx.ErrNotFound) {
			s.NotFoundMap[k] = v
		}
		return v, err
	} else {
		s.FoundMap[k] = v
		return v, nil
	}
}