package models import ( "fmt" "nCovTrack-Backend/global" "reflect" "regexp" "strconv" "time" "gorm.io/gorm" ) var colNameReg, _ = regexp.Compile(".*column:(.*);?") var j2cMap = make(map[string]map[string]string) var c2jMap = make(map[string]map[string]string) const IS_DELETE = "is_delete" // initJcMap the gorm models need to call this function in init function func initJcMap[T any]() { t := reflect.TypeOf(new(T)).Elem() tJ2cMap, tC2jMap := make(map[string]string), make(map[string]string) for i := 0; i < t.NumField(); i++ { colName := columnName(t.Field(i).Tag.Get("gorm")) // TODO: Deal with (-) jsonName := t.Field(i).Tag.Get("json") if colName == "" || jsonName == "" { continue } tJ2cMap[jsonName] = colName tC2jMap[colName] = jsonName } j2cMap[t.Name()] = tJ2cMap c2jMap[t.Name()] = tC2jMap } // columnName get the mysql column name of the tag func columnName(gormTag string) string { colNames := colNameReg.FindSubmatch([]byte(gormTag)) if len(colNames) != 2 { panic("Model tag regex error") } return string(colNames[1]) } // MapJ2c convert jsonMap to colMap, which will used by gorm func MapJ2c[T any](jsonMap map[string]interface{}, ignoreNil bool) (colMap map[string]interface{}) { tName := reflect.TypeOf(new(T)).Elem().Name() tJ2cMap := j2cMap[tName] if tJ2cMap == nil { panic(tName + " is not init registered int j2cMap") } colMap = make(map[string]interface{}) for k, v := range jsonMap { //TODO 无法转换 if colName := tJ2cMap[k]; colName != "" && (!ignoreNil || v != nil) { colMap[colName] = v } } return colMap } func MapsJ2c[T any](jsonMaps []map[string]interface{}, ignoreNil bool) (colMaps []map[string]interface{}) { for _, jsonMap := range jsonMaps { colMap := MapJ2c[T](jsonMap, ignoreNil) colMaps = append(colMaps, colMap) } return colMaps } func MapC2j[T any](colMap map[string]interface{}, ignoreNil bool) (jsonMap map[string]interface{}) { tName := reflect.TypeOf(new(T)).Elem().Name() tC2jMap := c2jMap[tName] if tC2jMap == nil { panic(tName + " is not init registered int j2cMap") } jsonMap = make(map[string]interface{}) for k, v := range colMap { //TODO 无法转换 if jsonKey := tC2jMap[k]; jsonKey != "" && (!ignoreNil || v != nil) { jsonMap[jsonKey] = v } } return jsonMap } func MapsC2j[T any](jsonMaps []map[string]interface{}, ignoreNil bool) (colMaps []map[string]interface{}) { for _, jsonMap := range jsonMaps { colMap := MapC2j[T](jsonMap, ignoreNil) colMaps = append(colMaps, colMap) } return colMaps } // BeforeSave need to set some field while insert or update func BeforeSave(colMap map[string]interface{}, user int) { if colMap["id"] == nil || int(colMap["id"].(float64)) == 0 { colMap["create_time"] = time.Now() if user != -1 { colMap["create_user"] = user } else { colMap["create_user"] = 0 } } colMap["modify_time"] = time.Now() if user != -1 { colMap["modify_user"] = user } else { colMap["modify_user"] = 0 } } func BeforeBatchSave(colMaps *[]map[string]interface{}, user int) { for _, colMap := range *colMaps { BeforeSave(colMap, user) } } /*----------------------------------------------------------------------------------------------------------*/ // Due to gorm can't deal with the zero value, so we use gorm with map. // The generic will make the function is generally used to gorm models // TODO: add uniqueKey map, which can be used when Upsert func Upsert[T any](colMap map[string]interface{}) (ok bool, rowsAffected int64) { var tx *gorm.DB if colMap["id"] == nil || getMapId(colMap["id"]) == 0 { tx = global.Db.Model(new(T)).Create(colMap) } else { tx = global.Db.Model(new(T)).Where("id = ?", colMap["id"]).Updates(colMap) } if tx.Error != nil { fmt.Println(tx.Error) return false, 0 } return true, tx.RowsAffected } func BatchInsert[T any](colMaps []map[string]interface{}) (ok bool, rowsAffected int64) { tx := global.Db.Model(new(T)).Create(&colMaps) if tx.Error != nil { fmt.Println(tx.Error) return false, 0 } return true, tx.RowsAffected } func Update[T any](queryMap []map[string]interface{}, updateMap map[string]interface{}) (ok bool, rowsAffected int64) { tx := global.Db.Model(new(T)) for _, e := range queryMap { e[IS_DELETE] = 0 tx = tx.Or(e) } return UpdateByOrm(tx, updateMap) } func UpdateByOrm(tx *gorm.DB, updateMap map[string]interface{}) (ok bool, rowsAffected int64) { tx.Updates(updateMap) if tx.Error != nil { fmt.Println(tx.Error) return false, 0 } return true, tx.RowsAffected } // DeleteById will delete by id, not delete the record from database, only set the field "is_delete" as 1 func DeleteById[T any](id int) (ok bool, rowsAffected int64) { tx := global.Db.Model(new(T)).Where("id = ?", id).Update("is_delete", 1) if tx.Error != nil { return false, 0 } return true, tx.RowsAffected } func DropById[T any](id int) { global.Db.Model(new(T)).Delete("id = ?", id) } func List[T any](queryMap []map[string]interface{}) *[]map[string]interface{} { tx := global.Db.Model(new(T)) for _, e := range queryMap { e[IS_DELETE] = 0 tx = tx.Or(e) } return ListByOrm(tx) } func ListField[T any](queryMap []map[string]interface{}, isOmit bool, queryField ...string) *[]map[string]interface{} { tx := global.Db.Model(new(T)) for _, e := range queryMap { e[IS_DELETE] = 0 tx = tx.Or(e) } if len(queryMap) == 0 { return ListByOrm(tx) } if isOmit { tx = tx.Omit(queryField...) } else { tx = tx.Select(queryField[0], queryField[1:]) } return ListByOrm(tx) } func ListByOrm(tx *gorm.DB) *[]map[string]interface{} { var res []map[string]interface{} tx.Find(&res) return &res } func Get[T any](queryMap []map[string]interface{}) map[string]interface{} { tx := global.Db.Model(new(T)) for _, e := range queryMap { e[IS_DELETE] = 0 tx = tx.Or(e) } return GetByOrm(tx) } func GetField[T any](queryMap []map[string]interface{}, isOmit bool, queryField ...string) map[string]interface{} { tx := global.Db.Model(new(T)) for _, e := range queryMap { e[IS_DELETE] = 0 tx = tx.Or(e) } if len(queryMap) == 0 { return GetByOrm(tx) } if isOmit { tx = tx.Omit(queryField...) } else { tx = tx.Select(queryField[0], queryField[1:]) } return GetByOrm(tx) } func GetByOrm(tx *gorm.DB) map[string]interface{} { var res map[string]interface{} tx.Limit(1).Find(&res) return res } func Count[T any](queryMap []map[string]interface{}) int64 { tx := global.Db.Model(new(T)) for _, e := range queryMap { e[IS_DELETE] = 0 tx = tx.Or(e) } return CountByOrm(tx) } func CountByOrm(tx *gorm.DB) int64 { var count int64 tx.Count(&count) return count } func getMapId(id interface{}) int { switch id.(type) { case int: return id.(int) case string: id, _ := strconv.Atoi(id.(string)) return id case float64: return int(id.(float64)) default: return -1 } }