feat: add announcement data models

Add announcement and read-tracking models with active/unread query helpers for the operations announcement system.
This commit is contained in:
zizi 2026-05-20 13:01:47 +08:00
parent 1de1cdb14f
commit 6a409b7724
3 changed files with 129 additions and 0 deletions

92
model/announcement.go Normal file
View File

@ -0,0 +1,92 @@
package model
import (
"time"
"gorm.io/gorm"
)
type Announcement struct {
Id int `json:"id" gorm:"primaryKey;autoIncrement"`
Title string `json:"title" gorm:"type:varchar(255);not null"`
Content string `json:"content" gorm:"type:text;not null"`
ContentHtml string `json:"content_html" gorm:"type:text"`
Status string `json:"status" gorm:"type:varchar(16);not null;default:'draft';index"`
NotifyMode string `json:"notify_mode" gorm:"type:varchar(16);not null;default:'silent'"`
JsonRules string `json:"json_rules" gorm:"type:text"`
StartsAt *time.Time `json:"starts_at" gorm:"index"`
EndsAt *time.Time `json:"ends_at" gorm:"index"`
CreatedBy int `json:"created_by" gorm:"not null;default:0"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (Announcement) TableName() string {
return "announcements"
}
func (a *Announcement) Insert() error {
return DB.Create(a).Error
}
func (a *Announcement) Update() error {
return DB.Save(a).Error
}
func GetAnnouncementByID(id int) (*Announcement, error) {
var announcement Announcement
err := DB.First(&announcement, "id = ?", id).Error
return &announcement, err
}
func GetAnnouncements(page int, pageSize int, status string) ([]Announcement, int64, error) {
var announcements []Announcement
var total int64
query := DB.Model(&Announcement{})
if status != "" {
query = query.Where("status = ?", status)
}
if err := query.Count(&total).Error; err != nil {
return nil, 0, err
}
if page < 1 {
page = 1
}
if pageSize < 1 {
pageSize = 20
}
err := query.Order("id DESC").Offset((page - 1) * pageSize).Limit(pageSize).Find(&announcements).Error
return announcements, total, err
}
func GetActiveAnnouncements() ([]Announcement, error) {
var announcements []Announcement
err := activeAnnouncementQuery().
Order("id DESC").
Find(&announcements).Error
return announcements, err
}
func GetUnreadAnnouncements(userId int) ([]Announcement, error) {
var announcements []Announcement
readSubQuery := DB.Model(&AnnouncementRead{}).
Select("announcement_id").
Where("user_id = ?", userId)
err := activeAnnouncementQuery().
Where("id NOT IN (?)", readSubQuery).
Order("id DESC").
Find(&announcements).Error
return announcements, err
}
func DeleteAnnouncementByID(id int) error {
return DB.Delete(&Announcement{}, id).Error
}
func activeAnnouncementQuery() *gorm.DB {
now := time.Now()
return DB.Model(&Announcement{}).
Where("status = ?", "active").
Where("(starts_at IS NULL OR starts_at <= ?)", now).
Where("(ends_at IS NULL OR ends_at >= ?)", now)
}

View File

@ -0,0 +1,33 @@
package model
import "time"
type AnnouncementRead struct {
Id int `json:"id" gorm:"primaryKey;autoIncrement"`
AnnouncementId int `json:"announcement_id" gorm:"not null;index:idx_announcement_read_user,unique"`
UserId int `json:"user_id" gorm:"not null;index:idx_announcement_read_user,unique"`
ReadAt time.Time `json:"read_at" gorm:"not null"`
}
func (AnnouncementRead) TableName() string {
return "announcement_reads"
}
func (r *AnnouncementRead) Insert() error {
return DB.Create(r).Error
}
func IsAnnouncementRead(announcementId int, userId int) (bool, error) {
var count int64
err := DB.Model(&AnnouncementRead{}).
Where("announcement_id = ? AND user_id = ?", announcementId, userId).
Count(&count).Error
return count > 0, err
}
type AnnouncementReadStatus struct {
UserId int `json:"user_id"`
Username string `json:"username"`
ReadAt *time.Time `json:"read_at"`
IsRead bool `json:"is_read"`
}

View File

@ -283,6 +283,8 @@ func migrateDB() error {
&PerfMetric{},
&PromoCode{},
&PromoCodeUsage{},
&Announcement{},
&AnnouncementRead{},
)
if err != nil {
return err
@ -334,6 +336,8 @@ func migrateDBFast() error {
{&PerfMetric{}, "PerfMetric"},
{&PromoCode{}, "PromoCode"},
{&PromoCodeUsage{}, "PromoCodeUsage"},
{&Announcement{}, "Announcement"},
{&AnnouncementRead{}, "AnnouncementRead"},
}
// 动态计算migration数量确保errChan缓冲区足够大
errChan := make(chan error, len(migrations))