package t_uid import ( "fmt" "time" "gorm.io/gorm" "gorm.io/gorm/clause" "qoobing.com/gomod/log" ) type t_uid struct { F_id int64 `gorm:"column:F_id;primaryKey"` // ID范围id F_pid int64 `gorm:"column:F_pid"` // ID范围的前一个范围id F_type string `gorm:"column:F_type"` // ID类型 F_prefix string `gorm:"column:F_prefix"` // ID前缀(type+prefix+range_start唯一) F_range_start int64 `gorm:"column:F_range_start"` // ID范围的起始id F_range_length int `gorm:"column:F_range_length"` // ID范围的长度 F_range_owner string `gorm:"column:F_range_owner"` // ID范围的拥有者,一般是机器id F_create_time time.Time `gorm:"column:F_create_time"` // 记录创建时间 F_modify_time time.Time `gorm:"column:F_modify_time"` // 记录更新时间 } type typeInfo struct { Name string //type name RangeLength int //Range length } var table string = "t_uid" // id range table name var typeSupported = map[string]typeInfo{ "SIGNID": typeInfo{ Name: "SIGNID", RangeLength: 3, }, "USERID": typeInfo{ Name: "USERID", RangeLength: 3, }, } func Init(tablename string) { table = tablename } func (id *t_uid) TableName() string { return table } func (id *t_uid) BeforeCreate(db *gorm.DB) error { if id.F_create_time.IsZero() { id.F_create_time = time.Now() id.F_modify_time = time.Now() } return nil } func (id *t_uid) BeforeUpdate(db *gorm.DB) (err error) { id.F_modify_time = time.Now() return nil } func CreateIdRange(db *gorm.DB, typ, prefix string) (start int64, length int, err error) { // Step 1. init variables if t, ok := typeSupported[typ]; !ok { panic(fmt.Sprintf("id type [%s] not supported yet", typ)) } else { length = t.RangeLength } var ( lock = clause.Locking{Strength: "UPDATE"} root = t_uid{} rootcond = map[string]interface{}{ "F_type": fmt.Sprintf("%s#ROOT", typ), "F_prefix": prefix, "F_pid": 0, } rootattr = map[string]interface{}{ "F_range_start": -length, "F_range_length": length, "F_range_owner": "0", } selfid = "TODO" ) log.Infof("start CreateIdRange...") // Step 2. get lastid && lock root(type+prefix) var tx = db.Begin() var txsuccess = false defer func() { if !txsuccess { tx.Rollback() } }() txr := tx.Clauses(lock). Where(rootcond). Attrs(rootattr). FirstOrCreate(&root) if txr.Error != nil { return 0, 0, txr.Error } if root.F_range_owner == "0" { // for new created root we set owner to itself. root.F_range_owner = fmt.Sprintf("%d", root.F_id) } log.PrintPretty("root:", root) // Step 3. insert new range. [can DELETE this for perfermance] var newrange = t_uid{ F_type: typ, F_prefix: prefix, F_range_start: root.F_range_start + int64(length), F_range_length: length, F_range_owner: selfid, } if n, e := fmt.Sscanf(root.F_range_owner, "%d", &newrange.F_pid); e != nil { log.Debugf("sscanf F_range_owner(%s) failed: '%s'", root.F_range_owner, e) return 0, 0, fmt.Errorf("range_owner error") } else if n != 1 { log.Debugf("sscanf F_range_owner(%s) failed: n=%d", root.F_range_owner, n) return 0, 0, fmt.Errorf("range_owner error") } if txr := tx.Create(&newrange); txr.Error != nil { log.PrintPretty("newrange:", newrange) return 0, 0, txr.Error } // Step 4. update root root.F_range_start = newrange.F_range_start root.F_range_length = newrange.F_range_length root.F_range_owner = fmt.Sprintf("%d", newrange.F_id) if txr := tx.Save(&root); txr.Error != nil { return 0, 0, txr.Error } // Step 5. commit and return if txr := tx.Commit(); txr.Error != nil { return 0, 0, txr.Error } txsuccess = true start = newrange.F_range_start length = newrange.F_range_length log.Infof("create range success:(%s, %s, %d, %d)", typ, prefix, start, length) return start, length, nil }