add AutoTimer interface
This commit is contained in:
parent
55594d1dbe
commit
71bd7f9b31
|
@ -0,0 +1,7 @@
|
|||
package convert
|
||||
|
||||
import "time"
|
||||
|
||||
type AutoTimer interface {
|
||||
AutoTime(t time.Time) (interface{}, error)
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
// Copyright 2017 The Xorm Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package integrations
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type Timestamp int64
|
||||
|
||||
func FromTimeToTimestamp(t time.Time) Timestamp {
|
||||
return Timestamp(int64(t.UnixNano()) / 1e6)
|
||||
}
|
||||
|
||||
func (t Timestamp) String() string {
|
||||
return strconv.FormatInt(int64(t), 10)
|
||||
}
|
||||
func (t *Timestamp) Time() time.Time {
|
||||
return time.Unix(int64(*t)/1e3, (int64(*t)%1e3)*1e6)
|
||||
}
|
||||
|
||||
func (t *Timestamp) FromDB(b []byte) error {
|
||||
var err error
|
||||
var value int64
|
||||
value, err = strconv.ParseInt(string(b), 10, 64)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
*t = Timestamp(value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Timestamp) ToDB() ([]byte, error) {
|
||||
if t == nil {
|
||||
return nil, nil
|
||||
}
|
||||
data := strconv.FormatInt(int64(*t), 10)
|
||||
if len(data) == 0 {
|
||||
return []byte("0"), nil
|
||||
}
|
||||
return []byte(data), nil
|
||||
}
|
||||
|
||||
func (t *Timestamp) AutoTime(now time.Time) (interface{}, error) {
|
||||
data := int64(now.UnixNano()) / 1e6
|
||||
return data, nil
|
||||
}
|
||||
|
||||
type AutoTimerStruct struct {
|
||||
ID string `xorm:"varchar(20) pk unique 'id'" json:"id"`
|
||||
Name string `xorm:"varchar(100) notnull" json:"username"`
|
||||
Description string `xorm:"text" json:"description"`
|
||||
|
||||
CreatedAt Timestamp `xorm:"created bigint notnull" json:"created_at"`
|
||||
UpdatedAt Timestamp `xorm:"updated bigint notnull" json:"updated_at"`
|
||||
DeletedAt *Timestamp `xorm:"deleted bigint null" json:"deleted_at"`
|
||||
}
|
||||
|
||||
func TestAutoTimerStructInsert(t *testing.T) {
|
||||
assert.NoError(t, PrepareEngine())
|
||||
assertSync(t, new(AutoTimerStruct))
|
||||
// insert
|
||||
id := strconv.FormatInt(time.Now().UnixNano(), 10)
|
||||
item := AutoTimerStruct{
|
||||
ID: id,
|
||||
Name: "AutoTimer Test:Insert",
|
||||
}
|
||||
_, err := testEngine.Insert(&item)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, id, item.ID)
|
||||
// get
|
||||
var result AutoTimerStruct
|
||||
has, err := testEngine.ID(id).Get(&result)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, has)
|
||||
assert.NotEmpty(t, result.CreatedAt)
|
||||
assert.NotEmpty(t, result.UpdatedAt)
|
||||
assert.Nil(t, item.DeletedAt)
|
||||
}
|
||||
func TestAutoTimerStructUpdate(t *testing.T) {
|
||||
assert.NoError(t, PrepareEngine())
|
||||
assertSync(t, new(AutoTimerStruct))
|
||||
// insert
|
||||
id := strconv.FormatInt(time.Now().UnixNano(), 10)
|
||||
item := AutoTimerStruct{
|
||||
ID: id,
|
||||
Name: "AutoTimer Test:Update",
|
||||
}
|
||||
_, err := testEngine.Insert(&item)
|
||||
assert.NoError(t, err)
|
||||
// update
|
||||
item.Description = "updated"
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
_, err = testEngine.ID(id).Update(&item)
|
||||
assert.NoError(t, err)
|
||||
assert.Greater(t, item.UpdatedAt, item.CreatedAt)
|
||||
assert.Nil(t, item.DeletedAt)
|
||||
}
|
||||
|
||||
func TestAutoTimerStructDelete(t *testing.T) {
|
||||
assert.NoError(t, PrepareEngine())
|
||||
assertSync(t, new(AutoTimerStruct))
|
||||
// insert
|
||||
id := strconv.FormatInt(time.Now().UnixNano(), 10)
|
||||
item := AutoTimerStruct{
|
||||
ID: id,
|
||||
Name: "AutoTimer Test:Delete",
|
||||
}
|
||||
_, err := testEngine.Insert(&item)
|
||||
assert.NoError(t, err)
|
||||
// delete
|
||||
_, err = testEngine.ID(id).Delete(&item)
|
||||
assert.NoError(t, err)
|
||||
// get
|
||||
var result AutoTimerStruct
|
||||
has, err := testEngine.ID(id).Get(&result)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, has)
|
||||
assert.NotEmpty(t, result.CreatedAt)
|
||||
assert.NotEmpty(t, result.UpdatedAt)
|
||||
assert.NotEmpty(t, result.DeletedAt)
|
||||
}
|
|
@ -152,3 +152,44 @@ func (statement *Statement) Value2Interface(col *schemas.Column, fieldValue refl
|
|||
return fieldValue.Interface(), nil
|
||||
}
|
||||
}
|
||||
|
||||
// IsAutoTimer a field value of a struct implements AutoTimer interface
|
||||
func (statement *Statement) IsAutoTimer(fieldValue reflect.Value, t time.Time) (interface{}, bool) {
|
||||
fieldType := fieldValue.Type()
|
||||
if fieldValue.CanAddr() {
|
||||
if fieldType.Kind() == reflect.Ptr {
|
||||
// log.Println("[IsAutoTimer] is ptr")
|
||||
if !fieldValue.IsZero() {
|
||||
fieldValue = fieldValue.Elem()
|
||||
fieldType = fieldValue.Type()
|
||||
} else {
|
||||
fieldValue = reflect.Zero(fieldType)
|
||||
}
|
||||
}
|
||||
|
||||
// log.Printf("[IsAutoTimer].1 type=%v,kind=%v,addr=%v\n", fieldValue.Type(), fieldValue.Type().Kind(), fieldValue.CanAddr())
|
||||
if fieldValue.CanAddr() {
|
||||
if fieldConvert, ok := fieldValue.Addr().Interface().(convert.AutoTimer); ok {
|
||||
val, err := fieldConvert.AutoTime(t)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
// log.Println("[IsAutoTimer] Addr.OK")
|
||||
return val, true
|
||||
}
|
||||
// log.Println("[IsAutoTimer] Addr.Fail")
|
||||
return nil, false
|
||||
}
|
||||
// log.Printf("[IsAutoTimer].2 type=%v,addr=%v\n", fieldValue.Type(), fieldValue.CanAddr())
|
||||
if fieldConvert, ok := fieldValue.Interface().(convert.AutoTimer); ok {
|
||||
val, err := fieldConvert.AutoTime(t)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
// log.Println("[IsAutoTimer] OK")
|
||||
return val, true
|
||||
}
|
||||
}
|
||||
// log.Println("[IsAutoTimer] Fail")
|
||||
return nil, false
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"xorm.io/xorm/convert"
|
||||
"xorm.io/xorm/schemas"
|
||||
)
|
||||
|
||||
|
@ -32,6 +33,15 @@ func setColumnTime(bean interface{}, col *schemas.Column, t time.Time) {
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
if v.CanSet() {
|
||||
if fieldConvert, ok := v.Addr().Interface().(convert.AutoTimer); ok {
|
||||
_, err := fieldConvert.AutoTime(t)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
if v.CanSet() {
|
||||
switch v.Type().Kind() {
|
||||
case reflect.Struct:
|
||||
|
|
|
@ -201,6 +201,14 @@ func (session *Session) Delete(bean interface{}) (int64, error) {
|
|||
copy(condArgs[1:paramsLen], condArgs[0:paramsLen-1])
|
||||
|
||||
val, t := session.engine.nowTime(deletedColumn)
|
||||
fieldValuePtr, err := deletedColumn.ValueOf(bean)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
fieldValue := *fieldValuePtr
|
||||
if at, ok := session.statement.IsAutoTimer(fieldValue, t); ok {
|
||||
val = at
|
||||
}
|
||||
condArgs[0] = val
|
||||
|
||||
var colName = deletedColumn.Name
|
||||
|
|
|
@ -166,6 +166,9 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error
|
|||
}
|
||||
if (col.IsCreated || col.IsUpdated) && session.statement.UseAutoTime {
|
||||
val, t := session.engine.nowTime(col)
|
||||
if at, ok := session.statement.IsAutoTimer(fieldValue, t); ok {
|
||||
val = at
|
||||
}
|
||||
args = append(args, val)
|
||||
|
||||
var colName = col.Name
|
||||
|
@ -539,6 +542,9 @@ func (session *Session) genInsertColumns(bean interface{}) ([]string, []interfac
|
|||
if (col.IsCreated || col.IsUpdated) && session.statement.UseAutoTime /*&& isZero(fieldValue.Interface())*/ {
|
||||
// if time is non-empty, then set to auto time
|
||||
val, t := session.engine.nowTime(col)
|
||||
if at, ok := session.statement.IsAutoTimer(fieldValue, t); ok {
|
||||
val = at
|
||||
}
|
||||
args = append(args, val)
|
||||
|
||||
var colName = col.Name
|
||||
|
|
|
@ -206,6 +206,14 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6
|
|||
colNames = append(colNames, session.engine.Quote(table.Updated)+" = ?")
|
||||
col := table.UpdatedColumn()
|
||||
val, t := session.engine.nowTime(col)
|
||||
fieldValuePtr, err := col.ValueOf(bean)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
fieldValue := *fieldValuePtr
|
||||
if at, ok := session.statement.IsAutoTimer(fieldValue, t); ok {
|
||||
val = at
|
||||
}
|
||||
args = append(args, val)
|
||||
|
||||
var colName = col.Name
|
||||
|
@ -505,6 +513,14 @@ func (session *Session) genUpdateColumns(bean interface{}) ([]string, []interfac
|
|||
if col.IsUpdated && session.statement.UseAutoTime /*&& isZero(fieldValue.Interface())*/ {
|
||||
// if time is non-empty, then set to auto time
|
||||
val, t := session.engine.nowTime(col)
|
||||
fieldValuePtr, err := col.ValueOf(bean)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
fieldValue := *fieldValuePtr
|
||||
if at, ok := session.statement.IsAutoTimer(fieldValue, t); ok {
|
||||
val = at
|
||||
}
|
||||
args = append(args, val)
|
||||
|
||||
var colName = col.Name
|
||||
|
|
Loading…
Reference in New Issue