mirror of
https://github.com/SamyRai/tercul-backend.git
synced 2025-12-27 04:01:34 +00:00
Key changes include: - Added `goose` as a project dependency and integrated it into the application's startup logic to automatically apply migrations. - Created an initial PostgreSQL-compatible migration file containing the full database schema. - Updated the integration test suite to use the new migration system. - Refactored authorization logic for collection mutations from the GraphQL resolvers to the application service layer. - Cleaned up the codebase by removing dead code, unused helper functions, and duplicate struct definitions. - Fixed several build errors and a logic error in the integration tests. This change improves the project's production readiness by providing a structured and version-controlled way to manage database schema changes. It also enhances code quality by centralizing business logic and removing technical debt.
1147 lines
38 KiB
Go
1147 lines
38 KiB
Go
package domain
|
|
|
|
import (
|
|
"database/sql/driver"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"golang.org/x/crypto/bcrypt"
|
|
"gorm.io/gorm"
|
|
"time"
|
|
)
|
|
|
|
// JSONB is a custom type for JSONB columns.
|
|
type JSONB map[string]interface{}
|
|
|
|
// Value marshals JSONB for storing in the DB.
|
|
func (j JSONB) Value() (driver.Value, error) {
|
|
if j == nil {
|
|
return "{}", nil
|
|
}
|
|
return json.Marshal(j)
|
|
}
|
|
|
|
// Scan unmarshals a JSONB value.
|
|
func (j *JSONB) Scan(value interface{}) error {
|
|
if value == nil {
|
|
*j = JSONB{}
|
|
return nil
|
|
}
|
|
switch v := value.(type) {
|
|
case []byte:
|
|
if len(v) == 0 {
|
|
*j = JSONB{}
|
|
return nil
|
|
}
|
|
return json.Unmarshal(v, j)
|
|
case string:
|
|
if v == "" {
|
|
*j = JSONB{}
|
|
return nil
|
|
}
|
|
return json.Unmarshal([]byte(v), j)
|
|
default:
|
|
return fmt.Errorf("failed to unmarshal JSONB value of type %T: %v", value, value)
|
|
}
|
|
}
|
|
|
|
// BaseModel contains common fields for all models
|
|
type BaseModel struct {
|
|
ID uint `gorm:"primaryKey"`
|
|
CreatedAt time.Time
|
|
UpdatedAt time.Time
|
|
}
|
|
|
|
// TranslatableModel extends BaseModel with language support
|
|
type TranslatableModel struct {
|
|
BaseModel
|
|
Language string `gorm:"size:50;default:'multi'"`
|
|
Slug string `gorm:"size:255;index"`
|
|
}
|
|
|
|
// Translation status enum
|
|
type TranslationStatus string
|
|
|
|
const (
|
|
TranslationStatusDraft TranslationStatus = "draft"
|
|
TranslationStatusPublished TranslationStatus = "published"
|
|
TranslationStatusReviewing TranslationStatus = "reviewing"
|
|
TranslationStatusRejected TranslationStatus = "rejected"
|
|
)
|
|
|
|
// UserRole enum
|
|
type UserRole string
|
|
|
|
const (
|
|
UserRoleReader UserRole = "reader"
|
|
UserRoleContributor UserRole = "contributor"
|
|
UserRoleReviewer UserRole = "reviewer"
|
|
UserRoleEditor UserRole = "editor"
|
|
UserRoleAdmin UserRole = "admin"
|
|
)
|
|
|
|
// User represents a user of the platform
|
|
type User struct {
|
|
BaseModel
|
|
Username string `gorm:"size:50;not null;unique"`
|
|
Email string `gorm:"size:100;not null;unique"`
|
|
Password string `gorm:"size:255;not null"`
|
|
FirstName string `gorm:"size:50"`
|
|
LastName string `gorm:"size:50"`
|
|
DisplayName string `gorm:"size:100"`
|
|
Bio string `gorm:"type:text"`
|
|
AvatarURL string `gorm:"size:255"`
|
|
Role UserRole `gorm:"size:20;default:'reader'"`
|
|
LastLoginAt *time.Time
|
|
Verified bool `gorm:"default:false"`
|
|
Active bool `gorm:"default:true"`
|
|
Translations []*Translation `gorm:"foreignKey:TranslatorID"`
|
|
Comments []*Comment `gorm:"foreignKey:UserID"`
|
|
Likes []*Like `gorm:"foreignKey:UserID"`
|
|
Bookmarks []*Bookmark `gorm:"foreignKey:UserID"`
|
|
Collections []*Collection `gorm:"foreignKey:UserID"`
|
|
Contributions []*Contribution `gorm:"foreignKey:UserID"`
|
|
CountryID *uint
|
|
Country *Country `gorm:"foreignKey:CountryID"`
|
|
CityID *uint
|
|
City *City `gorm:"foreignKey:CityID"`
|
|
AddressID *uint
|
|
Address *Address `gorm:"foreignKey:AddressID"`
|
|
}
|
|
|
|
// UserProfile represents additional profile information for a user
|
|
type UserProfile struct {
|
|
BaseModel
|
|
UserID uint `gorm:"uniqueIndex"`
|
|
User *User `gorm:"foreignKey:UserID"`
|
|
PhoneNumber string `gorm:"size:20"`
|
|
Website string `gorm:"size:255"`
|
|
Twitter string `gorm:"size:50"`
|
|
Facebook string `gorm:"size:50"`
|
|
LinkedIn string `gorm:"size:50"`
|
|
Github string `gorm:"size:50"`
|
|
Preferences JSONB `gorm:"type:jsonb;default:'{}'"`
|
|
Settings JSONB `gorm:"type:jsonb;default:'{}'"`
|
|
}
|
|
|
|
// UserSession represents a user session
|
|
type UserSession struct {
|
|
BaseModel
|
|
UserID uint `gorm:"index"`
|
|
User *User `gorm:"foreignKey:UserID"`
|
|
Token string `gorm:"size:255;not null;uniqueIndex"`
|
|
IP string `gorm:"size:50"`
|
|
UserAgent string `gorm:"size:255"`
|
|
ExpiresAt time.Time `gorm:"not null"`
|
|
}
|
|
|
|
// PasswordReset represents a password reset request
|
|
type PasswordReset struct {
|
|
BaseModel
|
|
UserID uint `gorm:"index"`
|
|
User *User `gorm:"foreignKey:UserID"`
|
|
Token string `gorm:"size:255;not null;uniqueIndex"`
|
|
ExpiresAt time.Time `gorm:"not null"`
|
|
Used bool `gorm:"default:false"`
|
|
}
|
|
|
|
// EmailVerification represents an email verification request
|
|
type EmailVerification struct {
|
|
BaseModel
|
|
UserID uint `gorm:"index"`
|
|
User *User `gorm:"foreignKey:UserID"`
|
|
Token string `gorm:"size:255;not null;uniqueIndex"`
|
|
ExpiresAt time.Time `gorm:"not null"`
|
|
Used bool `gorm:"default:false"`
|
|
}
|
|
|
|
func (u *User) SetPassword(password string) error {
|
|
if password == "" {
|
|
return errors.New("password cannot be empty")
|
|
}
|
|
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
|
if err != nil {
|
|
return errors.New("failed to hash password: " + err.Error())
|
|
}
|
|
u.Password = string(hashedPassword)
|
|
return nil
|
|
}
|
|
|
|
func (u *User) BeforeSave(tx *gorm.DB) error {
|
|
if u.Password == "" {
|
|
return nil
|
|
}
|
|
if len(u.Password) >= 60 && u.Password[:4] == "$2a$" {
|
|
return nil
|
|
}
|
|
return u.SetPassword(u.Password)
|
|
}
|
|
|
|
func (u *User) CheckPassword(password string) bool {
|
|
err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
|
|
return err == nil
|
|
}
|
|
|
|
type WorkStatus string
|
|
const (
|
|
WorkStatusDraft WorkStatus = "draft"
|
|
WorkStatusPublished WorkStatus = "published"
|
|
WorkStatusArchived WorkStatus = "archived"
|
|
WorkStatusDeleted WorkStatus = "deleted"
|
|
)
|
|
type WorkType string
|
|
const (
|
|
WorkTypePoetry WorkType = "poetry"
|
|
WorkTypeProse WorkType = "prose"
|
|
WorkTypeDrama WorkType = "drama"
|
|
WorkTypeEssay WorkType = "essay"
|
|
WorkTypeNovel WorkType = "novel"
|
|
WorkTypeShortStory WorkType = "short_story"
|
|
WorkTypeNovella WorkType = "novella"
|
|
WorkTypePlay WorkType = "play"
|
|
WorkTypeScript WorkType = "script"
|
|
WorkTypeOther WorkType = "other"
|
|
)
|
|
type Work struct {
|
|
TranslatableModel
|
|
Title string `gorm:"size:255;not null"`
|
|
Description string `gorm:"type:text"`
|
|
Type WorkType `gorm:"size:50;default:'other'"`
|
|
Status WorkStatus `gorm:"size:50;default:'draft'"`
|
|
PublishedAt *time.Time
|
|
Translations []*Translation `gorm:"polymorphic:Translatable"`
|
|
Authors []*Author `gorm:"many2many:work_authors"`
|
|
Tags []*Tag `gorm:"many2many:work_tags"`
|
|
Categories []*Category `gorm:"many2many:work_categories"`
|
|
Copyrights []*Copyright `gorm:"many2many:work_copyrights;constraint:OnDelete:CASCADE"`
|
|
Monetizations []*Monetization `gorm:"many2many:work_monetizations;constraint:OnDelete:CASCADE"`
|
|
}
|
|
|
|
type AuthorStatus string
|
|
const (
|
|
AuthorStatusActive AuthorStatus = "active"
|
|
AuthorStatusInactive AuthorStatus = "inactive"
|
|
AuthorStatusDeceased AuthorStatus = "deceased"
|
|
)
|
|
type Author struct {
|
|
TranslatableModel
|
|
Name string `gorm:"size:255;not null"`
|
|
Status AuthorStatus `gorm:"size:50;default:'active'"`
|
|
BirthDate *time.Time
|
|
DeathDate *time.Time
|
|
Works []*Work `gorm:"many2many:work_authors"`
|
|
Books []*Book `gorm:"many2many:book_authors"`
|
|
CountryID *uint
|
|
Country *Country `gorm:"foreignKey:CountryID"`
|
|
CityID *uint
|
|
City *City `gorm:"foreignKey:CityID"`
|
|
PlaceID *uint
|
|
Place *Place `gorm:"foreignKey:PlaceID"`
|
|
AddressID *uint
|
|
Address *Address `gorm:"foreignKey:AddressID"`
|
|
Translations []*Translation `gorm:"polymorphic:Translatable"`
|
|
Copyrights []*Copyright `gorm:"many2many:author_copyrights;constraint:OnDelete:CASCADE"`
|
|
Monetizations []*Monetization `gorm:"many2many:author_monetizations;constraint:OnDelete:CASCADE"`
|
|
}
|
|
|
|
type BookStatus string
|
|
const (
|
|
BookStatusDraft BookStatus = "draft"
|
|
BookStatusPublished BookStatus = "published"
|
|
BookStatusOutOfPrint BookStatus = "out_of_print"
|
|
BookStatusArchived BookStatus = "archived"
|
|
)
|
|
type BookFormat string
|
|
const (
|
|
BookFormatHardcover BookFormat = "hardcover"
|
|
BookFormatPaperback BookFormat = "paperback"
|
|
BookFormatEbook BookFormat = "ebook"
|
|
BookFormatAudiobook BookFormat = "audiobook"
|
|
BookFormatDigital BookFormat = "digital"
|
|
)
|
|
type Book struct {
|
|
TranslatableModel
|
|
Title string `gorm:"size:255;not null"`
|
|
Description string `gorm:"type:text"`
|
|
ISBN string `gorm:"size:20;index"`
|
|
Format BookFormat `gorm:"size:50;default:'paperback'"`
|
|
Status BookStatus `gorm:"size:50;default:'draft'"`
|
|
PublishedAt *time.Time
|
|
Works []*Work `gorm:"many2many:book_works"`
|
|
Authors []*Author `gorm:"many2many:book_authors"`
|
|
PublisherID *uint
|
|
Publisher *Publisher `gorm:"foreignKey:PublisherID"`
|
|
Translations []*Translation `gorm:"polymorphic:Translatable"`
|
|
Copyrights []*Copyright `gorm:"many2many:book_copyrights;constraint:OnDelete:CASCADE"`
|
|
Monetizations []*Monetization `gorm:"many2many:book_monetizations;constraint:OnDelete:CASCADE"`
|
|
}
|
|
|
|
type PublisherStatus string
|
|
const (
|
|
PublisherStatusActive PublisherStatus = "active"
|
|
PublisherStatusInactive PublisherStatus = "inactive"
|
|
PublisherStatusDefunct PublisherStatus = "defunct"
|
|
)
|
|
type Publisher struct {
|
|
TranslatableModel
|
|
Name string `gorm:"size:255;not null"`
|
|
Description string `gorm:"type:text"`
|
|
Status PublisherStatus `gorm:"size:50;default:'active'"`
|
|
Books []*Book `gorm:"foreignKey:PublisherID"`
|
|
CountryID *uint
|
|
Country *Country `gorm:"foreignKey:CountryID"`
|
|
Translations []*Translation `gorm:"polymorphic:Translatable"`
|
|
Copyrights []*Copyright `gorm:"many2many:publisher_copyrights;constraint:OnDelete:CASCADE"`
|
|
Monetizations []*Monetization `gorm:"many2many:publisher_monetizations;constraint:OnDelete:CASCADE"`
|
|
}
|
|
|
|
type SourceStatus string
|
|
const (
|
|
SourceStatusActive SourceStatus = "active"
|
|
SourceStatusInactive SourceStatus = "inactive"
|
|
SourceStatusArchived SourceStatus = "archived"
|
|
)
|
|
type Source struct {
|
|
TranslatableModel
|
|
Name string `gorm:"size:255;not null"`
|
|
Description string `gorm:"type:text"`
|
|
URL string `gorm:"size:512"`
|
|
Status SourceStatus `gorm:"size:50;default:'active'"`
|
|
Works []*Work `gorm:"many2many:work_sources"`
|
|
Translations []*Translation `gorm:"polymorphic:Translatable"`
|
|
Copyrights []*Copyright `gorm:"many2many:source_copyrights;constraint:OnDelete:CASCADE"`
|
|
Monetizations []*Monetization `gorm:"many2many:source_monetizations;constraint:OnDelete:CASCADE"`
|
|
}
|
|
|
|
type EditionStatus string
|
|
const (
|
|
EditionStatusDraft EditionStatus = "draft"
|
|
EditionStatusPublished EditionStatus = "published"
|
|
EditionStatusOutOfPrint EditionStatus = "out_of_print"
|
|
EditionStatusArchived EditionStatus = "archived"
|
|
)
|
|
type Edition struct {
|
|
BaseModel
|
|
Title string `gorm:"size:255;not null"`
|
|
Description string `gorm:"type:text"`
|
|
ISBN string `gorm:"size:20;index"`
|
|
Version string `gorm:"size:50"`
|
|
Format BookFormat `gorm:"size:50;default:'paperback'"`
|
|
Status EditionStatus `gorm:"size:50;default:'draft'"`
|
|
PublishedAt *time.Time
|
|
BookID uint
|
|
Book *Book `gorm:"foreignKey:BookID"`
|
|
}
|
|
|
|
func (w *Work) BeforeSave(tx *gorm.DB) error {
|
|
if w.Title == "" {
|
|
w.Title = "Untitled Work"
|
|
}
|
|
return nil
|
|
}
|
|
func (a *Author) BeforeSave(tx *gorm.DB) error {
|
|
if a.Name == "" {
|
|
a.Name = "Unknown Author"
|
|
}
|
|
return nil
|
|
}
|
|
func (b *Book) BeforeSave(tx *gorm.DB) error {
|
|
if b.Title == "" {
|
|
b.Title = "Untitled Book"
|
|
}
|
|
return nil
|
|
}
|
|
func (p *Publisher) BeforeSave(tx *gorm.DB) error {
|
|
if p.Name == "" {
|
|
p.Name = "Unknown Publisher"
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type Comment struct {
|
|
BaseModel
|
|
Text string `gorm:"type:text;not null"`
|
|
UserID uint
|
|
User *User `gorm:"foreignKey:UserID"`
|
|
WorkID *uint
|
|
Work *Work `gorm:"foreignKey:WorkID"`
|
|
TranslationID *uint
|
|
Translation *Translation `gorm:"foreignKey:TranslationID"`
|
|
LineNumber *int `gorm:"index"`
|
|
TextBlockID *uint
|
|
TextBlock *TextBlock `gorm:"foreignKey:TextBlockID"`
|
|
ParentID *uint
|
|
Parent *Comment `gorm:"foreignKey:ParentID"`
|
|
Children []*Comment `gorm:"foreignKey:ParentID"`
|
|
Likes []*Like `gorm:"foreignKey:CommentID"`
|
|
}
|
|
type Like struct {
|
|
BaseModel
|
|
UserID uint `gorm:"index;uniqueIndex:uniq_like_user_target"`
|
|
User *User `gorm:"foreignKey:UserID"`
|
|
WorkID *uint `gorm:"index;uniqueIndex:uniq_like_user_target"`
|
|
Work *Work `gorm:"foreignKey:WorkID"`
|
|
TranslationID *uint `gorm:"index;uniqueIndex:uniq_like_user_target"`
|
|
Translation *Translation `gorm:"foreignKey:TranslationID"`
|
|
CommentID *uint `gorm:"index;uniqueIndex:uniq_like_user_target"`
|
|
Comment *Comment `gorm:"foreignKey:CommentID"`
|
|
}
|
|
type Bookmark struct {
|
|
BaseModel
|
|
Name string `gorm:"size:100"`
|
|
UserID uint `gorm:"index;uniqueIndex:uniq_bookmark_user_work"`
|
|
User *User `gorm:"foreignKey:UserID"`
|
|
WorkID uint `gorm:"index;uniqueIndex:uniq_bookmark_user_work"`
|
|
Work *Work `gorm:"foreignKey:WorkID"`
|
|
Notes string `gorm:"type:text"`
|
|
LastReadAt *time.Time
|
|
Progress int `gorm:"default:0"`
|
|
}
|
|
type Collection struct {
|
|
TranslatableModel
|
|
Name string `gorm:"size:100;not null"`
|
|
Description string `gorm:"type:text"`
|
|
UserID uint
|
|
User *User `gorm:"foreignKey:UserID"`
|
|
Works []*Work `gorm:"many2many:collection_works"`
|
|
IsPublic bool `gorm:"default:true"`
|
|
CoverImageURL string `gorm:"size:255"`
|
|
}
|
|
type Contribution struct {
|
|
BaseModel
|
|
Name string `gorm:"size:100;not null"`
|
|
Status string `gorm:"size:20;default:'draft'"`
|
|
UserID uint
|
|
User *User `gorm:"foreignKey:UserID"`
|
|
WorkID *uint
|
|
Work *Work `gorm:"foreignKey:WorkID"`
|
|
TranslationID *uint
|
|
Translation *Translation `gorm:"foreignKey:TranslationID"`
|
|
ReviewerID *uint
|
|
Reviewer *User `gorm:"foreignKey:ReviewerID"`
|
|
ReviewedAt *time.Time
|
|
Feedback string `gorm:"type:text"`
|
|
}
|
|
|
|
type Country struct {
|
|
TranslatableModel
|
|
Name string `gorm:"size:100;not null"`
|
|
Code string `gorm:"size:2;not null;uniqueIndex"`
|
|
PhoneCode string `gorm:"size:10"`
|
|
Currency string `gorm:"size:3"`
|
|
Continent string `gorm:"size:20"`
|
|
Cities []*City `gorm:"foreignKey:CountryID"`
|
|
Places []*Place `gorm:"foreignKey:CountryID"`
|
|
Addresses []*Address `gorm:"foreignKey:CountryID"`
|
|
}
|
|
type Language struct {
|
|
BaseModel
|
|
Code string `gorm:"size:16;not null;uniqueIndex"`
|
|
Name string `gorm:"size:100;not null"`
|
|
Script string `gorm:"size:20"`
|
|
Direction string `gorm:"size:5"`
|
|
}
|
|
type City struct {
|
|
TranslatableModel
|
|
Name string `gorm:"size:100;not null"`
|
|
CountryID uint
|
|
Country *Country `gorm:"foreignKey:CountryID"`
|
|
Places []*Place `gorm:"foreignKey:CityID"`
|
|
Addresses []*Address `gorm:"foreignKey:CityID"`
|
|
}
|
|
type Place struct {
|
|
TranslatableModel
|
|
Name string `gorm:"size:100;not null"`
|
|
Description string `gorm:"type:text"`
|
|
Latitude float64
|
|
Longitude float64
|
|
CountryID *uint
|
|
Country *Country `gorm:"foreignKey:CountryID"`
|
|
CityID *uint
|
|
City *City `gorm:"foreignKey:CityID"`
|
|
}
|
|
type Address struct {
|
|
BaseModel
|
|
Street string `gorm:"size:255"`
|
|
StreetNumber string `gorm:"size:20"`
|
|
PostalCode string `gorm:"size:20"`
|
|
CountryID *uint
|
|
Country *Country `gorm:"foreignKey:CountryID"`
|
|
CityID *uint
|
|
City *City `gorm:"foreignKey:CityID"`
|
|
Latitude *float64
|
|
Longitude *float64
|
|
}
|
|
|
|
type Tag struct {
|
|
BaseModel
|
|
Name string `gorm:"size:100;not null;uniqueIndex"`
|
|
Description string `gorm:"type:text"`
|
|
Works []*Work `gorm:"many2many:work_tags"`
|
|
Slug string `gorm:"size:255;index"`
|
|
}
|
|
type Category struct {
|
|
BaseModel
|
|
Name string `gorm:"size:100;not null;uniqueIndex"`
|
|
Description string `gorm:"type:text"`
|
|
ParentID *uint
|
|
Parent *Category `gorm:"foreignKey:ParentID"`
|
|
Children []*Category `gorm:"foreignKey:ParentID"`
|
|
Works []*Work `gorm:"many2many:work_categories"`
|
|
Path string `gorm:"size:1024;index"`
|
|
Slug string `gorm:"size:255;index"`
|
|
}
|
|
type Series struct {
|
|
BaseModel
|
|
Name string `gorm:"size:255;not null;uniqueIndex"`
|
|
Description string `gorm:"type:text"`
|
|
}
|
|
type WorkSeries struct {
|
|
BaseModel
|
|
WorkID uint `gorm:"index;uniqueIndex:uniq_work_series"`
|
|
Work *Work `gorm:"foreignKey:WorkID"`
|
|
SeriesID uint `gorm:"index;uniqueIndex:uniq_work_series"`
|
|
Series *Series `gorm:"foreignKey:SeriesID"`
|
|
NumberInSeries int `gorm:"default:0"`
|
|
}
|
|
|
|
type Translation struct {
|
|
BaseModel
|
|
Title string `gorm:"size:255;not null"`
|
|
Content string `gorm:"type:text"`
|
|
Description string `gorm:"type:text"`
|
|
Language string `gorm:"size:50;not null"`
|
|
Status TranslationStatus `gorm:"size:50;default:'draft'"`
|
|
PublishedAt *time.Time
|
|
TranslatableID uint `gorm:"not null"`
|
|
TranslatableType string `gorm:"size:50;not null"`
|
|
TranslatorID *uint
|
|
Translator *User `gorm:"foreignKey:TranslatorID"`
|
|
IsOriginalLanguage bool `gorm:"default:false"`
|
|
AudioURL string `gorm:"size:512"`
|
|
DateTranslated *time.Time
|
|
}
|
|
type TranslationField struct {
|
|
BaseModel
|
|
TranslationID uint
|
|
Translation *Translation `gorm:"foreignKey:TranslationID"`
|
|
FieldName string `gorm:"size:100;not null"`
|
|
FieldValue string `gorm:"type:text;not null"`
|
|
Language string `gorm:"size:50;not null"`
|
|
}
|
|
type TranslatableEntity interface {
|
|
GetID() uint
|
|
GetType() string
|
|
GetDefaultLanguage() string
|
|
}
|
|
func GetTranslatableFields(entityType string) []string {
|
|
fieldMappings := map[string][]string{
|
|
"Work": {"title", "content", "description"},
|
|
"Author": {"name", "biography"},
|
|
"Book": {"title", "description"},
|
|
"Country": {"name"},
|
|
"Publisher": {"name", "description"},
|
|
"Source": {"name", "description"},
|
|
}
|
|
if fields, exists := fieldMappings[entityType]; exists {
|
|
return fields
|
|
}
|
|
return []string{}
|
|
}
|
|
func (t *Translation) BeforeSave(tx *gorm.DB) error {
|
|
if t.Title == "" {
|
|
t.Title = "Untitled Translation"
|
|
}
|
|
return nil
|
|
}
|
|
func (w *Work) GetID() uint { return w.ID }
|
|
func (w *Work) GetType() string { return "Work" }
|
|
func (w *Work) GetDefaultLanguage() string { return w.Language }
|
|
func (a *Author) GetID() uint { return a.ID }
|
|
func (a *Author) GetType() string { return "Author" }
|
|
func (a *Author) GetDefaultLanguage() string { return a.Language }
|
|
func (b *Book) GetID() uint { return b.ID }
|
|
func (b *Book) GetType() string { return "Book" }
|
|
func (b *Book) GetDefaultLanguage() string { return b.Language }
|
|
func (c *Country) GetID() uint { return c.ID }
|
|
func (c *Country) GetType() string { return "Country" }
|
|
func (c *Country) GetDefaultLanguage() string { return c.Language }
|
|
func (p *Publisher) GetID() uint { return p.ID }
|
|
func (p *Publisher) GetType() string { return "Publisher" }
|
|
func (p *Publisher) GetDefaultLanguage() string { return p.Language }
|
|
func (s *Source) GetID() uint { return s.ID }
|
|
func (s *Source) GetType() string { return "Source" }
|
|
func (s *Source) GetDefaultLanguage() string { return s.Language }
|
|
|
|
type Copyright struct {
|
|
BaseModel
|
|
Identificator string `gorm:"size:100;not null"`
|
|
Name string `gorm:"size:255;not null"`
|
|
Description string `gorm:"type:text"`
|
|
License string `gorm:"size:100"`
|
|
StartDate *time.Time
|
|
EndDate *time.Time
|
|
Translations []CopyrightTranslation `gorm:"foreignKey:CopyrightID"`
|
|
}
|
|
type WorkCopyright struct {
|
|
WorkID uint `gorm:"primaryKey;index"`
|
|
CopyrightID uint `gorm:"primaryKey;index"`
|
|
CreatedAt time.Time
|
|
}
|
|
|
|
func (WorkCopyright) TableName() string { return "work_copyrights" }
|
|
|
|
type AuthorCopyright struct {
|
|
AuthorID uint `gorm:"primaryKey;index"`
|
|
CopyrightID uint `gorm:"primaryKey;index"`
|
|
CreatedAt time.Time
|
|
}
|
|
|
|
func (AuthorCopyright) TableName() string { return "author_copyrights" }
|
|
|
|
type BookCopyright struct {
|
|
BookID uint `gorm:"primaryKey;index"`
|
|
CopyrightID uint `gorm:"primaryKey;index"`
|
|
CreatedAt time.Time
|
|
}
|
|
|
|
func (BookCopyright) TableName() string { return "book_copyrights" }
|
|
|
|
type PublisherCopyright struct {
|
|
PublisherID uint `gorm:"primaryKey;index"`
|
|
CopyrightID uint `gorm:"primaryKey;index"`
|
|
CreatedAt time.Time
|
|
}
|
|
|
|
func (PublisherCopyright) TableName() string { return "publisher_copyrights" }
|
|
|
|
type SourceCopyright struct {
|
|
SourceID uint `gorm:"primaryKey;index"`
|
|
CopyrightID uint `gorm:"primaryKey;index"`
|
|
CreatedAt time.Time
|
|
}
|
|
|
|
func (SourceCopyright) TableName() string { return "source_copyrights" }
|
|
type CopyrightTranslation struct {
|
|
BaseModel
|
|
CopyrightID uint
|
|
Copyright *Copyright `gorm:"foreignKey:CopyrightID"`
|
|
LanguageCode string `gorm:"size:10;not null"`
|
|
Message string `gorm:"type:text;not null"`
|
|
Description string `gorm:"type:text"`
|
|
}
|
|
type CopyrightClaimStatus string
|
|
const (
|
|
CopyrightClaimStatusPending CopyrightClaimStatus = "pending"
|
|
CopyrightClaimStatusApproved CopyrightClaimStatus = "approved"
|
|
CopyrightClaimStatusRejected CopyrightClaimStatus = "rejected"
|
|
)
|
|
type CopyrightClaim struct {
|
|
BaseModel
|
|
Details string `gorm:"type:text;not null"`
|
|
Status CopyrightClaimStatus `gorm:"size:50;default:'pending'"`
|
|
ClaimDate time.Time `gorm:"not null"`
|
|
Resolution string `gorm:"type:text"`
|
|
ResolvedAt *time.Time
|
|
UserID *uint
|
|
User *User `gorm:"foreignKey:UserID"`
|
|
}
|
|
type MonetizationType string
|
|
const (
|
|
MonetizationTypeSubscription MonetizationType = "subscription"
|
|
MonetizationTypeOneTime MonetizationType = "one_time"
|
|
MonetizationTypeDonation MonetizationType = "donation"
|
|
MonetizationTypeAdvertisement MonetizationType = "advertisement"
|
|
MonetizationTypeLicensing MonetizationType = "licensing"
|
|
)
|
|
type MonetizationStatus string
|
|
const (
|
|
MonetizationStatusActive MonetizationStatus = "active"
|
|
MonetizationStatusInactive MonetizationStatus = "inactive"
|
|
MonetizationStatusPending MonetizationStatus = "pending"
|
|
)
|
|
type WorkMonetization struct {
|
|
WorkID uint `gorm:"primaryKey;index"`
|
|
MonetizationID uint `gorm:"primaryKey;index"`
|
|
CreatedAt time.Time
|
|
}
|
|
|
|
func (WorkMonetization) TableName() string { return "work_monetizations" }
|
|
|
|
type AuthorMonetization struct {
|
|
AuthorID uint `gorm:"primaryKey;index"`
|
|
MonetizationID uint `gorm:"primaryKey;index"`
|
|
CreatedAt time.Time
|
|
}
|
|
|
|
func (AuthorMonetization) TableName() string { return "author_monetizations" }
|
|
|
|
type BookMonetization struct {
|
|
BookID uint `gorm:"primaryKey;index"`
|
|
MonetizationID uint `gorm:"primaryKey;index"`
|
|
CreatedAt time.Time
|
|
}
|
|
|
|
func (BookMonetization) TableName() string { return "book_monetizations" }
|
|
|
|
type PublisherMonetization struct {
|
|
PublisherID uint `gorm:"primaryKey;index"`
|
|
MonetizationID uint `gorm:"primaryKey;index"`
|
|
CreatedAt time.Time
|
|
}
|
|
|
|
func (PublisherMonetization) TableName() string { return "publisher_monetizations" }
|
|
|
|
type SourceMonetization struct {
|
|
SourceID uint `gorm:"primaryKey;index"`
|
|
MonetizationID uint `gorm:"primaryKey;index"`
|
|
CreatedAt time.Time
|
|
}
|
|
|
|
func (SourceMonetization) TableName() string { return "source_monetizations" }
|
|
type Monetization struct {
|
|
BaseModel
|
|
Amount float64 `gorm:"type:decimal(10,2);default:0.0"`
|
|
Currency string `gorm:"size:3;default:'USD'"`
|
|
Type MonetizationType `gorm:"size:50"`
|
|
Status MonetizationStatus `gorm:"size:50;default:'active'"`
|
|
StartDate *time.Time
|
|
EndDate *time.Time
|
|
Language string `gorm:"size:50;not null"`
|
|
}
|
|
type License struct {
|
|
BaseModel
|
|
SPDXIdentifier string `gorm:"size:64;uniqueIndex"`
|
|
Name string `gorm:"size:255;not null"`
|
|
URL string `gorm:"size:512"`
|
|
Description string `gorm:"type:text"`
|
|
}
|
|
type ModerationFlag struct {
|
|
BaseModel
|
|
TargetType string `gorm:"size:50;not null"`
|
|
TargetID uint `gorm:"not null"`
|
|
Reason string `gorm:"size:255"`
|
|
Status string `gorm:"size:50;default:'open'"`
|
|
ReviewerID *uint
|
|
Reviewer *User `gorm:"foreignKey:ReviewerID"`
|
|
Notes string `gorm:"type:text"`
|
|
}
|
|
type AuditLog struct {
|
|
BaseModel
|
|
ActorID *uint
|
|
Actor *User `gorm:"foreignKey:ActorID"`
|
|
Action string `gorm:"size:50;not null"`
|
|
EntityType string `gorm:"size:50;not null"`
|
|
EntityID uint `gorm:"not null"`
|
|
Before JSONB `gorm:"type:jsonb;default:'{}'"`
|
|
After JSONB `gorm:"type:jsonb;default:'{}'"`
|
|
At time.Time `gorm:"autoCreateTime"`
|
|
}
|
|
|
|
// And all other models from the files I read...
|
|
// This is getting very long, but it's the correct approach.
|
|
// I will just paste the rest of the structs here.
|
|
|
|
type WorkStats struct {
|
|
BaseModel
|
|
Views int64 `gorm:"default:0"`
|
|
Likes int64 `gorm:"default:0"`
|
|
Comments int64 `gorm:"default:0"`
|
|
Bookmarks int64 `gorm:"default:0"`
|
|
Shares int64 `gorm:"default:0"`
|
|
TranslationCount int64 `gorm:"default:0"`
|
|
ReadingTime int `gorm:"default:0"`
|
|
Complexity float64 `gorm:"type:decimal(5,2);default:0.0"`
|
|
Sentiment float64 `gorm:"type:decimal(5,2);default:0.0"`
|
|
WorkID uint `gorm:"uniqueIndex;index"`
|
|
Work *Work `gorm:"foreignKey:WorkID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
|
|
}
|
|
type TranslationStats struct {
|
|
BaseModel
|
|
Views int64 `gorm:"default:0"`
|
|
Likes int64 `gorm:"default:0"`
|
|
Comments int64 `gorm:"default:0"`
|
|
Shares int64 `gorm:"default:0"`
|
|
ReadingTime int `gorm:"default:0"`
|
|
Sentiment float64 `gorm:"type:decimal(5,2);default:0.0"`
|
|
TranslationID uint `gorm:"uniqueIndex;index"`
|
|
Translation *Translation `gorm:"foreignKey:TranslationID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
|
|
}
|
|
|
|
type UserEngagement struct {
|
|
BaseModel
|
|
UserID uint `gorm:"index;uniqueIndex:uniq_user_engagement_date"`
|
|
User *User `gorm:"foreignKey:UserID"`
|
|
Date time.Time `gorm:"type:date;uniqueIndex:uniq_user_engagement_date"`
|
|
WorksRead int `gorm:"default:0"`
|
|
CommentsMade int `gorm:"default:0"`
|
|
LikesGiven int `gorm:"default:0"`
|
|
BookmarksMade int `gorm:"default:0"`
|
|
TranslationsMade int `gorm:"default:0"`
|
|
}
|
|
|
|
type Trending struct {
|
|
BaseModel
|
|
EntityType string `gorm:"size:50;not null;index:idx_trending_entity_period_date,uniqueIndex:uniq_trending_rank"`
|
|
EntityID uint `gorm:"not null;index:idx_trending_entity_period_date,uniqueIndex:uniq_trending_rank"`
|
|
Rank int `gorm:"not null;uniqueIndex:uniq_trending_rank"`
|
|
Score float64 `gorm:"type:decimal(10,2);default:0.0"`
|
|
TimePeriod string `gorm:"size:50;not null;index:idx_trending_entity_period_date,uniqueIndex:uniq_trending_rank"`
|
|
Date time.Time `gorm:"type:date;index:idx_trending_entity_period_date,uniqueIndex:uniq_trending_rank"`
|
|
}
|
|
|
|
type BookStats struct {
|
|
BaseModel
|
|
Sales int64 `gorm:"default:0"`
|
|
Views int64 `gorm:"default:0"`
|
|
Likes int64 `gorm:"default:0"`
|
|
BookID uint `gorm:"uniqueIndex;index"`
|
|
Book *Book `gorm:"foreignKey:BookID"`
|
|
}
|
|
type CollectionStats struct {
|
|
BaseModel
|
|
Items int64 `gorm:"default:0"`
|
|
Views int64 `gorm:"default:0"`
|
|
Likes int64 `gorm:"default:0"`
|
|
CollectionID uint `gorm:"uniqueIndex;index"`
|
|
Collection *Collection `gorm:"foreignKey:CollectionID"`
|
|
}
|
|
type MediaStats struct {
|
|
BaseModel
|
|
Views int64 `gorm:"default:0"`
|
|
Downloads int64 `gorm:"default:0"`
|
|
Shares int64 `gorm:"default:0"`
|
|
MediaID uint `gorm:"uniqueIndex;index"`
|
|
Media interface{} `gorm:"-"`
|
|
}
|
|
|
|
type BookWork struct {
|
|
BaseModel
|
|
BookID uint `gorm:"index;uniqueIndex:uniq_book_work"`
|
|
Book *Book `gorm:"foreignKey:BookID"`
|
|
WorkID uint `gorm:"index;uniqueIndex:uniq_book_work"`
|
|
Work *Work `gorm:"foreignKey:WorkID"`
|
|
Order int `gorm:"default:0"`
|
|
}
|
|
type AuthorCountry struct {
|
|
BaseModel
|
|
AuthorID uint `gorm:"index;uniqueIndex:uniq_author_country"`
|
|
Author *Author `gorm:"foreignKey:AuthorID"`
|
|
CountryID uint `gorm:"index;uniqueIndex:uniq_author_country"`
|
|
Country *Country `gorm:"foreignKey:CountryID"`
|
|
}
|
|
type WorkAuthor struct {
|
|
BaseModel
|
|
WorkID uint `gorm:"index;uniqueIndex:uniq_work_author_role"`
|
|
Work *Work `gorm:"foreignKey:WorkID"`
|
|
AuthorID uint `gorm:"index;uniqueIndex:uniq_work_author_role"`
|
|
Author *Author `gorm:"foreignKey:AuthorID"`
|
|
Role string `gorm:"size:50;default:'author';uniqueIndex:uniq_work_author_role"`
|
|
Ordinal int `gorm:"default:0"`
|
|
}
|
|
type BookAuthor struct {
|
|
BaseModel
|
|
BookID uint `gorm:"index;uniqueIndex:uniq_book_author_role"`
|
|
Book *Book `gorm:"foreignKey:BookID"`
|
|
AuthorID uint `gorm:"index;uniqueIndex:uniq_book_author_role"`
|
|
Author *Author `gorm:"foreignKey:AuthorID"`
|
|
Role string `gorm:"size:50;default:'author';uniqueIndex:uniq_book_author_role"`
|
|
Ordinal int `gorm:"default:0"`
|
|
}
|
|
|
|
type ReadabilityScore struct {
|
|
BaseModel
|
|
Score float64 `gorm:"type:decimal(5,2)"`
|
|
Language string `gorm:"size:50;not null"`
|
|
Method string `gorm:"size:50"`
|
|
WorkID uint
|
|
Work *Work `gorm:"foreignKey:WorkID"`
|
|
}
|
|
type WritingStyle struct {
|
|
BaseModel
|
|
Name string `gorm:"size:100;not null"`
|
|
Description string `gorm:"type:text"`
|
|
Language string `gorm:"size:50;not null"`
|
|
WorkID uint
|
|
Work *Work `gorm:"foreignKey:WorkID"`
|
|
}
|
|
type LinguisticLayer struct {
|
|
BaseModel
|
|
Name string `gorm:"size:100;not null"`
|
|
Description string `gorm:"type:text"`
|
|
Language string `gorm:"size:50;not null"`
|
|
Type string `gorm:"size:50"`
|
|
WorkID uint
|
|
Work *Work `gorm:"foreignKey:WorkID"`
|
|
Data JSONB `gorm:"type:jsonb;default:'{}'"`
|
|
}
|
|
type TextBlock struct {
|
|
BaseModel
|
|
WorkID *uint
|
|
Work *Work `gorm:"foreignKey:WorkID"`
|
|
TranslationID *uint
|
|
Translation *Translation `gorm:"foreignKey:TranslationID"`
|
|
Index int `gorm:"index"`
|
|
Type string `gorm:"size:30"`
|
|
StartOffset int `gorm:"default:0"`
|
|
EndOffset int `gorm:"default:0"`
|
|
Text string `gorm:"type:text"`
|
|
}
|
|
type TextMetadata struct {
|
|
BaseModel
|
|
Analysis string `gorm:"type:text"`
|
|
Language string `gorm:"size:50;not null"`
|
|
WordCount int `gorm:"default:0"`
|
|
SentenceCount int `gorm:"default:0"`
|
|
ParagraphCount int `gorm:"default:0"`
|
|
AverageWordLength float64 `gorm:"type:decimal(5,2)"`
|
|
AverageSentenceLength float64 `gorm:"type:decimal(5,2)"`
|
|
WorkID uint
|
|
Work *Work `gorm:"foreignKey:WorkID"`
|
|
}
|
|
type PoeticAnalysis struct {
|
|
BaseModel
|
|
Structure string `gorm:"type:text"`
|
|
Language string `gorm:"size:50;not null"`
|
|
RhymeScheme string `gorm:"size:100"`
|
|
MeterType string `gorm:"size:50"`
|
|
StanzaCount int `gorm:"default:0"`
|
|
LineCount int `gorm:"default:0"`
|
|
WorkID uint
|
|
Work *Work `gorm:"foreignKey:WorkID"`
|
|
}
|
|
type Word struct {
|
|
BaseModel
|
|
Text string `gorm:"size:100;not null"`
|
|
Language string `gorm:"size:50;not null"`
|
|
PartOfSpeech string `gorm:"size:20"`
|
|
Lemma string `gorm:"size:100"`
|
|
ConceptID *uint
|
|
Concept *Concept `gorm:"foreignKey:ConceptID"`
|
|
Works []*Work `gorm:"many2many:work_words"`
|
|
}
|
|
type WordOccurrence struct {
|
|
BaseModel
|
|
TextBlockID uint
|
|
TextBlock *TextBlock `gorm:"foreignKey:TextBlockID"`
|
|
WordID *uint
|
|
Word *Word `gorm:"foreignKey:WordID"`
|
|
StartOffset int `gorm:"default:0"`
|
|
EndOffset int `gorm:"default:0"`
|
|
Lemma string `gorm:"size:100"`
|
|
PartOfSpeech string `gorm:"size:20"`
|
|
}
|
|
type Concept struct {
|
|
BaseModel
|
|
Name string `gorm:"size:100;not null"`
|
|
Description string `gorm:"type:text"`
|
|
Words []*Word `gorm:"foreignKey:ConceptID"`
|
|
Works []*Work `gorm:"many2many:work_concepts"`
|
|
}
|
|
type LanguageEntity struct {
|
|
BaseModel
|
|
Name string `gorm:"size:100;not null"`
|
|
Type string `gorm:"size:50"`
|
|
Language string `gorm:"size:50;not null"`
|
|
Works []*Work `gorm:"many2many:work_language_entities"`
|
|
}
|
|
type EntityOccurrence struct {
|
|
BaseModel
|
|
TextBlockID uint
|
|
TextBlock *TextBlock `gorm:"foreignKey:TextBlockID"`
|
|
LanguageEntityID uint
|
|
LanguageEntity *LanguageEntity `gorm:"foreignKey:LanguageEntityID"`
|
|
StartOffset int `gorm:"default:0"`
|
|
EndOffset int `gorm:"default:0"`
|
|
}
|
|
|
|
type LanguageAnalysis struct {
|
|
BaseModel
|
|
Language string `gorm:"size:50;not null;uniqueIndex:uniq_work_language_analysis"`
|
|
Analysis JSONB `gorm:"type:jsonb;default:'{}'"`
|
|
WorkID uint `gorm:"index;uniqueIndex:uniq_work_language_analysis"`
|
|
Work *Work `gorm:"foreignKey:WorkID"`
|
|
}
|
|
type Gamification struct {
|
|
BaseModel
|
|
Points int `gorm:"default:0"`
|
|
Level int `gorm:"default:1"`
|
|
Badges JSONB `gorm:"type:jsonb;default:'{}'"`
|
|
Streaks int `gorm:"default:0"`
|
|
LastActive *time.Time
|
|
UserID uint `gorm:"uniqueIndex;index"`
|
|
User *User `gorm:"foreignKey:UserID"`
|
|
}
|
|
type Stats struct {
|
|
BaseModel
|
|
Data JSONB `gorm:"type:jsonb;default:'{}'"`
|
|
Period string `gorm:"size:50"`
|
|
StartDate time.Time
|
|
EndDate time.Time
|
|
UserID *uint
|
|
User *User `gorm:"foreignKey:UserID"`
|
|
WorkID *uint
|
|
Work *Work `gorm:"foreignKey:WorkID"`
|
|
}
|
|
type SearchDocument struct {
|
|
BaseModel
|
|
EntityType string `gorm:"size:50;index"`
|
|
EntityID uint `gorm:"index"`
|
|
LanguageCode string `gorm:"size:16;index"`
|
|
Title string `gorm:"size:512"`
|
|
Body string `gorm:"type:text"`
|
|
Keywords string `gorm:"type:text"`
|
|
}
|
|
|
|
type Emotion struct {
|
|
BaseModel
|
|
Name string `gorm:"size:100;not null"`
|
|
Description string `gorm:"type:text"`
|
|
Language string `gorm:"size:50;not null"`
|
|
Intensity float64 `gorm:"type:decimal(5,2);default:0.0"`
|
|
UserID *uint
|
|
User *User `gorm:"foreignKey:UserID"`
|
|
WorkID *uint
|
|
Work *Work `gorm:"foreignKey:WorkID"`
|
|
CollectionID *uint
|
|
Collection *Collection `gorm:"foreignKey:CollectionID"`
|
|
}
|
|
type Mood struct {
|
|
BaseModel
|
|
Name string `gorm:"size:100;not null"`
|
|
Description string `gorm:"type:text"`
|
|
Language string `gorm:"size:50;not null"`
|
|
Works []*Work `gorm:"many2many:work_moods"`
|
|
}
|
|
type TopicCluster struct {
|
|
BaseModel
|
|
Name string `gorm:"size:100;not null"`
|
|
Description string `gorm:"type:text"`
|
|
Keywords string `gorm:"type:text"`
|
|
Works []*Work `gorm:"many2many:work_topic_clusters"`
|
|
}
|
|
|
|
type Edge struct {
|
|
BaseModel
|
|
SourceTable string `gorm:"size:50;not null;index:idx_edge_source;uniqueIndex:uniq_edge"`
|
|
SourceID uint `gorm:"not null;index:idx_edge_source;uniqueIndex:uniq_edge"`
|
|
TargetTable string `gorm:"size:50;not null;index:idx_edge_target;uniqueIndex:uniq_edge"`
|
|
TargetID uint `gorm:"not null;index:idx_edge_target;uniqueIndex:uniq_edge"`
|
|
Relation string `gorm:"size:50;default:'ASSOCIATED_WITH';not null;index;uniqueIndex:uniq_edge"`
|
|
Language string `gorm:"size:10;default:'en';index;uniqueIndex:uniq_edge"`
|
|
Extra JSONB `gorm:"type:jsonb;default:'{}'"`
|
|
}
|
|
type Embedding struct {
|
|
BaseModel
|
|
ExternalID string `gorm:"size:64;index"`
|
|
EntityType string `gorm:"size:50;not null;index:idx_embedding_entity;uniqueIndex:uniq_embedding"`
|
|
EntityID uint `gorm:"not null;index:idx_embedding_entity;uniqueIndex:uniq_embedding"`
|
|
Model string `gorm:"size:50;not null;uniqueIndex:uniq_embedding"`
|
|
Dim int `gorm:"default:0"`
|
|
WorkID *uint
|
|
Work *Work `gorm:"foreignKey:WorkID"`
|
|
TranslationID *uint
|
|
Translation *Translation `gorm:"foreignKey:TranslationID"`
|
|
}
|
|
|
|
type Localization struct {
|
|
BaseModel
|
|
Key string `gorm:"size:255;not null;uniqueIndex:uniq_localization_key_language"`
|
|
Value string `gorm:"type:text;not null"`
|
|
Language string `gorm:"size:50;not null;uniqueIndex:uniq_localization_key_language"`
|
|
}
|
|
type Media struct {
|
|
BaseModel
|
|
URL string `gorm:"size:512;not null"`
|
|
Type string `gorm:"size:50;not null"`
|
|
MimeType string `gorm:"size:100"`
|
|
Size int64 `gorm:"default:0"`
|
|
Title string `gorm:"size:255"`
|
|
Description string `gorm:"type:text"`
|
|
Language string `gorm:"size:50;not null"`
|
|
AuthorID *uint
|
|
Author *Author `gorm:"foreignKey:AuthorID"`
|
|
TranslationID *uint
|
|
Translation *Translation `gorm:"foreignKey:TranslationID"`
|
|
CountryID *uint
|
|
Country *Country `gorm:"foreignKey:CountryID"`
|
|
CityID *uint
|
|
City *City `gorm:"foreignKey:CityID"`
|
|
}
|
|
|
|
type Notification struct {
|
|
BaseModel
|
|
Message string `gorm:"type:text;not null"`
|
|
Type string `gorm:"size:50"`
|
|
Read bool `gorm:"default:false"`
|
|
Language string `gorm:"size:50;not null"`
|
|
UserID uint
|
|
User *User `gorm:"foreignKey:UserID"`
|
|
RelatedID *uint
|
|
RelatedType string `gorm:"size:50"`
|
|
}
|
|
type EditorialWorkflow struct {
|
|
BaseModel
|
|
Stage string `gorm:"size:50;not null"`
|
|
Notes string `gorm:"type:text"`
|
|
Language string `gorm:"size:50;not null"`
|
|
WorkID *uint
|
|
Work *Work `gorm:"foreignKey:WorkID"`
|
|
TranslationID *uint
|
|
Translation *Translation `gorm:"foreignKey:TranslationID"`
|
|
UserID uint
|
|
User *User `gorm:"foreignKey:UserID"`
|
|
AssignedToID *uint
|
|
AssignedTo *User `gorm:"foreignKey:AssignedToID"`
|
|
DueDate *time.Time
|
|
CompletedAt *time.Time
|
|
}
|
|
type Admin struct {
|
|
BaseModel
|
|
UserID uint
|
|
User *User `gorm:"foreignKey:UserID"`
|
|
Role string `gorm:"size:50;not null"`
|
|
Permissions JSONB `gorm:"type:jsonb;default:'{}'"`
|
|
}
|
|
type Vote struct {
|
|
BaseModel
|
|
Value int `gorm:"default:0"`
|
|
UserID uint
|
|
User *User `gorm:"foreignKey:UserID"`
|
|
WorkID *uint
|
|
Work *Work `gorm:"foreignKey:WorkID"`
|
|
TranslationID *uint
|
|
Translation *Translation `gorm:"foreignKey:TranslationID"`
|
|
CommentID *uint
|
|
Comment *Comment `gorm:"foreignKey:CommentID"`
|
|
}
|
|
type Contributor struct {
|
|
BaseModel
|
|
Name string `gorm:"size:100;not null"`
|
|
Role string `gorm:"size:50"`
|
|
UserID *uint
|
|
User *User `gorm:"foreignKey:UserID"`
|
|
WorkID *uint
|
|
Work *Work `gorm:"foreignKey:WorkID"`
|
|
TranslationID *uint
|
|
Translation *Translation `gorm:"foreignKey:TranslationID"`
|
|
}
|
|
type InteractionEvent struct {
|
|
BaseModel
|
|
UserID *uint
|
|
User *User `gorm:"foreignKey:UserID"`
|
|
TargetType string `gorm:"size:50;not null"`
|
|
TargetID uint `gorm:"not null"`
|
|
Kind string `gorm:"size:30;not null"`
|
|
OccurredAt time.Time `gorm:"index"`
|
|
}
|
|
type HybridEntityWork struct {
|
|
BaseModel
|
|
Name string `gorm:"size:100;not null"`
|
|
Type string `gorm:"size:50"`
|
|
WorkID *uint
|
|
Work *Work `gorm:"foreignKey:WorkID"`
|
|
TranslationID *uint
|
|
Translation *Translation `gorm:"foreignKey:TranslationID"`
|
|
}
|