feat: add tenant and user management with migrations, handlers, and tests

- Implemented tenant and user management features including:
  - Tenant creation and management with associated migrations.
  - User creation and management with associated migrations.
  - Tenant membership management with associated migrations.
  - Platform user roles management with associated migrations.
  - Quota management with associated migrations.
  - Article and template management with associated migrations.
- Added HTTP handlers for templates and workspaces.
- Created tests for protected and public routes.
- Introduced a script to check tenant scope in SQL queries.
- Documented task plan for backend completion and frontend foundation.
This commit is contained in:
2026-04-01 00:58:42 +08:00
commit de30497f59
210 changed files with 23733 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
package domain
import "time"
type Article struct {
ID int64
TenantID int64
SourceType string
TemplateID *int64
CurrentVersionID *int64
GenerateStatus string
PublishStatus string
CreatedAt time.Time
UpdatedAt time.Time
}
@@ -0,0 +1,21 @@
package domain
import "time"
type ArticleTemplate struct {
ID int64
Scope string
TenantID *int64
OriginType string
TemplateKey string
TemplateName string
SchemaJSON map[string]interface{}
PromptTemplate *string
PromptVisibility string
ProtectedPromptAssetKey *string
CardConfigJSON map[string]interface{}
Status string
VersionNo int
CreatedAt time.Time
UpdatedAt time.Time
}
@@ -0,0 +1,15 @@
package domain
import "time"
type ArticleVersion struct {
ID int64
ArticleID int64
VersionNo int
Title *string
HTMLContent *string
MarkdownContent *string
WordCount int
SourceLabel *string
CreatedAt time.Time
}
+13
View File
@@ -0,0 +1,13 @@
package domain
import "time"
type Brand struct {
ID int64
TenantID int64
Name string
Description *string
Status string
CreatedAt time.Time
UpdatedAt time.Time
}
@@ -0,0 +1,13 @@
package domain
import "time"
type BrandKeyword struct {
ID int64
TenantID int64
BrandID int64
Name string
Status string
CreatedAt time.Time
UpdatedAt time.Time
}
@@ -0,0 +1,23 @@
package domain
import "time"
type BrandQuestion struct {
ID int64
TenantID int64
BrandID int64
KeywordID int64
CurrentVersionID *int64
Status string
CreatedAt time.Time
}
type BrandQuestionVersion struct {
ID int64
QuestionID int64
QuestionText string
QuestionHash string
VersionNo int
IsActive bool
CreatedAt time.Time
}
@@ -0,0 +1,18 @@
package domain
import (
"encoding/json"
"time"
)
type Competitor struct {
ID int64
TenantID int64
BrandID int64
Name string
Website *string
Description *string
ProductLinesJSON json.RawMessage
CreatedAt time.Time
UpdatedAt time.Time
}
@@ -0,0 +1,19 @@
package domain
import "time"
type GenerationTask struct {
ID int64
TenantID int64
ArticleID *int64
TaskBatchID *string
QuotaReservationID *int64
TaskType string
RequestHash *string
InputParamsJSON map[string]interface{}
Status string
ErrorMessage *string
StartedAt *time.Time
CompletedAt *time.Time
CreatedAt time.Time
}
+20
View File
@@ -0,0 +1,20 @@
package domain
import "time"
type Tenant struct {
ID int64
Name string
Status string
FrozenAt *time.Time
CreatedAt time.Time
UpdatedAt time.Time
}
type TenantMembership struct {
ID int64
TenantID int64
UserID int64
TenantRole string
CreatedAt time.Time
}
+15
View File
@@ -0,0 +1,15 @@
package domain
import "time"
type User struct {
ID int64
Email string
Phone *string
PasswordHash string
Name string
AvatarURL *string
Status string
CreatedAt time.Time
UpdatedAt time.Time
}
@@ -0,0 +1,31 @@
package domain
import "time"
type WorkspaceOverview struct {
ArticleCount int64 `json:"article_count"`
PublishedCount int64 `json:"published_count"`
BrandCount int64 `json:"brand_count"`
SupportedPlatformCount int `json:"supported_platform_count"`
}
type RecentArticle struct {
ID int64 `json:"id"`
Title *string `json:"title"`
TemplateName *string `json:"template_name"`
GenerateStatus string `json:"generate_status"`
PublishStatus string `json:"publish_status"`
SourceType string `json:"source_type"`
WordCount int `json:"word_count"`
SourceLabel *string `json:"source_label"`
CreatedAt time.Time `json:"created_at"`
}
type QuotaSummary struct {
PlanCode string `json:"plan_code"`
PlanName string `json:"plan_name"`
TotalQuota int `json:"total_quota"`
UsedQuota int `json:"used_quota"`
Balance int `json:"balance"`
ResetAt *time.Time `json:"reset_at"`
}