diff --git a/app/theme/wp/index.go b/app/theme/wp/index.go index 23bcd0f..5436b49 100644 --- a/app/theme/wp/index.go +++ b/app/theme/wp/index.go @@ -134,7 +134,7 @@ func (i *IndexHandle) BuildIndexData(parm *IndexParams) (err error) { } i.Posts = posts i.TotalRows = totalRows - i.ginH["totalPage"] = number.CalTotalPage(totalRows, i.Param.PageSize) + i.ginH["totalPage"] = number.DivideCeil(totalRows, i.Param.PageSize) return } diff --git a/cache/cachemanager/manger.go b/cache/cachemanager/manger.go index a83cb0e..7895ac1 100644 --- a/cache/cachemanager/manger.go +++ b/cache/cachemanager/manger.go @@ -169,7 +169,7 @@ func parseArgs(args ...any) (string, func() time.Duration) { } func NewMapCache[K comparable, V any](data cache.Cache[K, V], batchFn cache.MapBatchFn[K, V], fn cache.MapSingleFn[K, V], args ...any) *cache.MapCache[K, V] { - inc := helper.ParseArgs(cache.IncreaseUpdate[K, V]{}, args...) + inc := helper.ParseArgs((*cache.IncreaseUpdate[K, V])(nil), args...) m := cache.NewMapCache[K, V](data, fn, batchFn, inc) FlushPush(m) ClearPush(m) @@ -219,7 +219,7 @@ func ClearExpired() { } func NewVarCache[T any](c cache.AnyCache[T], fn func(context.Context, ...any) (T, error), a ...any) *cache.VarCache[T] { - inc := helper.ParseArgs(cache.IncreaseUpdateVar[T]{}, a...) + inc := helper.ParseArgs((*cache.IncreaseUpdateVar[T])(nil), a...) ref := helper.ParseArgs(cache.RefreshVar[T](nil), a...) v := cache.NewVarCache(c, fn, inc, ref) FlushPush(v) diff --git a/cache/map.go b/cache/map.go index cbb83e4..54f27b6 100644 --- a/cache/map.go +++ b/cache/map.go @@ -18,7 +18,7 @@ type MapCache[K comparable, V any] struct { batchCacheFn MapBatchFn[K, V] getCacheBatch func(c context.Context, key []K, timeout time.Duration, params ...any) ([]V, error) getCacheBatchToMap func(c context.Context, key []K, timeout time.Duration, params ...any) (map[K]V, error) - increaseUpdate IncreaseUpdate[K, V] + increaseUpdate *IncreaseUpdate[K, V] refresh Refresh[K, V] } type IncreaseUpdate[K comparable, V any] struct { @@ -26,16 +26,16 @@ type IncreaseUpdate[K comparable, V any] struct { Fn IncreaseFn[K, V] } -func NewIncreaseUpdate[K comparable, V any](name string, fn IncreaseFn[K, V], cycleTime time.Duration, tFn func() time.Duration) IncreaseUpdate[K, V] { +func NewIncreaseUpdate[K comparable, V any](name string, fn IncreaseFn[K, V], cycleTime time.Duration, tFn func() time.Duration) *IncreaseUpdate[K, V] { tFn = reload.FnVal(name, cycleTime, tFn) - return IncreaseUpdate[K, V]{CycleTime: tFn, Fn: fn} + return &IncreaseUpdate[K, V]{CycleTime: tFn, Fn: fn} } type MapSingleFn[K, V any] func(context.Context, K, ...any) (V, error) type MapBatchFn[K comparable, V any] func(context.Context, []K, ...any) (map[K]V, error) type IncreaseFn[K comparable, V any] func(c context.Context, currentData V, k K, t time.Time, a ...any) (data V, save bool, refresh bool, err error) -func NewMapCache[K comparable, V any](ca Cache[K, V], cacheFunc MapSingleFn[K, V], batchCacheFn MapBatchFn[K, V], inc IncreaseUpdate[K, V]) *MapCache[K, V] { +func NewMapCache[K comparable, V any](ca Cache[K, V], cacheFunc MapSingleFn[K, V], batchCacheFn MapBatchFn[K, V], inc *IncreaseUpdate[K, V]) *MapCache[K, V] { r := &MapCache[K, V]{ Cache: ca, mux: sync.Mutex{}, @@ -118,45 +118,50 @@ func (m *MapCache[K, V]) Flush(ctx context.Context) { m.Cache.Flush(ctx) } +func (m *MapCache[K, V]) increaseUpdates(c context.Context, timeout time.Duration, data V, key K, params ...any) (V, error) { + var err error + nowTime := time.Now() + if nowTime.Sub(m.GetLastSetTime(c, key)) < m.increaseUpdate.CycleTime() { + return data, err + } + fn := func() { + m.mux.Lock() + defer m.mux.Unlock() + if nowTime.Sub(m.GetLastSetTime(c, key)) < m.increaseUpdate.CycleTime() { + return + } + dat, save, refresh, er := m.increaseUpdate.Fn(c, data, key, m.GetLastSetTime(c, key), params...) + if er != nil { + err = er + return + } + if refresh { + m.refresh.Refresh(c, key, params...) + } + if save { + m.Set(c, key, dat) + data = dat + } + } + if timeout > 0 { + er := helper.RunFnWithTimeout(c, timeout, fn) + if err == nil && er != nil { + return data, fmt.Errorf("increateUpdate cache %v err:[%s]", key, er) + } + } else { + fn() + } + return data, err +} + func (m *MapCache[K, V]) GetCache(c context.Context, key K, timeout time.Duration, params ...any) (V, error) { data, ok := m.Get(c, key) var err error if ok { - if m.increaseUpdate.Fn == nil || m.refresh == nil { + if m.increaseUpdate == nil || m.refresh == nil { return data, err } - nowTime := time.Now() - if nowTime.Sub(m.GetLastSetTime(c, key)) < m.increaseUpdate.CycleTime() { - return data, err - } - fn := func() { - m.mux.Lock() - defer m.mux.Unlock() - if nowTime.Sub(m.GetLastSetTime(c, key)) < m.increaseUpdate.CycleTime() { - return - } - dat, save, refresh, er := m.increaseUpdate.Fn(c, data, key, m.GetLastSetTime(c, key), params...) - if er != nil { - err = er - return - } - if refresh { - m.refresh.Refresh(c, key, params...) - } - if save { - m.Set(c, key, dat) - data = dat - } - } - if timeout > 0 { - er := helper.RunFnWithTimeout(c, timeout, fn, fmt.Sprintf("increateUpdate cache %v err", key)) - if err == nil && er != nil { - return data, er - } - } else { - fn() - } - return data, err + return m.increaseUpdates(c, timeout, data, key, params...) } call := func() { m.mux.Lock() diff --git a/cache/vars.go b/cache/vars.go index 27e4490..45776ea 100644 --- a/cache/vars.go +++ b/cache/vars.go @@ -12,7 +12,7 @@ type VarCache[T any] struct { AnyCache[T] setCacheFunc func(context.Context, ...any) (T, error) mutex sync.Mutex - increaseUpdate IncreaseUpdateVar[T] + increaseUpdate *IncreaseUpdateVar[T] refresh RefreshVar[T] } @@ -27,7 +27,7 @@ func (t *VarCache[T]) GetCache(ctx context.Context, timeout time.Duration, param data, ok := t.Get(ctx) var err error if ok { - if t.increaseUpdate.Fn != nil && t.refresh != nil { + if t.increaseUpdate != nil && t.refresh != nil { nowTime := time.Now() if t.increaseUpdate.CycleTime() > nowTime.Sub(t.GetLastSetTime(ctx)) { return data, nil @@ -125,7 +125,7 @@ func (c *VarMemoryCache[T]) GetLastSetTime(_ context.Context) time.Time { return c.v.Load().setTime } -func NewVarCache[T any](cache AnyCache[T], fn func(context.Context, ...any) (T, error), inc IncreaseUpdateVar[T], ref RefreshVar[T]) *VarCache[T] { +func NewVarCache[T any](cache AnyCache[T], fn func(context.Context, ...any) (T, error), inc *IncreaseUpdateVar[T], ref RefreshVar[T]) *VarCache[T] { return &VarCache[T]{ AnyCache: cache, setCacheFunc: fn, mutex: sync.Mutex{}, increaseUpdate: inc, diff --git a/helper/number/number.go b/helper/number/number.go index 2e904cc..011b214 100644 --- a/helper/number/number.go +++ b/helper/number/number.go @@ -97,8 +97,8 @@ func Divide[T constraints.Integer | constraints.Float](i, j T) T { return i / j } -func CalTotalPage[T constraints.Integer](totalRows, size T) T { - return T(math.Ceil(float64(totalRows) / float64(size))) +func DivideCeil[T constraints.Integer](num1, num2 T) T { + return T(math.Ceil(float64(num1) / float64(num2))) } type Counter[T constraints.Integer] func() T diff --git a/helper/number/number_test.go b/helper/number/number_test.go index 7c6ab61..7c8514a 100644 --- a/helper/number/number_test.go +++ b/helper/number/number_test.go @@ -245,8 +245,8 @@ func TestCalTotalPage(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := CalTotalPage(tt.args.totalRows, tt.args.size); got != tt.want { - t.Errorf("CalTotalPage() = %v, want %v", got, tt.want) + if got := DivideCeil(tt.args.totalRows, tt.args.size); got != tt.want { + t.Errorf("DivideCeil() = %v, want %v", got, tt.want) } }) } diff --git a/model/condition.go b/model/condition.go index c13fd47..b83e470 100644 --- a/model/condition.go +++ b/model/condition.go @@ -12,11 +12,12 @@ type QueryCondition struct { Having SqlBuilder Limit int Offset int + TotalRow int In [][]any RelationFn []func() (bool, bool, *QueryCondition, RelationFn) } -type RelationFn func() (func(any) []any, func(any, any), any, any, Relationship) +type RelationFn func() (func(any) []any, func(any, any), func(bool) any, Relationship) func Conditions(fns ...Condition) *QueryCondition { r := &QueryCondition{} @@ -78,6 +79,13 @@ func Limit(limit int) Condition { } } +// TotalRaw only effect on Pagination,when TotalRaw>0 ,will not query count +func TotalRaw(total int) Condition { + return func(c *QueryCondition) { + c.TotalRow = total + } +} + func Offset(offset int) Condition { return func(c *QueryCondition) { c.Offset = offset @@ -96,7 +104,7 @@ func WithCtx(ctx *context.Context) Condition { } } -func WithFn(getVal, isJoin bool, q *QueryCondition, fn func() (func(any) []any, func(any, any), any, any, Relationship)) Condition { +func WithFn(getVal, isJoin bool, q *QueryCondition, fn func() (func(any) []any, func(any, any), func(bool) any, Relationship)) Condition { return func(c *QueryCondition) { c.RelationFn = append(c.RelationFn, func() (bool, bool, *QueryCondition, RelationFn) { return getVal, isJoin, q, fn diff --git a/model/query.go b/model/query.go index fb1cd66..0fc7906 100644 --- a/model/query.go +++ b/model/query.go @@ -37,7 +37,7 @@ func pagination[T Model](db dbQuery, ctx context.Context, q *QueryCondition, pag From: q.From, Fields: "count(*) n", } - if q.Group != "" { + if q.Group != "" && q.TotalRow <= 0 { qx.Fields = q.Fields if qx.From == "" { qx.From = Table[T]() @@ -55,11 +55,17 @@ func pagination[T Model](db dbQuery, ctx context.Context, q *QueryCondition, pag Fields: "count(*) n", } } - n, err := gets[count[T]](db, ctx, &qx) - total = n.N - if err != nil || total < 1 { - return + if q.TotalRow <= 0 { + n, er := gets[count[T]](db, ctx, &qx) + total = n.N + if er != nil || total < 1 { + err = er + return + } + } else { + total = q.TotalRow } + offset := 0 if page > 1 { offset = (page - 1) * q.Limit diff --git a/model/relation.go b/model/relation.go index 655a528..f999cfd 100644 --- a/model/relation.go +++ b/model/relation.go @@ -3,6 +3,7 @@ package model import ( "context" "database/sql" + "errors" "fmt" "github.com/fthvgb1/wp-go/helper" "github.com/fthvgb1/wp-go/helper/slice" @@ -24,7 +25,7 @@ const ( // Relationship join table // -// RelationType HasOne| HasMany +// # RelationType HasOne| HasMany // // eg: hasOne, post has a user. ForeignKey is user's id , Local is post's userId field // @@ -119,7 +120,7 @@ func Relation(isPlural bool, db dbQuery, ctx context.Context, r any, q *QueryCon for _, f := range q.RelationFn { getVal, isJoin, qq, relationship := f() - idFn, assignmentFn, rr, rrs, ship := relationship() + idFn, assignmentFn, varFn, ship := relationship() if isJoin { beforeFn = append(beforeFn, func() { parseBeforeJoin(qx, ship) @@ -167,15 +168,15 @@ func Relation(isPlural bool, db dbQuery, ctx context.Context, r any, q *QueryCon } qq.In = in } - err = ParseRelation(isPlural || ship.RelationType == HasMany, db, ctx, helper.Or(isPlural, rrs, rr), qq) + err = ParseRelation(isPlural || ship.RelationType == HasMany, db, ctx, varFn(isPlural), qq) if err != nil { - if err == sql.ErrNoRows { + if errors.Is(err, sql.ErrNoRows) { err = nil } else { return err } } - assignmentFn(r, helper.Or(isPlural, rrs, rr)) + assignmentFn(r, varFn(isPlural)) return err }) } @@ -250,10 +251,15 @@ func SetHasMany[T, V any, K comparable](assignmentFn func(*T, *[]V), pIdFn func( func RelationHasOne[M, P any, I constraints.Integer | constraints.Unsigned](fId func(*M) I, pId func(*P) I, setVal func(*M, *P), r Relationship) RelationFn { idFn := GetWithID(fId) setFn := SetHasOne(setVal, fId, pId) - return func() (func(any) []any, func(any, any), any, any, Relationship) { - var s P - var ss []P - return idFn, setFn, &s, &ss, r + return func() (func(any) []any, func(any, any), func(bool) any, Relationship) { + return idFn, setFn, func(isPlural bool) any { + if isPlural { + var ss []P + return &ss + } + var s P + return &s + }, r } } @@ -262,9 +268,11 @@ func RelationHasOne[M, P any, I constraints.Integer | constraints.Unsigned](fId func RelationHasMany[M, P any, I constraints.Integer | constraints.Unsigned](mId func(*M) I, pId func(*P) I, setVal func(*M, *[]P), r Relationship) RelationFn { idFn := GetWithID(mId) setFn := SetHasMany(setVal, mId, pId) - return func() (func(any) []any, func(any, any), any, any, Relationship) { - var ss []P - return idFn, setFn, &ss, &ss, r + return func() (func(any) []any, func(any, any), func(bool) any, Relationship) { + return idFn, setFn, func(_ bool) any { + var ss []P + return &ss + }, r } } diff --git a/plugin/pagination/pagination.go b/plugin/pagination/pagination.go index 3148f77..af1860c 100644 --- a/plugin/pagination/pagination.go +++ b/plugin/pagination/pagination.go @@ -26,7 +26,7 @@ type ParsePagination struct { } func NewParsePagination(totalRaw int, pageSize int, currentPage, step int, query string, path string) ParsePagination { - return ParsePagination{TotalPage: number.CalTotalPage(totalRaw, pageSize), TotalRaw: totalRaw, PageSize: pageSize, CurrentPage: currentPage, Query: query, Path: path, Step: step} + return ParsePagination{TotalPage: number.DivideCeil(totalRaw, pageSize), TotalRaw: totalRaw, PageSize: pageSize, CurrentPage: currentPage, Query: query, Path: path, Step: step} } func Paginate(e Elements, p ParsePagination) string {