From a64f61e81f55d3cf7de1a07e5b5a46c64adbc902 Mon Sep 17 00:00:00 2001 From: shanyujie <1196661499@qq.com> Date: Thu, 11 Dec 2025 13:40:07 +0800 Subject: [PATCH 1/4] =?UTF-8?q?refactor(database):=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E8=BD=AF=E5=88=A0=E9=99=A4=E6=97=B6=E9=97=B4=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- database/gdb/gdb_model_delete.go | 6 +- database/gdb/gdb_model_insert.go | 12 +- database/gdb/gdb_model_select.go | 2 +- database/gdb/gdb_model_soft_time.go | 372 +++++++++++----------------- database/gdb/gdb_model_update.go | 8 +- 5 files changed, 160 insertions(+), 240 deletions(-) diff --git a/database/gdb/gdb_model_delete.go b/database/gdb/gdb_model_delete.go index f8811c086aa..ba7d566dfa5 100644 --- a/database/gdb/gdb_model_delete.go +++ b/database/gdb/gdb_model_delete.go @@ -31,8 +31,8 @@ func (m *Model) Delete(where ...any) (result sql.Result, err error) { var ( conditionWhere, conditionExtra, conditionArgs = m.formatCondition(ctx, false, false) conditionStr = conditionWhere + conditionExtra - fieldNameDelete, fieldTypeDelete = m.softTimeMaintainer().GetFieldNameAndTypeForDelete( - ctx, "", m.tablesInit, + fieldNameDelete, fieldTypeDelete = m.softTimeMaintainer().GetFieldInfo( + ctx, "", m.tablesInit, SoftTimeFieldDelete, ) ) if m.unscoped { @@ -52,7 +52,7 @@ func (m *Model) Delete(where ...any) (result sql.Result, err error) { // Soft deleting. if fieldNameDelete != "" { - dataHolder, dataValue := m.softTimeMaintainer().GetDataByFieldNameAndTypeForDelete( + dataHolder, dataValue := m.softTimeMaintainer().GetDeleteData( ctx, "", fieldNameDelete, fieldTypeDelete, ) in := &HookUpdateInput{ diff --git a/database/gdb/gdb_model_insert.go b/database/gdb/gdb_model_insert.go index a322d75f43f..035af1f4f0b 100644 --- a/database/gdb/gdb_model_insert.go +++ b/database/gdb/gdb_model_insert.go @@ -262,9 +262,9 @@ func (m *Model) doInsertWithOption(ctx context.Context, insertOption InsertOptio var ( list List stm = m.softTimeMaintainer() - fieldNameCreate, fieldTypeCreate = stm.GetFieldNameAndTypeForCreate(ctx, "", m.tablesInit) - fieldNameUpdate, fieldTypeUpdate = stm.GetFieldNameAndTypeForUpdate(ctx, "", m.tablesInit) - fieldNameDelete, fieldTypeDelete = stm.GetFieldNameAndTypeForDelete(ctx, "", m.tablesInit) + fieldNameCreate, fieldTypeCreate = stm.GetFieldInfo(ctx, "", m.tablesInit, SoftTimeFieldCreate) + fieldNameUpdate, fieldTypeUpdate = stm.GetFieldInfo(ctx, "", m.tablesInit, SoftTimeFieldUpdate) + fieldNameDelete, fieldTypeDelete = stm.GetFieldInfo(ctx, "", m.tablesInit, SoftTimeFieldDelete) ) // m.data was already converted to type List/Map by function Data newData, err := m.filterDataForInsertOrUpdate(m.data) @@ -295,20 +295,20 @@ func (m *Model) doInsertWithOption(ctx context.Context, insertOption InsertOptio if !m.unscoped && isSoftTimeFeatureEnabled { for k, v := range list { if fieldNameCreate != "" && empty.IsNil(v[fieldNameCreate]) { - fieldCreateValue := stm.GetValueByFieldTypeForCreateOrUpdate(ctx, fieldTypeCreate, false) + fieldCreateValue := stm.GetFieldValue(ctx, fieldTypeCreate, false) if fieldCreateValue != nil { v[fieldNameCreate] = fieldCreateValue } } if fieldNameUpdate != "" && empty.IsNil(v[fieldNameUpdate]) { - fieldUpdateValue := stm.GetValueByFieldTypeForCreateOrUpdate(ctx, fieldTypeUpdate, false) + fieldUpdateValue := stm.GetFieldValue(ctx, fieldTypeUpdate, false) if fieldUpdateValue != nil { v[fieldNameUpdate] = fieldUpdateValue } } // for timestamp field that should initialize the delete_at field with value, for example 0. if fieldNameDelete != "" && empty.IsNil(v[fieldNameDelete]) { - fieldDeleteValue := stm.GetValueByFieldTypeForCreateOrUpdate(ctx, fieldTypeDelete, true) + fieldDeleteValue := stm.GetFieldValue(ctx, fieldTypeDelete, true) if fieldDeleteValue != nil { v[fieldNameDelete] = fieldDeleteValue } diff --git a/database/gdb/gdb_model_select.go b/database/gdb/gdb_model_select.go index 00317629513..1b1220fe778 100644 --- a/database/gdb/gdb_model_select.go +++ b/database/gdb/gdb_model_select.go @@ -941,7 +941,7 @@ func (m *Model) formatCondition( } // WHERE conditionWhere, conditionArgs = m.whereBuilder.Build() - softDeletingCondition := m.softTimeMaintainer().GetWhereConditionForDelete(ctx) + softDeletingCondition := m.softTimeMaintainer().GetDeleteCondition(ctx) if m.rawSql != "" && conditionWhere != "" { if gstr.ContainsI(m.rawSql, " WHERE ") { conditionWhere = " AND " + conditionWhere diff --git a/database/gdb/gdb_model_soft_time.go b/database/gdb/gdb_model_soft_time.go index 1ddcdcb12a8..c609bf48686 100644 --- a/database/gdb/gdb_model_soft_time.go +++ b/database/gdb/gdb_model_soft_time.go @@ -43,28 +43,27 @@ type softTimeMaintainer struct { *Model } -type iSoftTimeMaintainer interface { - GetFieldNameAndTypeForCreate( - ctx context.Context, schema string, table string, - ) (fieldName string, fieldType LocalType) +// SoftTimeFieldType represents different soft time field purposes +type SoftTimeFieldType int - GetFieldNameAndTypeForUpdate( - ctx context.Context, schema string, table string, - ) (fieldName string, fieldType LocalType) +const ( + SoftTimeFieldCreate SoftTimeFieldType = iota + SoftTimeFieldUpdate + SoftTimeFieldDelete +) - GetFieldNameAndTypeForDelete( - ctx context.Context, schema string, table string, - ) (fieldName string, fieldType LocalType) +type iSoftTimeMaintainer interface { + // GetFieldInfo returns field name and type for specified field purpose + GetFieldInfo(ctx context.Context, schema, table string, fieldType SoftTimeFieldType) (fieldName string, localType LocalType) - GetValueByFieldTypeForCreateOrUpdate( - ctx context.Context, fieldType LocalType, isDeletedField bool, - ) (dataValue any) + // GetFieldValue generates value for create/update/delete operations + GetFieldValue(ctx context.Context, localType LocalType, isDeleted bool) any - GetDataByFieldNameAndTypeForDelete( - ctx context.Context, fieldPrefix, fieldName string, fieldType LocalType, - ) (dataHolder string, dataValue any) + // GetDeleteCondition returns WHERE condition for soft delete query + GetDeleteCondition(ctx context.Context) string - GetWhereConditionForDelete(ctx context.Context) string + // GetDeleteData returns UPDATE statement data for soft delete + GetDeleteData(ctx context.Context, prefix, fieldName string, localType LocalType) (holder string, value any) } // getSoftFieldNameAndTypeCacheItem is the internal struct for storing create/update/delete fields. @@ -102,137 +101,84 @@ func (m *Model) softTimeMaintainer() iSoftTimeMaintainer { } } -// GetFieldNameAndTypeForCreate checks and returns the field name for record creating time. -// If there's no field name for storing creating time, it returns an empty string. +// GetFieldInfo returns field name and type for specified field purpose. // It checks the key with or without cases or chars '-'/'_'/'.'/' '. -func (m *softTimeMaintainer) GetFieldNameAndTypeForCreate( - ctx context.Context, schema string, table string, -) (fieldName string, fieldType LocalType) { - // It checks whether this feature disabled. +func (m *softTimeMaintainer) GetFieldInfo( + ctx context.Context, schema, table string, fieldType SoftTimeFieldType, +) (fieldName string, localType LocalType) { + // Check if feature is disabled if m.db.GetConfig().TimeMaintainDisabled { return "", LocalTypeUndefined } - tableName := "" - if table != "" { - tableName = table - } else { - tableName = m.tablesInit - } - config := m.db.GetConfig() - if config.CreatedAt != "" { - return m.getSoftFieldNameAndType( - ctx, schema, tableName, []string{config.CreatedAt}, - ) - } - return m.getSoftFieldNameAndType( - ctx, schema, tableName, createdFieldNames, - ) -} -// GetFieldNameAndTypeForUpdate checks and returns the field name for record updating time. -// If there's no field name for storing updating time, it returns an empty string. -// It checks the key with or without cases or chars '-'/'_'/'.'/' '. -func (m *softTimeMaintainer) GetFieldNameAndTypeForUpdate( - ctx context.Context, schema string, table string, -) (fieldName string, fieldType LocalType) { - // It checks whether this feature disabled. - if m.db.GetConfig().TimeMaintainDisabled { - return "", LocalTypeUndefined - } - tableName := "" - if table != "" { - tableName = table - } else { + // Determine table name + tableName := table + if tableName == "" { tableName = m.tablesInit } + + // Get config and field candidates config := m.db.GetConfig() - if config.UpdatedAt != "" { - return m.getSoftFieldNameAndType( - ctx, schema, tableName, []string{config.UpdatedAt}, - ) - } - return m.getSoftFieldNameAndType( - ctx, schema, tableName, updatedFieldNames, + var ( + configField string + defaultFields []string ) -} -// GetFieldNameAndTypeForDelete checks and returns the field name for record deleting time. -// If there's no field name for storing deleting time, it returns an empty string. -// It checks the key with or without cases or chars '-'/'_'/'.'/' '. -func (m *softTimeMaintainer) GetFieldNameAndTypeForDelete( - ctx context.Context, schema string, table string, -) (fieldName string, fieldType LocalType) { - // It checks whether this feature disabled. - if m.db.GetConfig().TimeMaintainDisabled { - return "", LocalTypeUndefined + switch fieldType { + case SoftTimeFieldCreate: + configField = config.CreatedAt + defaultFields = createdFieldNames + case SoftTimeFieldUpdate: + configField = config.UpdatedAt + defaultFields = updatedFieldNames + case SoftTimeFieldDelete: + configField = config.DeletedAt + defaultFields = deletedFieldNames } - tableName := "" - if table != "" { - tableName = table - } else { - tableName = m.tablesInit - } - config := m.db.GetConfig() - if config.DeletedAt != "" { - return m.getSoftFieldNameAndType( - ctx, schema, tableName, []string{config.DeletedAt}, - ) + + // Use config field if specified, otherwise use defaults + if configField != "" { + return m.getSoftFieldNameAndType(ctx, schema, tableName, []string{configField}) } - return m.getSoftFieldNameAndType( - ctx, schema, tableName, deletedFieldNames, - ) + return m.getSoftFieldNameAndType(ctx, schema, tableName, defaultFields) } // getSoftFieldNameAndType retrieves and returns the field name of the table for possible key. func (m *softTimeMaintainer) getSoftFieldNameAndType( - ctx context.Context, - schema string, table string, checkFiledNames []string, + ctx context.Context, schema, table string, candidates []string, ) (fieldName string, fieldType LocalType) { - var ( - innerMemCache = m.db.GetCore().GetInnerMemCache() - cacheKey = fmt.Sprintf( - `getSoftFieldNameAndType:%s#%s#%s`, - schema, table, strings.Join(checkFiledNames, "_"), - ) - cacheDuration = gcache.DurationNoExpire - cacheFunc = func(ctx context.Context) (value any, err error) { - // Ignore the error from TableFields. - fieldsMap, err := m.TableFields(table, schema) - if err != nil { - return nil, err - } - if len(fieldsMap) == 0 { - return nil, nil - } - for _, checkFiledName := range checkFiledNames { - fieldName = searchFieldNameFromMap(fieldsMap, checkFiledName) - if fieldName != "" { - fieldType, _ = m.db.CheckLocalTypeForField( - ctx, fieldsMap[fieldName].Type, nil, - ) - var cacheItem = getSoftFieldNameAndTypeCacheItem{ - FieldName: fieldName, - FieldType: fieldType, - } - return cacheItem, nil - } + // Build cache key + cacheKey := fmt.Sprintf(`soft_field:%s:%s:%s`, + schema, table, strings.Join(candidates, ",")) + + // Try to get from cache + cache := m.db.GetCore().GetInnerMemCache() + result, err := cache.GetOrSetFunc(ctx, cacheKey, func(ctx context.Context) (any, error) { + // Get table fields + fieldsMap, err := m.TableFields(table, schema) + if err != nil || len(fieldsMap) == 0 { + return nil, err + } + + // Search for matching field + for _, candidate := range candidates { + if name := searchFieldNameFromMap(fieldsMap, candidate); name != "" { + fType, _ := m.db.CheckLocalTypeForField(ctx, fieldsMap[name].Type, nil) + return getSoftFieldNameAndTypeCacheItem{ + FieldName: name, + FieldType: fType, + }, nil } - return } - ) - result, err := innerMemCache.GetOrSetFunc( - ctx, cacheKey, cacheFunc, cacheDuration, - ) - if err != nil { - return - } - if result == nil { - return + return nil, nil + }, gcache.DurationNoExpire) + + if err != nil || result == nil { + return "", LocalTypeUndefined } - cacheItem := result.Val().(getSoftFieldNameAndTypeCacheItem) - fieldName = cacheItem.FieldName - fieldType = cacheItem.FieldType - return + + item := result.Val().(getSoftFieldNameAndTypeCacheItem) + return item.FieldName, item.FieldType } func searchFieldNameFromMap(fieldsMap map[string]*TableField, key string) string { @@ -252,13 +198,13 @@ func searchFieldNameFromMap(fieldsMap map[string]*TableField, key string) string return "" } -// GetWhereConditionForDelete retrieves and returns the condition string for soft deleting. +// GetDeleteCondition returns WHERE condition for soft delete query. // It supports multiple tables string like: // "user u, user_detail ud" // "user u LEFT JOIN user_detail ud ON(ud.uid=u.uid)" // "user LEFT JOIN user_detail ON(user_detail.uid=user.uid)" // "user u LEFT JOIN user_detail ud ON(ud.uid=u.uid) LEFT JOIN user_stats us ON(us.uid=u.uid)". -func (m *softTimeMaintainer) GetWhereConditionForDelete(ctx context.Context) string { +func (m *softTimeMaintainer) GetDeleteCondition(ctx context.Context) string { if m.unscoped { return "" } @@ -284,9 +230,9 @@ func (m *softTimeMaintainer) GetWhereConditionForDelete(ctx context.Context) str return conditionArray.Join(" AND ") } // Only one table. - fieldName, fieldType := m.GetFieldNameAndTypeForDelete(ctx, "", m.tablesInit) + fieldName, fieldType := m.GetFieldInfo(ctx, "", m.tablesInit, SoftTimeFieldDelete) if fieldName != "" { - return m.getConditionByFieldNameAndTypeForSoftDeleting(ctx, "", fieldName, fieldType) + return m.buildDeleteCondition(ctx, "", fieldName, fieldType) } return "" } @@ -310,141 +256,117 @@ func (m *softTimeMaintainer) getConditionOfTableStringForSoftDeleting(ctx contex } else { table = array2[0] } - fieldName, fieldType := m.GetFieldNameAndTypeForDelete(ctx, schema, table) + fieldName, fieldType := m.GetFieldInfo(ctx, schema, table, SoftTimeFieldDelete) if fieldName == "" { return "" } if len(array1) >= 3 { - return m.getConditionByFieldNameAndTypeForSoftDeleting(ctx, array1[2], fieldName, fieldType) + return m.buildDeleteCondition(ctx, array1[2], fieldName, fieldType) } if len(array1) >= 2 { - return m.getConditionByFieldNameAndTypeForSoftDeleting(ctx, array1[1], fieldName, fieldType) + return m.buildDeleteCondition(ctx, array1[1], fieldName, fieldType) } - return m.getConditionByFieldNameAndTypeForSoftDeleting(ctx, table, fieldName, fieldType) + return m.buildDeleteCondition(ctx, table, fieldName, fieldType) } -// GetDataByFieldNameAndTypeForDelete creates and returns the placeholder and value for -// specified field name and type in soft-deleting scenario. -func (m *softTimeMaintainer) GetDataByFieldNameAndTypeForDelete( - ctx context.Context, fieldPrefix, fieldName string, fieldType LocalType, -) (dataHolder string, dataValue any) { - var ( - quotedFieldPrefix = m.db.GetCore().QuoteWord(fieldPrefix) - quotedFieldName = m.db.GetCore().QuoteWord(fieldName) - ) - if quotedFieldPrefix != "" { - quotedFieldName = fmt.Sprintf(`%s.%s`, quotedFieldPrefix, quotedFieldName) +// GetDeleteData returns UPDATE statement data for soft delete. +func (m *softTimeMaintainer) GetDeleteData( + ctx context.Context, prefix, fieldName string, fieldType LocalType, +) (holder string, value any) { + core := m.db.GetCore() + quotedName := core.QuoteWord(fieldName) + + if prefix != "" { + quotedName = fmt.Sprintf(`%s.%s`, core.QuoteWord(prefix), quotedName) } - dataHolder = fmt.Sprintf(`%s=?`, quotedFieldName) - dataValue = m.GetValueByFieldTypeForCreateOrUpdate(ctx, fieldType, false) + + holder = fmt.Sprintf(`%s=?`, quotedName) + value = m.GetFieldValue(ctx, fieldType, false) return } -func (m *softTimeMaintainer) getConditionByFieldNameAndTypeForSoftDeleting( - ctx context.Context, fieldPrefix, fieldName string, fieldType LocalType, +// buildDeleteCondition builds WHERE condition for soft delete filtering. +func (m *softTimeMaintainer) buildDeleteCondition( + ctx context.Context, prefix, fieldName string, fieldType LocalType, ) string { - var ( - quotedFieldPrefix = m.db.GetCore().QuoteWord(fieldPrefix) - quotedFieldName = m.db.GetCore().QuoteWord(fieldName) - ) - if quotedFieldPrefix != "" { - quotedFieldName = fmt.Sprintf(`%s.%s`, quotedFieldPrefix, quotedFieldName) + core := m.db.GetCore() + quotedName := core.QuoteWord(fieldName) + + if prefix != "" { + quotedName = fmt.Sprintf(`%s.%s`, core.QuoteWord(prefix), quotedName) } switch m.softTimeOption.SoftTimeType { case SoftTimeTypeAuto: switch fieldType { case LocalTypeDate, LocalTypeTime, LocalTypeDatetime: - return fmt.Sprintf(`%s IS NULL`, quotedFieldName) + return fmt.Sprintf(`%s IS NULL`, quotedName) case LocalTypeInt, LocalTypeUint, LocalTypeInt64, LocalTypeUint64, LocalTypeBool: - return fmt.Sprintf(`%s=0`, quotedFieldName) + return fmt.Sprintf(`%s=0`, quotedName) default: - intlog.Errorf( - ctx, - `invalid field type "%s" of field name "%s" with prefix "%s" for soft deleting condition`, - fieldType, fieldName, fieldPrefix, - ) + intlog.Errorf(ctx, `invalid field type "%s" for soft delete condition: %s`, fieldType, fieldName) + return "" } case SoftTimeTypeTime: - return fmt.Sprintf(`%s IS NULL`, quotedFieldName) + return fmt.Sprintf(`%s IS NULL`, quotedName) default: - return fmt.Sprintf(`%s=0`, quotedFieldName) + return fmt.Sprintf(`%s=0`, quotedName) } - return "" } -// GetValueByFieldTypeForCreateOrUpdate creates and returns the value for specified field type, -// usually for creating or updating operations. -func (m *softTimeMaintainer) GetValueByFieldTypeForCreateOrUpdate( - ctx context.Context, fieldType LocalType, isDeletedField bool, +// GetFieldValue generates value for create/update/delete operations. +func (m *softTimeMaintainer) GetFieldValue( + ctx context.Context, fieldType LocalType, isDeleted bool, ) any { - var value any - // for create or update procedure, the deleted field is always set to non-deleted value. - if isDeletedField { - switch fieldType { - case LocalTypeDate, LocalTypeTime, LocalTypeDatetime: - value = nil - default: - value = 0 - } - return value - } - switch m.softTimeOption.SoftTimeType { - case SoftTimeTypeAuto: - switch fieldType { - case LocalTypeDate, LocalTypeTime, LocalTypeDatetime: - value = gtime.Now() - case LocalTypeInt, LocalTypeUint, LocalTypeInt64, LocalTypeUint64: - value = gtime.Timestamp() - case LocalTypeBool: - value = 1 - default: - intlog.Errorf( - ctx, - `invalid field type "%s" for soft deleting data`, - fieldType, - ) - } - - default: - switch fieldType { - case LocalTypeBool: - value = 1 - default: - value = m.createValueBySoftTimeOption(isDeletedField) - } + // For deleted field, return "empty" value + if isDeleted { + return m.getEmptyValue(fieldType) } - return value -} -func (m *softTimeMaintainer) createValueBySoftTimeOption(isDeletedField bool) any { - var value any - if isDeletedField { - switch m.softTimeOption.SoftTimeType { - case SoftTimeTypeTime: - value = nil - default: - value = 0 - } - return value - } + // For create/update, return current time value switch m.softTimeOption.SoftTimeType { + case SoftTimeTypeAuto: + return m.getAutoValue(ctx, fieldType) case SoftTimeTypeTime: - value = gtime.Now() + return gtime.Now() case SoftTimeTypeTimestamp: - value = gtime.Timestamp() + return gtime.Timestamp() case SoftTimeTypeTimestampMilli: - value = gtime.TimestampMilli() + return gtime.TimestampMilli() case SoftTimeTypeTimestampMicro: - value = gtime.TimestampMicro() + return gtime.TimestampMicro() case SoftTimeTypeTimestampNano: - value = gtime.TimestampNano() + return gtime.TimestampNano() default: panic(gerror.NewCodef( gcode.CodeInternalPanic, `unrecognized SoftTimeType "%d"`, m.softTimeOption.SoftTimeType, )) } - return value +} + +// getEmptyValue returns "empty" value for deleted field. +func (m *softTimeMaintainer) getEmptyValue(fieldType LocalType) any { + switch fieldType { + case LocalTypeDate, LocalTypeTime, LocalTypeDatetime: + return nil + default: + return 0 + } +} + +// getAutoValue returns auto-detected value based on field type. +func (m *softTimeMaintainer) getAutoValue(ctx context.Context, fieldType LocalType) any { + switch fieldType { + case LocalTypeDate, LocalTypeTime, LocalTypeDatetime: + return gtime.Now() + case LocalTypeInt, LocalTypeUint, LocalTypeInt64, LocalTypeUint64: + return gtime.Timestamp() + case LocalTypeBool: + return 1 + default: + intlog.Errorf(ctx, `invalid field type "%s" for soft time auto value`, fieldType) + return nil + } } diff --git a/database/gdb/gdb_model_update.go b/database/gdb/gdb_model_update.go index 05e430b1386..714c6d06e0f 100644 --- a/database/gdb/gdb_model_update.go +++ b/database/gdb/gdb_model_update.go @@ -50,9 +50,7 @@ func (m *Model) Update(dataAndWhere ...any) (result sql.Result, err error) { reflectInfo = reflection.OriginTypeAndKind(m.data) conditionWhere, conditionExtra, conditionArgs = m.formatCondition(ctx, false, false) conditionStr = conditionWhere + conditionExtra - fieldNameUpdate, fieldTypeUpdate = stm.GetFieldNameAndTypeForUpdate( - ctx, "", m.tablesInit, - ) + fieldNameUpdate, fieldTypeUpdate = stm.GetFieldInfo(ctx, "", m.tablesInit, SoftTimeFieldUpdate) ) if fieldNameUpdate != "" && (m.unscoped || m.isFieldInFieldsEx(fieldNameUpdate)) { fieldNameUpdate = "" @@ -68,7 +66,7 @@ func (m *Model) Update(dataAndWhere ...any) (result sql.Result, err error) { var dataMap = anyValueToMapBeforeToRecord(newData) // Automatically update the record updating time. if fieldNameUpdate != "" && empty.IsNil(dataMap[fieldNameUpdate]) { - dataValue := stm.GetValueByFieldTypeForCreateOrUpdate(ctx, fieldTypeUpdate, false) + dataValue := stm.GetFieldValue(ctx, fieldTypeUpdate, false) dataMap[fieldNameUpdate] = dataValue } newData = dataMap @@ -77,7 +75,7 @@ func (m *Model) Update(dataAndWhere ...any) (result sql.Result, err error) { var updateStr = gconv.String(newData) // Automatically update the record updating time. if fieldNameUpdate != "" && !gstr.Contains(updateStr, fieldNameUpdate) { - dataValue := stm.GetValueByFieldTypeForCreateOrUpdate(ctx, fieldTypeUpdate, false) + dataValue := stm.GetFieldValue(ctx, fieldTypeUpdate, false) updateStr += fmt.Sprintf(`,%s=?`, fieldNameUpdate) conditionArgs = append([]any{dataValue}, conditionArgs...) } From 2d532170584b618d9f26af6277199d7d792c6a0e Mon Sep 17 00:00:00 2001 From: shanyujie <1196661499@qq.com> Date: Thu, 11 Dec 2025 14:30:35 +0800 Subject: [PATCH 2/4] =?UTF-8?q?feat(database):=20=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E8=BD=AF=E5=88=A0=E9=99=A4=E6=97=B6=E9=97=B4=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E7=9A=84=E5=A4=9A=E7=A7=8D=E7=B1=BB=E5=9E=8B=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- database/gdb/gdb_model_soft_time.go | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/database/gdb/gdb_model_soft_time.go b/database/gdb/gdb_model_soft_time.go index c609bf48686..f4b8b8da4c9 100644 --- a/database/gdb/gdb_model_soft_time.go +++ b/database/gdb/gdb_model_soft_time.go @@ -324,10 +324,23 @@ func (m *softTimeMaintainer) GetFieldValue( return m.getEmptyValue(fieldType) } - // For create/update, return current time value + // For create/update/delete, return current time value switch m.softTimeOption.SoftTimeType { case SoftTimeTypeAuto: return m.getAutoValue(ctx, fieldType) + default: + switch fieldType { + case LocalTypeBool: + return 1 + default: + return m.getTimestampValue() + } + } +} + +// getTimestampValue returns timestamp value for soft time. +func (m *softTimeMaintainer) getTimestampValue() any { + switch m.softTimeOption.SoftTimeType { case SoftTimeTypeTime: return gtime.Now() case SoftTimeTypeTimestamp: @@ -370,3 +383,15 @@ func (m *softTimeMaintainer) getAutoValue(ctx context.Context, fieldType LocalTy return nil } } + +// getTimeValue returns time value for datetime field type. +// For non-datetime types, returns 1 as a fallback marker. +func (m *softTimeMaintainer) getTimeValue(fieldType LocalType) any { + switch fieldType { + case LocalTypeDate, LocalTypeTime, LocalTypeDatetime: + return gtime.Now() + default: + // For Bool or other types, use 1 as delete marker + return 1 + } +} From bb3144c01c42323dece95d03cf86690a059b414d Mon Sep 17 00:00:00 2001 From: shanyujie <1196661499@qq.com> Date: Thu, 11 Dec 2025 14:48:34 +0800 Subject: [PATCH 3/4] =?UTF-8?q?refactor(gdb):=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E9=94=AE=E7=94=9F=E6=88=90=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- database/gdb/gdb_core.go | 2 +- database/gdb/gdb_func.go | 12 ++++++++++++ database/gdb/gdb_model_soft_time.go | 9 ++++----- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 56d10bd35fe..4bb712c02a1 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -787,7 +787,7 @@ func (c *Core) SetTableFields(ctx context.Context, table string, fields map[stri func (c *Core) GetTablesWithCache() ([]string, error) { var ( ctx = c.db.GetCtx() - cacheKey = fmt.Sprintf(`Tables:%s`, c.db.GetGroup()) + cacheKey = genTableNamesCacheKey(c.db.GetGroup()) cacheDuration = gcache.DurationNoExpire innerMemCache = c.GetInnerMemCache() ) diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index 66f5dc0fb8b..a4f115c20f7 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -966,6 +966,7 @@ func FormatMultiLineSqlToSingle(sql string) (string, error) { return sql, nil } +// genTableFieldsCacheKey generates cache key for table fields. func genTableFieldsCacheKey(group, schema, table string) string { return fmt.Sprintf( `%s%s@%s#%s`, @@ -976,6 +977,7 @@ func genTableFieldsCacheKey(group, schema, table string) string { ) } +// genSelectCacheKey generates cache key for select. func genSelectCacheKey(table, group, schema, name, sql string, args ...any) string { if name == "" { name = fmt.Sprintf( @@ -988,3 +990,13 @@ func genSelectCacheKey(table, group, schema, name, sql string, args ...any) stri } return fmt.Sprintf(`%s%s`, cachePrefixSelectCache, name) } + +// genTableNamesCacheKey generates cache key for table names. +func genTableNamesCacheKey(group string) string { + return fmt.Sprintf(`Tables:%s`, group) +} + +// genSoftTimeFieldNameTypeCacheKey generates cache key for soft time field name and type. +func genSoftTimeFieldNameTypeCacheKey(schema, table string, candidateFields []string) string { + return fmt.Sprintf(`getSoftFieldNameAndType:%s#%s#%s`, schema, table, strings.Join(candidateFields, ",")) +} diff --git a/database/gdb/gdb_model_soft_time.go b/database/gdb/gdb_model_soft_time.go index f4b8b8da4c9..92d030f7297 100644 --- a/database/gdb/gdb_model_soft_time.go +++ b/database/gdb/gdb_model_soft_time.go @@ -145,11 +145,10 @@ func (m *softTimeMaintainer) GetFieldInfo( // getSoftFieldNameAndType retrieves and returns the field name of the table for possible key. func (m *softTimeMaintainer) getSoftFieldNameAndType( - ctx context.Context, schema, table string, candidates []string, + ctx context.Context, schema, table string, candidateFields []string, ) (fieldName string, fieldType LocalType) { // Build cache key - cacheKey := fmt.Sprintf(`soft_field:%s:%s:%s`, - schema, table, strings.Join(candidates, ",")) + cacheKey := genSoftTimeFieldNameTypeCacheKey(schema, table, candidateFields) // Try to get from cache cache := m.db.GetCore().GetInnerMemCache() @@ -161,8 +160,8 @@ func (m *softTimeMaintainer) getSoftFieldNameAndType( } // Search for matching field - for _, candidate := range candidates { - if name := searchFieldNameFromMap(fieldsMap, candidate); name != "" { + for _, field := range candidateFields { + if name := searchFieldNameFromMap(fieldsMap, field); name != "" { fType, _ := m.db.CheckLocalTypeForField(ctx, fieldsMap[name].Type, nil) return getSoftFieldNameAndTypeCacheItem{ FieldName: name, From 1ef51a0d1ff5eb91afff773de21f55258cc8ab17 Mon Sep 17 00:00:00 2001 From: shanyujie <1196661499@qq.com> Date: Fri, 12 Dec 2025 09:42:11 +0800 Subject: [PATCH 4/4] =?UTF-8?q?refactor(database):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E8=BD=AF=E5=88=A0=E9=99=A4=E6=97=B6=E9=97=B4=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- database/gdb/gdb_func.go | 2 +- database/gdb/gdb_model_delete.go | 4 +--- database/gdb/gdb_model_soft_time.go | 30 +++++++++-------------------- 3 files changed, 11 insertions(+), 25 deletions(-) diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index a4f115c20f7..29b8537565b 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -998,5 +998,5 @@ func genTableNamesCacheKey(group string) string { // genSoftTimeFieldNameTypeCacheKey generates cache key for soft time field name and type. func genSoftTimeFieldNameTypeCacheKey(schema, table string, candidateFields []string) string { - return fmt.Sprintf(`getSoftFieldNameAndType:%s#%s#%s`, schema, table, strings.Join(candidateFields, ",")) + return fmt.Sprintf(`getSoftFieldNameAndType:%s#%s#%s`, schema, table, strings.Join(candidateFields, "_")) } diff --git a/database/gdb/gdb_model_delete.go b/database/gdb/gdb_model_delete.go index ba7d566dfa5..072d4e77ce0 100644 --- a/database/gdb/gdb_model_delete.go +++ b/database/gdb/gdb_model_delete.go @@ -31,9 +31,7 @@ func (m *Model) Delete(where ...any) (result sql.Result, err error) { var ( conditionWhere, conditionExtra, conditionArgs = m.formatCondition(ctx, false, false) conditionStr = conditionWhere + conditionExtra - fieldNameDelete, fieldTypeDelete = m.softTimeMaintainer().GetFieldInfo( - ctx, "", m.tablesInit, SoftTimeFieldDelete, - ) + fieldNameDelete, fieldTypeDelete = m.softTimeMaintainer().GetFieldInfo(ctx, "", m.tablesInit, SoftTimeFieldDelete) ) if m.unscoped { fieldNameDelete = "" diff --git a/database/gdb/gdb_model_soft_time.go b/database/gdb/gdb_model_soft_time.go index 92d030f7297..4deb39620ff 100644 --- a/database/gdb/gdb_model_soft_time.go +++ b/database/gdb/gdb_model_soft_time.go @@ -43,7 +43,7 @@ type softTimeMaintainer struct { *Model } -// SoftTimeFieldType represents different soft time field purposes +// SoftTimeFieldType represents different soft time field purposes. type SoftTimeFieldType int const ( @@ -53,16 +53,16 @@ const ( ) type iSoftTimeMaintainer interface { - // GetFieldInfo returns field name and type for specified field purpose - GetFieldInfo(ctx context.Context, schema, table string, fieldType SoftTimeFieldType) (fieldName string, localType LocalType) + // GetFieldInfo returns field name and type for specified field purpose. + GetFieldInfo(ctx context.Context, schema, table string, fieldPurpose SoftTimeFieldType) (fieldName string, localType LocalType) - // GetFieldValue generates value for create/update/delete operations + // GetFieldValue generates value for create/update/delete operations. GetFieldValue(ctx context.Context, localType LocalType, isDeleted bool) any - // GetDeleteCondition returns WHERE condition for soft delete query + // GetDeleteCondition returns WHERE condition for soft delete query. GetDeleteCondition(ctx context.Context) string - // GetDeleteData returns UPDATE statement data for soft delete + // GetDeleteData returns UPDATE statement data for soft delete. GetDeleteData(ctx context.Context, prefix, fieldName string, localType LocalType) (holder string, value any) } @@ -104,7 +104,7 @@ func (m *Model) softTimeMaintainer() iSoftTimeMaintainer { // GetFieldInfo returns field name and type for specified field purpose. // It checks the key with or without cases or chars '-'/'_'/'.'/' '. func (m *softTimeMaintainer) GetFieldInfo( - ctx context.Context, schema, table string, fieldType SoftTimeFieldType, + ctx context.Context, schema, table string, fieldPurpose SoftTimeFieldType, ) (fieldName string, localType LocalType) { // Check if feature is disabled if m.db.GetConfig().TimeMaintainDisabled { @@ -124,7 +124,7 @@ func (m *softTimeMaintainer) GetFieldInfo( defaultFields []string ) - switch fieldType { + switch fieldPurpose { case SoftTimeFieldCreate: configField = config.CreatedAt defaultFields = createdFieldNames @@ -302,7 +302,7 @@ func (m *softTimeMaintainer) buildDeleteCondition( case LocalTypeInt, LocalTypeUint, LocalTypeInt64, LocalTypeUint64, LocalTypeBool: return fmt.Sprintf(`%s=0`, quotedName) default: - intlog.Errorf(ctx, `invalid field type "%s" for soft delete condition: %s`, fieldType, fieldName) + intlog.Errorf(ctx, `invalid field type "%s" for soft delete condition: prefix=%s, field=%s`, fieldType, prefix, fieldName) return "" } @@ -382,15 +382,3 @@ func (m *softTimeMaintainer) getAutoValue(ctx context.Context, fieldType LocalTy return nil } } - -// getTimeValue returns time value for datetime field type. -// For non-datetime types, returns 1 as a fallback marker. -func (m *softTimeMaintainer) getTimeValue(fieldType LocalType) any { - switch fieldType { - case LocalTypeDate, LocalTypeTime, LocalTypeDatetime: - return gtime.Now() - default: - // For Bool or other types, use 1 as delete marker - return 1 - } -}