feat: establish Orbit models, migrations and development baseline
This commit is contained in:
65
model/id.go
Normal file
65
model/id.go
Normal file
@@ -0,0 +1,65 @@
|
||||
// Package model 提供 Orbit 各模块共用且不绑定存储实现的基础类型。
|
||||
// 服务端模型不直接作为公开 SDK 协议。
|
||||
package model
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"database/sql/driver"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ID 是 128 位随机标识,对外使用小写十六进制,MySQL 使用 BINARY(16)。
|
||||
// 标识只用于定位资源,不能作为访问凭据。
|
||||
type ID string
|
||||
|
||||
func NewID() (ID, error) {
|
||||
var b [16]byte
|
||||
if _, err := rand.Read(b[:]); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return ID(hex.EncodeToString(b[:])), nil
|
||||
}
|
||||
|
||||
func (id ID) Validate() error {
|
||||
if len(id) != 32 {
|
||||
return errors.New("id must contain 32 lowercase hexadecimal characters")
|
||||
}
|
||||
for _, c := range id {
|
||||
if !(c >= '0' && c <= '9' || c >= 'a' && c <= 'f') {
|
||||
return errors.New("id must contain lowercase hexadecimal characters")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (id ID) Value() (driver.Value, error) {
|
||||
if err := id.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return hex.DecodeString(string(id))
|
||||
}
|
||||
|
||||
func (id *ID) Scan(src any) error {
|
||||
b, ok := src.([]byte)
|
||||
if !ok || len(b) != 16 {
|
||||
return fmt.Errorf("id: expected BINARY(16), got %T", src)
|
||||
}
|
||||
*id = ID(hex.EncodeToString(b))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (id *ID) UnmarshalJSON(b []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(b, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
candidate := ID(s)
|
||||
if err := candidate.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
*id = candidate
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user