Files
nixcn-cms/data/user.go
Asai Neko e0e1abab93
All checks were successful
Build Backend (NixCN CMS) TeamCity build finished
Build Frontend (NixCN CMS) TeamCity build finished
Add Bio to user table, set varchar for role in attendance table
Signed-off-by: Asai Neko <sugar@sne.moe>
2026-01-01 14:52:24 +08:00

164 lines
3.6 KiB
Go

package data
import (
"github.com/go-viper/mapstructure/v2"
"github.com/google/uuid"
"github.com/meilisearch/meilisearch-go"
"gorm.io/gorm"
)
// Permission Level
// Banned User: 0
// Normal User: 10
// Admin User: 20
// Super User: 30
type User struct {
Id uint `json:"id" gorm:"primarykey;autoincrement"`
UUID uuid.UUID `json:"uuid" gorm:"type:uuid;uniqueindex;not null"`
UserId uuid.UUID `json:"user_id" gorm:"type:uuid;uniqueindex;not null"`
Email string `json:"email" gorm:"type:varchar(255);uniqueindex;not null"`
Nickname string `json:"nickname"`
Subtitle string `json:"subtitle"`
Avatar string `json:"avatar"`
Bio string `json:"bio" gorm:"type:text"`
PermissionLevel uint `json:"permission_level" gorm:"default:10;not null"`
}
type UserSearchDoc struct {
UserId string `json:"user_id"`
Email string `json:"email"`
Type string `json:"type"`
Nickname string `json:"nickname"`
Subtitle string `json:"subtitle"`
Avatar string `json:"avatar"`
PermissionLevel uint `json:"permission_level"`
}
func (self *User) GetByEmail(email string) (*User, error) {
var user User
err := Database.
Where("email = ?", email).
First(&user).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, err
}
return &user, nil
}
func (self *User) GetByUserId(userId uuid.UUID) (*User, error) {
var user User
err := Database.
Where("user_id = ?", userId).
First(&user).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, err
}
return &user, err
}
func (self *User) Create() error {
self.UUID = uuid.New()
self.UserId = uuid.New()
// DB transaction only
if err := Database.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(self).Error; err != nil {
return err
}
return nil
}); err != nil {
return err
}
// Search index (eventual consistency)
if err := self.UpdateSearchIndex(); err != nil {
// TODO: async retry / log
return err
}
return nil
}
func (self *User) UpdateByUserID(userId uuid.UUID) error {
return Database.Transaction(func(tx *gorm.DB) error {
if err := tx.Model(&User{}).Where("user_id = ?", userId).Updates(&self).Error; err != nil {
return err
}
return nil
})
}
func (self *User) GetFullTable() (*[]User, error) {
var users []User
err := Database.Find(&users).Error
if err != nil {
return nil, err
}
return &users, nil
}
func (self *User) FastListUsers(limit, offset int64) (*[]UserSearchDoc, error) {
index := MeiliSearch.Index("user")
// Fast read from MeiliSearch, no DB involved
result, err := index.Search("", &meilisearch.SearchRequest{
Limit: limit,
Offset: offset,
})
if err != nil {
return nil, err
}
var list []UserSearchDoc
if err := mapstructure.Decode(result.Hits, &list); err != nil {
return nil, err
}
return &list, nil
}
func (self *User) UpdateSearchIndex() error {
doc := UserSearchDoc{
UserId: self.UserId.String(),
Email: self.Email,
Nickname: self.Nickname,
Subtitle: self.Subtitle,
Avatar: self.Avatar,
PermissionLevel: self.PermissionLevel,
}
index := MeiliSearch.Index("user")
primaryKey := "user_id"
opts := &meilisearch.DocumentOptions{
PrimaryKey: &primaryKey,
}
if _, err := index.UpdateDocuments(
[]UserSearchDoc{doc},
opts,
); err != nil {
return err
}
return nil
}
func (self *User) DeleteSearchIndex() error {
index := MeiliSearch.Index("user")
_, err := index.DeleteDocument(self.UserId.String(), nil)
return err
}