146 lines
3.9 KiB
Go
146 lines
3.9 KiB
Go
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
|
||
}
|