Version: 0.9.78.dev.260506

This commit is contained in:
Losita
2026-05-06 00:30:08 +08:00
parent 3b6fca44a6
commit 33227e48a7
71 changed files with 13137 additions and 62 deletions

View File

@@ -0,0 +1,186 @@
package model
import "time"
const (
// ForumPostStatusPublished 表示帖子已公开展示在计划广场。
ForumPostStatusPublished = "published"
// ForumPostStatusHidden 表示帖子被作者隐藏或后续治理流程下架。
ForumPostStatusHidden = "hidden"
// ForumPostStatusDeleted 表示帖子已软删除P0 暂不对前端展示。
ForumPostStatusDeleted = "deleted"
// ForumPostStatusPendingReview 预留审核态P0 不启用审核后台。
ForumPostStatusPendingReview = "pending_review"
)
const (
// ForumLikeStatusActive 表示当前用户仍保持点赞。
ForumLikeStatusActive = "active"
// ForumLikeStatusCanceled 表示用户取消点赞,保留记录用于奖励幂等。
ForumLikeStatusCanceled = "canceled"
)
const (
// ForumCommentStatusVisible 表示评论正常展示。
ForumCommentStatusVisible = "visible"
// ForumCommentStatusDeleted 表示评论已由本人删除,服务层仍保留子回复结构。
ForumCommentStatusDeleted = "deleted"
)
const (
// ForumImportStatusPending 表示导入记录已占位,正在创建 TaskClass 副本。
ForumImportStatusPending = "pending"
// ForumImportStatusImported 表示导入已成功创建当前用户自己的 TaskClass 副本。
ForumImportStatusImported = "imported"
// ForumImportStatusFailed 表示导入副本创建或最终确认失败,可由后续重试覆盖。
ForumImportStatusFailed = "failed"
)
// ForumPost 是计划广场帖子主体表。
//
// 职责边界:
// 1. 只保存社区帖子可展示信息、作者和计数字段;
// 2. 不保存完整 TaskClass 模板,模板快照归 ForumPostTemplate / ForumPostTemplateItem
// 3. 计数字段由服务事务内维护,避免列表页每次做聚合统计。
type ForumPost struct {
ID uint64 `gorm:"column:id;primaryKey;autoIncrement"`
AuthorUserID uint64 `gorm:"column:author_user_id;not null;index:idx_forum_posts_author_status,priority:1;uniqueIndex:uk_forum_posts_author_idem,priority:1;comment:作者用户ID"`
SourceTaskClassID uint64 `gorm:"column:source_task_class_id;not null;index:idx_forum_posts_source_task_class;comment:发布时选择的原始TaskClass ID仅用于审计"`
Title string `gorm:"column:title;type:varchar(80);not null;comment:帖子标题"`
Summary string `gorm:"column:summary;type:text;comment:帖子简介"`
TagsJSON string `gorm:"column:tags_json;type:json;not null;comment:标签JSON数组"`
IdempotencyKey *string `gorm:"column:idempotency_key;type:varchar(128);uniqueIndex:uk_forum_posts_author_idem,priority:2;comment:发布请求幂等键"`
Status string `gorm:"column:status;type:varchar(32);not null;default:'published';index:idx_forum_posts_status_created,priority:1;index:idx_forum_posts_author_status,priority:2;comment:published/hidden/deleted/pending_review"`
LikeCount int64 `gorm:"column:like_count;not null;default:0;index:idx_forum_posts_like_count;comment:点赞数冗余计数"`
CommentCount int64 `gorm:"column:comment_count;not null;default:0;comment:评论数冗余计数"`
ImportCount int64 `gorm:"column:import_count;not null;default:0;index:idx_forum_posts_import_count;comment:导入数冗余计数"`
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;index:idx_forum_posts_status_created,priority:2;comment:创建时间"`
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;comment:更新时间"`
DeletedAt *time.Time `gorm:"column:deleted_at;index;comment:软删除时间"`
}
func (ForumPost) TableName() string {
return "forum_posts"
}
// ForumPostTemplate 是发布瞬间复制出的 TaskClass 配置快照。
//
// 职责边界:
// 1. 只保存可分享的 TaskClass 配置白名单;
// 2. 不保存 embedded_time、schedule 绑定和用户私有排程状态;
// 3. 后续原作者修改原 TaskClass 时,本快照不跟随变化。
type ForumPostTemplate struct {
ID uint64 `gorm:"column:id;primaryKey;autoIncrement"`
PostID uint64 `gorm:"column:post_id;not null;uniqueIndex:uk_forum_templates_post;comment:所属帖子ID"`
SourceTaskClassID uint64 `gorm:"column:source_task_class_id;not null;comment:原始TaskClass ID"`
Mode string `gorm:"column:mode;type:varchar(32);comment:TaskClass 模式"`
StartDate *time.Time `gorm:"column:start_date;comment:计划开始日期"`
EndDate *time.Time `gorm:"column:end_date;comment:计划结束日期"`
SubjectType string `gorm:"column:subject_type;type:varchar(32);comment:学科类型"`
DifficultyLevel string `gorm:"column:difficulty_level;type:varchar(16);comment:难度等级"`
CognitiveIntensity string `gorm:"column:cognitive_intensity;type:varchar(16);comment:认知强度"`
TotalSlots int `gorm:"column:total_slots;comment:分配的总节数"`
AllowFillerCourse bool `gorm:"column:allow_filler_course;not null;default:true;comment:是否允许填充课程空隙"`
Strategy string `gorm:"column:strategy;type:varchar(32);comment:规划策略"`
ExcludedSlotsJSON *string `gorm:"column:excluded_slots_json;type:json;comment:排除节次JSON数组"`
ExcludedDaysOfWeekJSON *string `gorm:"column:excluded_days_of_week_json;type:json;comment:排除星期JSON数组"`
StrategyLabelsJSON *string `gorm:"column:strategy_labels_json;type:json;comment:前端展示用策略标签JSON数组"`
ConfigSnapshotJSON *string `gorm:"column:config_snapshot_json;type:json;comment:过滤后的配置快照,便于后续兼容扩展"`
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间"`
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;comment:更新时间"`
}
func (ForumPostTemplate) TableName() string {
return "forum_post_templates"
}
// ForumPostTemplateItem 是 TaskClassItem 的可分享快照。
//
// 职责边界:
// 1. 只保存任务条目的顺序和内容;
// 2. 不保存 embedded_time避免把原作者私有排程状态带给导入用户
// 3. 导入时服务层按这些快照重新创建当前用户自己的 TaskClassItem。
type ForumPostTemplateItem struct {
ID uint64 `gorm:"column:id;primaryKey;autoIncrement"`
TemplateID uint64 `gorm:"column:template_id;not null;uniqueIndex:uk_forum_template_items_order,priority:1;index:idx_forum_template_items_template;comment:模板ID"`
PostID uint64 `gorm:"column:post_id;not null;index:idx_forum_template_items_post;comment:帖子ID便于按帖子直接读取预览"`
SourceTaskItemID uint64 `gorm:"column:source_task_item_id;not null;comment:原始TaskClassItem ID仅用于审计"`
Order int `gorm:"column:item_order;not null;uniqueIndex:uk_forum_template_items_order,priority:2;comment:条目顺序"`
Content string `gorm:"column:content;type:text;not null;comment:任务条目内容"`
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间"`
}
func (ForumPostTemplateItem) TableName() string {
return "forum_post_template_items"
}
// ForumLike 是点赞幂等表。
//
// 职责边界:
// 1. 通过 post_id + user_id 唯一约束保证同一用户同一帖子只有一条点赞状态;
// 2. 取消点赞只把状态改为 canceled不删除记录避免作者奖励被反复触发
// 3. event_id 对应首次点赞奖励事件,供 token-store 账本幂等使用。
type ForumLike struct {
ID uint64 `gorm:"column:id;primaryKey;autoIncrement"`
PostID uint64 `gorm:"column:post_id;not null;uniqueIndex:uk_forum_likes_post_user,priority:1;index:idx_forum_likes_post_status,priority:1;comment:帖子ID"`
UserID uint64 `gorm:"column:user_id;not null;uniqueIndex:uk_forum_likes_post_user,priority:2;comment:点赞用户ID"`
AuthorUserID uint64 `gorm:"column:author_user_id;not null;index:idx_forum_likes_author;comment:帖子作者ID便于奖励和审计"`
Status string `gorm:"column:status;type:varchar(32);not null;default:'active';index:idx_forum_likes_post_status,priority:2;comment:active/canceled"`
EventID string `gorm:"column:event_id;type:varchar(128);not null;uniqueIndex:uk_forum_likes_event;comment:首次点赞事件ID"`
LikedAt time.Time `gorm:"column:liked_at;autoCreateTime;comment:首次点赞时间"`
CanceledAt *time.Time `gorm:"column:canceled_at;comment:最近一次取消点赞时间"`
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间"`
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;comment:更新时间"`
}
func (ForumLike) TableName() string {
return "forum_likes"
}
// ForumComment 是评论和多层回复的扁平存储表。
//
// 职责边界:
// 1. 数据库只保存 parent_comment_id不保存树结构
// 2. 服务层按帖子读取扁平评论后组装评论树;
// 3. 删除评论使用 status + deleted_at 软删除,保留子回复链路。
type ForumComment struct {
ID uint64 `gorm:"column:id;primaryKey;autoIncrement"`
PostID uint64 `gorm:"column:post_id;not null;index:idx_forum_comments_post_parent_created,priority:1;comment:帖子ID"`
ParentCommentID *uint64 `gorm:"column:parent_comment_id;index:idx_forum_comments_post_parent_created,priority:2;comment:父评论ID根评论为空"`
UserID uint64 `gorm:"column:user_id;not null;uniqueIndex:uk_forum_comments_user_idem,priority:1;index:idx_forum_comments_user;comment:评论用户ID"`
Content string `gorm:"column:content;type:text;not null;comment:评论内容"`
Status string `gorm:"column:status;type:varchar(32);not null;default:'visible';index:idx_forum_comments_status;comment:visible/deleted"`
IdempotencyKey *string `gorm:"column:idempotency_key;type:varchar(128);uniqueIndex:uk_forum_comments_user_idem,priority:2;comment:评论创建幂等键"`
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;index:idx_forum_comments_post_parent_created,priority:3;comment:创建时间"`
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;comment:更新时间"`
DeletedAt *time.Time `gorm:"column:deleted_at;comment:用户删除时间"`
}
func (ForumComment) TableName() string {
return "forum_comments"
}
// ForumImport 是一键导入记录表。
//
// 职责边界:
// 1. 通过 post_id + user_id 唯一约束保证同一用户同一计划只导入一次;
// 2. 只记录导入到 TaskClass 的结果,不写 schedule
// 3. event_id 对应导入奖励事件,供 token-store 账本幂等使用。
type ForumImport struct {
ID uint64 `gorm:"column:id;primaryKey;autoIncrement"`
PostID uint64 `gorm:"column:post_id;not null;uniqueIndex:uk_forum_imports_post_user,priority:1;index:idx_forum_imports_post;comment:帖子ID"`
UserID uint64 `gorm:"column:user_id;not null;uniqueIndex:uk_forum_imports_post_user,priority:2;uniqueIndex:uk_forum_imports_user_idem,priority:1;index:idx_forum_imports_user;comment:导入用户ID"`
AuthorUserID uint64 `gorm:"column:author_user_id;not null;index:idx_forum_imports_author;comment:帖子作者ID便于奖励和审计"`
NewTaskClassID *uint64 `gorm:"column:new_task_class_id;comment:导入后创建的当前用户TaskClass IDpending/failed 时为空"`
TargetTitle string `gorm:"column:target_title;type:varchar(80);comment:导入后的TaskClass标题"`
Status string `gorm:"column:status;type:varchar(32);not null;default:'pending';comment:pending/imported/failed"`
EventID string `gorm:"column:event_id;type:varchar(128);not null;uniqueIndex:uk_forum_imports_event;comment:导入事件ID"`
IdempotencyKey *string `gorm:"column:idempotency_key;type:varchar(128);uniqueIndex:uk_forum_imports_user_idem,priority:2;comment:导入请求幂等键"`
LastError *string `gorm:"column:last_error;type:text;comment:最近一次导入失败原因"`
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间"`
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;comment:更新时间"`
}
func (ForumImport) TableName() string {
return "forum_imports"
}