Compare commits
2 Commits
Author | SHA1 | Date |
---|---|---|
|
1430943c48 | |
|
4eef560649 |
4
go.mod
4
go.mod
|
@ -5,8 +5,8 @@ go 1.23.0
|
|||
toolchain go1.23.10
|
||||
|
||||
require (
|
||||
git.nobla.cn/golang/aeus v0.0.10
|
||||
git.nobla.cn/golang/rest v0.1.3
|
||||
git.nobla.cn/golang/aeus v0.0.11
|
||||
git.nobla.cn/golang/rest v0.1.4
|
||||
github.com/envoyproxy/protoc-gen-validate v1.2.1
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6
|
||||
|
|
8
go.sum
8
go.sum
|
@ -1,11 +1,11 @@
|
|||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
git.nobla.cn/golang/aeus v0.0.10 h1:MDov2GQgV4dxFcjhiJDMwWgfEX0ROwarH3/ETP/9/ik=
|
||||
git.nobla.cn/golang/aeus v0.0.10/go.mod h1:oOEwqIp6AhKKqj6sLFO8x7IycOROYHCb/2/CjF4+9CU=
|
||||
git.nobla.cn/golang/aeus v0.0.11 h1:gbXIOVOQRDTIQTjw9wPVfNC9nXBaTJCABeDYmrHW2Oc=
|
||||
git.nobla.cn/golang/aeus v0.0.11/go.mod h1:oOEwqIp6AhKKqj6sLFO8x7IycOROYHCb/2/CjF4+9CU=
|
||||
git.nobla.cn/golang/kos v0.1.32 h1:sFVCA7vKc8dPUd0cxzwExOSPX2mmMh2IuwL6cYS1pBc=
|
||||
git.nobla.cn/golang/kos v0.1.32/go.mod h1:35Z070+5oB39WcVrh5DDlnVeftL/Ccmscw2MZFe9fUg=
|
||||
git.nobla.cn/golang/rest v0.1.3 h1:B+MX1teFURVxol77Ho5+SxyF61VfvXbzsh8IIajoGG0=
|
||||
git.nobla.cn/golang/rest v0.1.3/go.mod h1:4viDk7VujDokpUeHQGbnSp2bkkVZEoIkWQIs/l/TTPQ=
|
||||
git.nobla.cn/golang/rest v0.1.4 h1:9/XscfNXI3aPESpy8CPtVl17VSMxU9BihhedeG+h8YY=
|
||||
git.nobla.cn/golang/rest v0.1.4/go.mod h1:4viDk7VujDokpUeHQGbnSp2bkkVZEoIkWQIs/l/TTPQ=
|
||||
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
|
||||
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
||||
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
||||
|
|
|
@ -8,12 +8,19 @@ import (
|
|||
)
|
||||
|
||||
// Menu 合并菜单
|
||||
func Menu(db *gorm.DB, model *models.Menu) (err error) {
|
||||
if err = db.Where("name = ?", model.Name).First(model).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
err = db.Create(model).Error
|
||||
func Menu(db *gorm.DB, datas ...*models.Menu) (err error) {
|
||||
tx := db.Begin()
|
||||
for _, model := range datas {
|
||||
if err = tx.Where("name = ?", model.Name).First(model).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
if err = tx.Create(model).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
err = tx.Commit().Error
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -31,6 +38,7 @@ func Permission(db *gorm.DB, menuName string, permission string, label string) (
|
|||
return
|
||||
}
|
||||
|
||||
// #2c7be5
|
||||
// Default 合并初始化数据集
|
||||
func Default(db *gorm.DB) (err error) {
|
||||
var (
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
package chartjs
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// DataCounter 计数统计, 实现类似Pie 和 Doughnut 之类的图形
|
||||
type DataCounter struct {
|
||||
mutex sync.Mutex
|
||||
legends map[string]*counterValue
|
||||
}
|
||||
|
||||
func (c *DataCounter) Inc(leg string, label string, value float64) {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
v, ok := c.legends[leg]
|
||||
if !ok {
|
||||
v = &counterValue{
|
||||
Label: leg,
|
||||
Values: make(map[string]float64),
|
||||
}
|
||||
c.legends[leg] = v
|
||||
}
|
||||
v.Values[label] += value
|
||||
}
|
||||
|
||||
func (c *DataCounter) Dec(legend string, label string, value float64) {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
v, ok := c.legends[legend]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if _, ok := v.Values[label]; ok {
|
||||
v.Values[label] -= value
|
||||
}
|
||||
}
|
||||
|
||||
func (c *DataCounter) Data() *CounterData {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
data := &CounterData{
|
||||
Lables: make([]string, 0, 5),
|
||||
Datasets: make([]*CounterValue, 0, len(c.legends)),
|
||||
}
|
||||
for _, row := range c.legends {
|
||||
for k, _ := range row.Values {
|
||||
if !slices.Contains(data.Lables, k) {
|
||||
data.Lables = append(data.Lables, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, row := range c.legends {
|
||||
set := &CounterValue{
|
||||
Label: row.Label,
|
||||
Data: make([]float64, 0, len(data.Lables)),
|
||||
}
|
||||
for _, label := range data.Lables {
|
||||
set.Data = append(set.Data, row.Values[label])
|
||||
}
|
||||
data.Datasets = append(data.Datasets, set)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func NewDataCounter(legends ...string) *DataCounter {
|
||||
c := &DataCounter{
|
||||
legends: make(map[string]*counterValue),
|
||||
}
|
||||
for _, legend := range legends {
|
||||
c.legends[legend] = &counterValue{
|
||||
Label: legend,
|
||||
Values: make(map[string]float64),
|
||||
}
|
||||
}
|
||||
return c
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package chartjs
|
||||
|
||||
import "time"
|
||||
|
||||
// TimeSeriesGroup 时间计算数据, 实现类似以时间为维度的折线图
|
||||
type TimeSeriesGroup struct {
|
||||
step string
|
||||
values map[string]*TimeSeries
|
||||
}
|
||||
|
||||
func (g *TimeSeriesGroup) Inc(tm time.Time, key string, value float64) {
|
||||
v, ok := g.values[key]
|
||||
if !ok {
|
||||
v = NewTimeSeries(g.step)
|
||||
g.values[key] = v
|
||||
}
|
||||
v.Inc(tm, value)
|
||||
}
|
||||
|
||||
func (g *TimeSeriesGroup) Dec(tm time.Time, key string, value float64) {
|
||||
v, ok := g.values[key]
|
||||
if !ok {
|
||||
v = NewTimeSeries(g.step)
|
||||
g.values[key] = v
|
||||
}
|
||||
v.Dec(tm, value)
|
||||
}
|
||||
|
||||
// Data 生成chart.js 的图表dataset
|
||||
func (g *TimeSeriesGroup) Data(sts, ets int64) (values []*TimeseriesData) {
|
||||
for key, row := range g.values {
|
||||
data := &TimeseriesData{
|
||||
Label: key,
|
||||
Data: row.Values(sts, ets),
|
||||
}
|
||||
values = append(values, data)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func NewTimeSeriesGroup(step string) *TimeSeriesGroup {
|
||||
return &TimeSeriesGroup{
|
||||
step: step,
|
||||
values: make(map[string]*TimeSeries),
|
||||
}
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
package chartjs
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
Minute = "minute"
|
||||
Hour = "hour"
|
||||
Day = "day"
|
||||
Method = "method"
|
||||
)
|
||||
|
||||
// 时间序列值, 实现了按时间进行分组的值计算
|
||||
type TimeSeries struct {
|
||||
mutex sync.Mutex
|
||||
step string
|
||||
values []*TimeseriesValue
|
||||
}
|
||||
|
||||
func (s *TimeSeries) cutTimeSeries(tm time.Time) int64 {
|
||||
switch s.step {
|
||||
case Method:
|
||||
return time.Date(tm.Year(), tm.Month(), 0, 0, 0, 0, 0, time.Local).Unix()
|
||||
case Day:
|
||||
return time.Date(tm.Year(), tm.Month(), tm.Day(), 0, 0, 0, 0, time.Local).Unix()
|
||||
case Hour:
|
||||
return time.Date(tm.Year(), tm.Month(), tm.Day(), tm.Hour(), 0, 0, 0, time.Local).Unix()
|
||||
default:
|
||||
return time.Date(tm.Year(), tm.Month(), tm.Day(), tm.Hour(), tm.Minute(), 0, 0, time.Local).Unix()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *TimeSeries) truncateTime(ts int64) int64 {
|
||||
tm := time.Unix(ts, 0)
|
||||
switch s.step {
|
||||
case Method:
|
||||
return time.Date(tm.Year(), tm.Month(), 0, 0, 0, 0, 0, time.Local).Unix()
|
||||
case Day:
|
||||
return time.Date(tm.Year(), tm.Month(), tm.Day(), 0, 0, 0, 0, time.Local).Unix()
|
||||
case Hour:
|
||||
return time.Date(tm.Year(), tm.Month(), tm.Day(), tm.Hour(), 0, 0, 0, time.Local).Unix()
|
||||
default:
|
||||
return time.Date(tm.Year(), tm.Month(), tm.Day(), tm.Hour(), tm.Minute(), 0, 0, time.Local).Unix()
|
||||
}
|
||||
return tm.Unix()
|
||||
}
|
||||
|
||||
func (s *TimeSeries) nextTimestamp(ts int64) int64 {
|
||||
tm := time.Unix(ts, 0)
|
||||
switch s.step {
|
||||
case Method:
|
||||
return tm.AddDate(0, 1, 0).Unix()
|
||||
case Day:
|
||||
return tm.AddDate(0, 0, 1).Unix()
|
||||
case Hour:
|
||||
return ts + 3600
|
||||
default:
|
||||
return ts + 60
|
||||
}
|
||||
}
|
||||
|
||||
func (s *TimeSeries) Inc(tm time.Time, value float64) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
ts := s.cutTimeSeries(tm)
|
||||
for _, v := range s.values {
|
||||
if v.Timestamp == ts {
|
||||
v.Value += value
|
||||
return
|
||||
}
|
||||
}
|
||||
s.values = append(s.values, &TimeseriesValue{
|
||||
Timestamp: ts,
|
||||
Value: value,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *TimeSeries) Dec(tm time.Time, value float64) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
ts := s.cutTimeSeries(tm)
|
||||
for _, v := range s.values {
|
||||
if v.Timestamp == ts {
|
||||
v.Value -= value
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *TimeSeries) Values(sts, ets int64) []*TimeseriesValue {
|
||||
var (
|
||||
nextStamp int64
|
||||
)
|
||||
sts = s.truncateTime(sts)
|
||||
ets = s.truncateTime(ets)
|
||||
series := make([]*TimeseriesValue, 0, len(s.values))
|
||||
nextStamp = sts
|
||||
for _, row := range s.values {
|
||||
for row.Timestamp > nextStamp {
|
||||
series = append(series, &TimeseriesValue{
|
||||
Timestamp: nextStamp,
|
||||
Value: 0,
|
||||
})
|
||||
nextStamp = s.nextTimestamp(nextStamp)
|
||||
}
|
||||
series = append(series, row)
|
||||
nextStamp = s.nextTimestamp(nextStamp)
|
||||
}
|
||||
for ets > nextStamp {
|
||||
series = append(series, &TimeseriesValue{
|
||||
Timestamp: nextStamp,
|
||||
Value: 0,
|
||||
})
|
||||
nextStamp = s.nextTimestamp(nextStamp)
|
||||
}
|
||||
return series
|
||||
}
|
||||
|
||||
func NewTimeSeries(step string) *TimeSeries {
|
||||
return &TimeSeries{
|
||||
step: step,
|
||||
values: make([]*TimeseriesValue, 0, 64),
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
package chartjs
|
||||
|
||||
/*
|
||||
{
|
||||
label: 'My Time Series Data',
|
||||
data: [
|
||||
{ x: '2025-01-01', y: 10 },
|
||||
{ x: '2025-01-02', y: 15 },
|
||||
{ x: '2025-01-03', y: 8 },
|
||||
{ x: '2025-01-04', y: 20 },
|
||||
{ x: '2025-01-05', y: 12 }
|
||||
],
|
||||
borderColor: 'rgb(75, 192, 192)',
|
||||
tension: 0.1
|
||||
}
|
||||
*/
|
||||
|
||||
type TimeseriesValue struct {
|
||||
Timestamp int64 `json:"x"`
|
||||
Value float64 `json:"y"`
|
||||
}
|
||||
|
||||
// TimeseriesData 时间序列的数据
|
||||
type TimeseriesData struct {
|
||||
Label string `json:"label"`
|
||||
Data []*TimeseriesValue `json:"data"`
|
||||
BorderColor string `json:"borderColor,omitempty"`
|
||||
BackgroundColor string `json:"backgroundColor,omitempty"`
|
||||
Tension float64 `json:"tension,omitempty"`
|
||||
Fill bool `json:"fill,omitempty"`
|
||||
}
|
||||
|
||||
/**
|
||||
{
|
||||
labels: labels,
|
||||
datasets: [
|
||||
{
|
||||
label: 'Dataset 1',
|
||||
data: Utils.numbers(NUMBER_CFG),
|
||||
backgroundColor: [
|
||||
Utils.transparentize(Utils.CHART_COLORS.red, 0.5),
|
||||
Utils.transparentize(Utils.CHART_COLORS.orange, 0.5),
|
||||
Utils.transparentize(Utils.CHART_COLORS.yellow, 0.5),
|
||||
Utils.transparentize(Utils.CHART_COLORS.green, 0.5),
|
||||
Utils.transparentize(Utils.CHART_COLORS.blue, 0.5),
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
*/
|
||||
|
||||
type counterValue struct {
|
||||
Label string `json:"label"`
|
||||
Values map[string]float64 `json:"data"`
|
||||
}
|
||||
|
||||
type CounterValue struct {
|
||||
Label string `json:"label"`
|
||||
Data []float64 `json:"data"`
|
||||
BackgroundColor []string `json:"backgroundColor,omitempty"`
|
||||
}
|
||||
|
||||
type CounterData struct {
|
||||
Lables []string `json:"labels"`
|
||||
Datasets []*CounterValue `json:"datasets"`
|
||||
}
|
|
@ -84,7 +84,7 @@ func TryCache[T any](ctx context.Context, key string, f func(tx *gorm.DB) (T, er
|
|||
if opts.dependency == nil {
|
||||
return entry.Value, nil
|
||||
}
|
||||
if dependValue, err = opts.dependency.GetValue(ctx, opts.db); err == nil {
|
||||
if dependValue, err = opts.dependency.GetValue(ctx, opts.db); err == nil && dependValue != "" {
|
||||
hasDependValue = true
|
||||
if entry.CompareValue == dependValue {
|
||||
return entry.Value, nil
|
||||
|
@ -98,7 +98,7 @@ func TryCache[T any](ctx context.Context, key string, f func(tx *gorm.DB) (T, er
|
|||
if val, err, _ = singleInstance.Do(key, func() (any, error) {
|
||||
if result, err = f(tx); err == nil {
|
||||
if !hasDependValue && opts.dependency != nil {
|
||||
dependValue, err = opts.dependency.GetValue(ctx, tx)
|
||||
dependValue, _ = opts.dependency.GetValue(ctx, tx)
|
||||
}
|
||||
opts.cache.Store(ctx, key, &cacheEntry[T]{
|
||||
CompareValue: dependValue,
|
||||
|
|
144
server.go
144
server.go
|
@ -10,6 +10,7 @@ import (
|
|||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.nobla.cn/golang/aeus-admin/migrate"
|
||||
"git.nobla.cn/golang/aeus-admin/models"
|
||||
|
@ -334,10 +335,151 @@ func registerRESTRoute(domain string, db *gorm.DB, hs *http.Server) {
|
|||
}
|
||||
}
|
||||
|
||||
handleKeyValues := func(ctx *http.Context) (err error) {
|
||||
var (
|
||||
dbDependency dbcache.CacheDependency
|
||||
pairs []*restTypes.TypeValue[any]
|
||||
modelValue any
|
||||
)
|
||||
moduleName := ctx.Param("module")
|
||||
tableName := ctx.Param("table")
|
||||
entities := rest.GetModels()
|
||||
for _, entry := range entities {
|
||||
if entry.ModuleName() == moduleName && entry.TableName() == tableName {
|
||||
modelValue = reflect.New(entry.Value().Type()).Interface()
|
||||
for _, field := range entry.Fields() {
|
||||
if field.AutoUpdateTime > 0 {
|
||||
dbDependency = dbcache.NewSqlDependency(fmt.Sprintf("SELECT MAX(`%s`) FROM `%s`", field.DBName, entry.TableName()))
|
||||
break
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if modelValue == nil {
|
||||
return ctx.Error(errors.NotFound, "model not found")
|
||||
}
|
||||
labelColumn := ctx.Param("label")
|
||||
valueColumn := ctx.Param("value")
|
||||
opts := make([]dbcache.CacheOption, 0, 4)
|
||||
opts = append(opts, dbcache.WithDB(db))
|
||||
if dbDependency != nil {
|
||||
opts = append(opts, dbcache.WithCacheDuration(time.Minute*30))
|
||||
opts = append(opts, dbcache.WithDependency(dbDependency))
|
||||
} else {
|
||||
opts = append(opts, dbcache.WithCacheDuration(time.Minute))
|
||||
}
|
||||
if pairs, err = dbcache.TryCache(ctx.Context(), fmt.Sprintf("rest:kvpairs:%s:%s:%s:%s", moduleName, tableName, labelColumn, valueColumn), func(tx *gorm.DB) ([]*restTypes.TypeValue[any], error) {
|
||||
return rest.ModelTypes[any](ctx.Context(), db, modelValue, "", labelColumn, valueColumn)
|
||||
}, opts...); err == nil {
|
||||
return ctx.Success(pairs)
|
||||
} else {
|
||||
return ctx.Error(errors.Unavailable, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
handleTierPairs := func(ctx *http.Context) (err error) {
|
||||
var (
|
||||
dbDependency dbcache.CacheDependency
|
||||
pairs []*restTypes.TierValue[string]
|
||||
modelValue any
|
||||
)
|
||||
moduleName := ctx.Param("module")
|
||||
tableName := ctx.Param("table")
|
||||
entities := rest.GetModels()
|
||||
for _, entry := range entities {
|
||||
if entry.ModuleName() == moduleName && entry.TableName() == tableName {
|
||||
// 权限控制
|
||||
if err = entry.HasPermission(ctx.Context(), entry.Permission(restTypes.ScenarioList)); err != nil {
|
||||
return ctx.Error(errors.AccessDenied, err.Error())
|
||||
}
|
||||
modelValue = reflect.New(entry.Value().Type()).Interface()
|
||||
for _, field := range entry.Fields() {
|
||||
if field.AutoUpdateTime > 0 {
|
||||
dbDependency = dbcache.NewSqlDependency(fmt.Sprintf("SELECT MAX(`%s`) FROM `%s`", field.DBName, entry.TableName()))
|
||||
break
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if modelValue == nil {
|
||||
return ctx.Error(errors.NotFound, "model not found")
|
||||
}
|
||||
parentColumn := ctx.Param("parent")
|
||||
labelColumn := ctx.Param("label")
|
||||
valueColumn := ctx.Param("value")
|
||||
opts := make([]dbcache.CacheOption, 0, 4)
|
||||
opts = append(opts, dbcache.WithDB(db))
|
||||
if dbDependency != nil {
|
||||
opts = append(opts, dbcache.WithCacheDuration(time.Minute*30))
|
||||
opts = append(opts, dbcache.WithDependency(dbDependency))
|
||||
} else {
|
||||
opts = append(opts, dbcache.WithCacheDuration(time.Minute))
|
||||
}
|
||||
if pairs, err = dbcache.TryCache(ctx.Context(), fmt.Sprintf("rest:tierpairs:%s:%s:%s:%s", moduleName, tableName, labelColumn, valueColumn), func(tx *gorm.DB) ([]*restTypes.TierValue[string], error) {
|
||||
return rest.ModelTiers[string](ctx.Context(), db, modelValue, "", parentColumn, labelColumn, valueColumn)
|
||||
}, opts...); err == nil {
|
||||
return ctx.Success(pairs)
|
||||
} else {
|
||||
return ctx.Error(errors.Unavailable, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
handleNumberTierPairs := func(ctx *http.Context) (err error) {
|
||||
var (
|
||||
dbDependency dbcache.CacheDependency
|
||||
pairs []*restTypes.TierValue[int64]
|
||||
modelValue any
|
||||
)
|
||||
moduleName := ctx.Param("module")
|
||||
tableName := ctx.Param("table")
|
||||
entities := rest.GetModels()
|
||||
for _, entry := range entities {
|
||||
if entry.ModuleName() == moduleName && entry.TableName() == tableName {
|
||||
// 权限控制
|
||||
if err = entry.HasPermission(ctx.Context(), entry.Permission(restTypes.ScenarioList)); err != nil {
|
||||
return ctx.Error(errors.AccessDenied, err.Error())
|
||||
}
|
||||
modelValue = reflect.New(entry.Value().Type()).Interface()
|
||||
for _, field := range entry.Fields() {
|
||||
if field.AutoUpdateTime > 0 {
|
||||
dbDependency = dbcache.NewSqlDependency(fmt.Sprintf("SELECT MAX(`%s`) FROM `%s`", field.DBName, entry.TableName()))
|
||||
break
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if modelValue == nil {
|
||||
return ctx.Error(errors.NotFound, "model not found")
|
||||
}
|
||||
parentColumn := ctx.Param("parent")
|
||||
labelColumn := ctx.Param("label")
|
||||
valueColumn := ctx.Param("value")
|
||||
opts := make([]dbcache.CacheOption, 0, 4)
|
||||
opts = append(opts, dbcache.WithDB(db))
|
||||
if dbDependency != nil {
|
||||
opts = append(opts, dbcache.WithCacheDuration(time.Minute*30))
|
||||
opts = append(opts, dbcache.WithDependency(dbDependency))
|
||||
} else {
|
||||
opts = append(opts, dbcache.WithCacheDuration(time.Minute))
|
||||
}
|
||||
if pairs, err = dbcache.TryCache(ctx.Context(), fmt.Sprintf("rest:tierpairs:%s:%s:%s:%s", moduleName, tableName, labelColumn, valueColumn), func(tx *gorm.DB) ([]*restTypes.TierValue[int64], error) {
|
||||
return rest.ModelTiers[int64](ctx.Context(), db, modelValue, "", parentColumn, labelColumn, valueColumn)
|
||||
}, opts...); err == nil {
|
||||
return ctx.Success(pairs)
|
||||
} else {
|
||||
return ctx.Error(errors.Unavailable, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
hs.GET("/rest/schema/:module/:table", handleListSchemas)
|
||||
hs.PUT("/rest/schema/:module/:table", handleUpdateSchemas)
|
||||
hs.DELETE("/rest/schema/:id", handleDeleteSchema)
|
||||
|
||||
hs.GET("/rest/kvpairs/:module/:table/:value/:label", handleKeyValues) //处理键值对数据
|
||||
hs.GET("/rest/tierpairs/str/:module/:table/:parent/:value/:label", handleTierPairs) //处理字符串类型的层级数据
|
||||
hs.GET("/rest/tierpairs/num/:module/:table/:parent/:value/:label", handleNumberTierPairs) //处理数字类型的层级数据, 只支持int64
|
||||
}
|
||||
|
||||
// AutoMigrate 自动生成一个模型的schema和权限的定义
|
||||
|
|
|
@ -17,6 +17,7 @@ type (
|
|||
|
||||
SettingOption func(o *settingOptions)
|
||||
)
|
||||
|
||||
type SettingService struct {
|
||||
opts *settingOptions
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue