1 Star 0 Fork 9

hcxiong/go-adm

forked from K./go-adm 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
model.go 10.33 KB
一键复制 编辑 原始数据 按行查看 历史
K. 提交于 2015-05-04 10:31 +08:00 . 修正Export用错Index的问题。
package adm
import (
"reflect"
"time"
"git.oschina.net/janpoem/go-agi"
"strings"
)
const (
TagColumn = "col"
TagPk = "pk"
TagOn = "on"
TagValNothing = ""
TagValPkAi = "ai"
TagValPkNormal = "nor"
OnCreate = "create"
OnUpdate = "update"
)
type Model interface {
GetRow() *ResultRow
Conn() *Connection
Table() string
IsExists() bool
IsNew() bool
// Event Api
OnBind() // 增加一个OnBind接口
BeforeUpdate() EventSignal
BeforeCreate() EventSignal
BeforeSave() EventSignal
BeforeDestroy() EventSignal
AfterUpdate()
AfterCreate()
AfterSave()
AfterDestroy()
}
//func mkQuery(obj Model, query *QueryBuilder) *QueryBuilder {
// if query == nil {
// query = Select("*")
// }
// if len(query.Columns) <= 0 {
// query.Cols("*")
// }
// return query.Table(obj.Table())
//}
//
//func Find(obj Model, query *QueryBuilder) *ResultSet {
// return obj.Conn().Find(mkQuery(obj, query))
//}
//
//func FindOne(obj Model, query *QueryBuilder) Model {
// rs := obj.Conn().Find(mkQuery(obj, query).Limit(1))
// if rs.Count() > 0 {
// rs.Row(0).Fetch(obj)
// }
// return obj
//}
func Save(obj Model) State {
if obj.IsNew() {
return Create(obj)
} else {
return Upgrade(obj)
}
return StateNothing
}
func Bind(row *ResultRow, obj Model) Model {
if row == nil {
return nil
}
valueOf := agi.ValueOf(obj)
mapping := GetModelMapping(obj)
// 将记录行绑定到Model
valueOf.FieldByName("ResultRow").Set(reflect.ValueOf(row))
for i := 0; i < mapping.Size; i++ {
col := mapping.Columns[i]
field := valueOf.Field(col.Index)
raw := row.Str(col.Column)
assignFieldValue(field, raw)
}
obj.OnBind()
return obj
}
// 创建,需要补充两个字段,自增主键,和创建时间
func Create(obj Model) State {
if obj.IsExists() {
return StateFail
}
if obj.BeforeCreate() == EventBreak || obj.BeforeSave() == EventBreak {
return StateFail
}
now := time.Now()
valueOf := agi.ValueOf(obj)
mapping := GetModelMapping(obj)
size := mapping.Size
// 标记是否需要跳过主键字段
exceptPk := !PkValid(obj)
if exceptPk {
size -= 1
}
// struct的属性结构是静态的,所以可以以他的Size来预定填充
// 这样可以减少每次动态调整尺寸的次数
pushIndex := 0
columns := make([]string, size)
values := make([]interface{}, size)
// 需要修改obj的值
// 这里要修改的值,一定不会超过字段的总长度
changedIndex := 0
changedFields := make([]reflect.Value, mapping.Size)
changedValues := make([]interface{}, mapping.Size)
for i := 0; i < mapping.Size; i++ {
col := mapping.Columns[i]
// 跳过主键
if exceptPk && col == mapping.Pk {
continue
}
field := valueOf.Field(col.Index)
columns[pushIndex] = col.Column
var val interface{}
// 避免碰到一些异常的情况,检查一下字段是否有效
// 如果碰到无效的话,字段还是照样添加,但是给一个nil值,因为Columns是提前映射出来的
if !field.IsValid() {
val = nil
} else if col.Column == "updated_at" || col.Column == "created_at" {
changedFields[changedIndex] = field // 直接把要变更的字段传过去
changedValues[changedIndex] = now
changedIndex++
val = now.Unix()
} else {
val = filterSaveValue(field)
}
// 过滤要保存的字段的值
values[pushIndex] = val
pushIndex++
}
rs := obj.Conn().Insert(Insert(obj.Table()).Cols(columns...).Values(values...))
// 写入执行结果的状态
valueOf.FieldByName("ExecResult").Set(reflect.ValueOf(rs))
if rs.IsCreate() {
// 写入主键
if mapping.PkMode == PkAi {
assignFieldValue(valueOf.Field(mapping.Pk.Index), rs.LastInsertId)
}
// 写入其他的更改字段
for j := 0; j < changedIndex; j++ {
// 改为调用统一的方法
assignFieldValue(changedFields[j], changedValues[j])
}
obj.AfterCreate()
obj.AfterSave()
return StateUpdated
}
return StateFail
}
// 更新操作,数据实例 本身已经持有数据,更新操作需要将本身 实例 所持有的数据和数据源进行差异化比较
// 目前比较使用字符串的比较
func Upgrade(obj Model) State {
if obj.IsNew() {
return StateFail
}
if obj.BeforeUpdate() == EventBreak || obj.BeforeSave() == EventBreak {
return StateFail
}
row := obj.GetRow()
now := time.Now()
valueOf := agi.ValueOf(obj)
mapping := GetModelMapping(obj)
update := Update(obj.Table())
// 这个是用来标记更新的字段数量
updateCount := 0
// 这个是用来记录,需要变更的字段
changedIndex := 0
changedFields := make([]reflect.Value, mapping.Size)
changedValues := make([]interface{}, mapping.Size)
pkValid := PkValid(obj)
// 当updateCount > 0的时候,才追加填充更新的字段,初始比较的时候并不默认填充这些字段的值
autoFillIndex := 0
autoFillValues := make([]interface{}, mapping.Size)
autoFillColumns := make([]string, mapping.Size) // 数据库的字段
for i := 0; i < mapping.Size; i++ {
col := mapping.Columns[i]
field := valueOf.Field(col.Index)
var hitUpdate = false
var val interface{}
if col.Column == "updated_at" {
changedValues[changedIndex] = now
changedFields[changedIndex] = field
changedIndex++
autoFillValues[autoFillIndex] = now.Unix()
autoFillColumns[autoFillIndex] = col.Column
autoFillIndex++
} else if (isDiff(field, row.Get(col.Column))) {
val = filterSaveValue(field)
hitUpdate = true
}
if hitUpdate {
update.Set(col.Column, val)
updateCount++
}
// 没主键,就用这条数据的每个字段作为查询条件——很血腥的
if !pkValid || mapping.PkMode == PkNone {
update.In(col.Column, row.Str(col.Column))
} else if pkValid && mapping.Pk == col {
update.In(col.Column, row.Str(col.Column))
}
}
if updateCount > 0 {
// 补充自动填充的字段
for k := 0; k < autoFillIndex; k++ {
update.Set(autoFillColumns[k], autoFillValues[k])
}
rs := obj.Conn().Update(update)
valueOf.FieldByName("ExecResult").Set(reflect.ValueOf(rs))
if rs.IsUpdate() {
// 写入其他的更改字段
for j := 0; j < changedIndex; j++ {
assignFieldValue(changedFields[j], changedValues[j])
}
obj.AfterUpdate()
obj.AfterSave()
return StateUpdated
}
return StateFail
}
return StateNothing
}
func Destroy(obj Model) State {
if obj.IsNew() {
return StateFail
}
if obj.BeforeDestroy() == EventBreak {
return StateFail
}
row := obj.GetRow()
valueOf := agi.ValueOf(obj)
mapping := GetModelMapping(obj)
pkValid := PkValid(obj)
delete := Delete(obj.Table())
for i := 0; i < mapping.Size; i++ {
col := mapping.Columns[i]
// 没主键,就用这条数据的每个字段作为查询条件——很血腥的
if !pkValid || mapping.PkMode == PkNone {
delete.In(col.Column, row.Str(col.Column))
} else if pkValid && mapping.Pk == col {
delete.In(col.Column, row.Str(col.Column))
}
}
rs := obj.Conn().Delete(delete)
valueOf.FieldByName("ExecResult").Set(reflect.ValueOf(rs))
if rs.IsDelete() {
obj.AfterDestroy()
return StateUpdated
}
return StateFail
}
func filterSaveValue(field reflect.Value) interface{} {
v := field.Interface()
switch v.(type) {
case time.Time :
if t, ok := v.(time.Time); ok {
if agi.IsValidTime(t) {
return t.Unix()
} else {
return 0
}
}
}
return v
}
func isDiff(value reflect.Value, raw []byte) bool {
str := string(raw)
typ := value.Type()
kind := typ.Kind()
switch kind {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64 :
return agi.StrToFInt64(str) != value.Int()
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64 :
return agi.AnyToUInt64(str) != value.Uint()
case reflect.Float32, reflect.Float64 :
return agi.StrToFloat(str) != value.Float()
case reflect.String :
return !strings.EqualFold(str, value.String())
default :
if typ.String() == "time.Time" {
if compare, ok := (value.Interface()).(time.Time); ok {
intTime := agi.AnyToInt64(str)
compareValue := compare.Unix()
if !agi.IsValidTime(compare) {
compareValue = 0
}
return compareValue != intTime
}
}
}
return false
}
// 从结果行(ResultRow)绑定对象属性,他应该和isDiff成一对
func setFieldValue(value reflect.Value, raw []byte) {
str := string(raw)
typ := value.Type()
kind := typ.Kind()
switch kind {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64 :
value.SetInt(agi.StrToFInt64(str))
case reflect.Float32, reflect.Float64 :
value.SetFloat(agi.StrToFloat(str))
case reflect.String :
value.SetString(str)
default :
if typ.String() == "time.Time" {
rawTime := agi.IntToTime(agi.StrToFInt64(str))
value.Set(reflect.ValueOf(rawTime))
}
}
}
func filterValue(v interface {}) interface{} {
switch v.(type) {
case time.Time :
if t, ok := v.(time.Time); ok {
if agi.IsValidTime(t) {
return t.Unix()
} else {
return 0
}
}
}
return v
}
//func isDiff2(t reflect.Type, v reflect.Value, raw []byte) bool {
// // 先把源改为字符串
// str := string(raw)
// switch t.Name() {
// case "int64", "int" :
// return agi.StrToFInt64(str) != v.Int()
// case "float64" :
// return agi.StrToFloat(str) != v.Float()
// case "Time" :
// rawTime := agi.IntToTime(agi.StrToFInt64(str))
// compare := (v.Interface()).(time.Time)
// return !compare.Equal(rawTime)
// case "string" :
// return !strings.EqualFold(string(raw), v.String())
// }
// return true
//}
//func Mapping(obj Model, ope Ope) {
// rt := reflect.TypeOf(obj)
// el := rt.Elem()
// var pk string
// var pkMode = PkNone
// for i := 0; i < el.NumField(); i++ {
// f := el.Field(i)
// tag := f.Tag
// col := tag.Get(TagColumn)
// if len(col) <= 0 {
// continue
// }
// fpk := tag.Get(TagPk)
// if pk == TagValNothing && fpk != TagValNothing {
// pk = col
// pkMode = detectPkMode(fpk)
// }
// }
// fmt.Println(pk, pkMode)
//}
func Export(obj Model) map[string]interface{} {
valueOf := agi.ValueOf(obj)
mapping := GetModelMapping(obj)
export := make(map[string]interface{}, mapping.Size)
for i := 0; i < mapping.Size; i++ {
col := mapping.Columns[i]
field := valueOf.Field(col.Index)
export[col.Column] = filterSaveValue(field)
}
return export
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/hcxiong/go-adm.git
git@gitee.com:hcxiong/go-adm.git
hcxiong
go-adm
go-adm
master

搜索帮助