Files
cpdaily_xiaozhushou/backed/internal/handler/admin_announcement.go
T
becomer 2e2c161770 feat: init cpdaily-xiaozhushou with blue-green deploy
- backed/: Go Fiber backend with cpdaily integration
- frondend/: Vue 3 + Vite frontend
- Dockerfile: multi-stage build (Node → Go → Alpine)
- deploy/docker-compose.yml: production compose file
- .woodpecker/build.yml: CI/CD with blue-green zero-downtime deploy
- /health endpoint for deployment health checks
- Port 8884/8885 blue-green swap via 1Panel OpenResty
2026-07-03 15:37:55 +08:00

171 lines
4.5 KiB
Go

package handler
import (
"cpdaily-new/internal/database"
"cpdaily-new/internal/model"
"cpdaily-new/internal/utils"
"errors"
"log/slog"
"time"
"github.com/gofiber/fiber/v2"
"gorm.io/gorm"
)
// CreateAnnouncementRequest 创建公告请求
type CreateAnnouncementRequest struct {
Title string `json:"title"`
Content string `json:"content"`
TargetType string `json:"targetType"` // agent / user
ForcePopup bool `json:"forcePopup"`
LoopPopup bool `json:"loopPopup"`
}
// CreateAnnouncement 创建公告
func CreateAnnouncement(c *fiber.Ctx) error {
adminID := c.Locals("adminId").(uint)
var req CreateAnnouncementRequest
if err := c.BodyParser(&req); err != nil {
return utils.FailParam(c, "请求参数错误")
}
if req.Title == "" {
return utils.FailParam(c, "公告标题不能为空")
}
if req.Content == "" {
return utils.FailParam(c, "公告内容不能为空")
}
if req.TargetType != "agent" && req.TargetType != "user" {
return utils.Fail(c, 400, "目标类型必须为 agent 或 user")
}
announcement := model.Announcement{
Title: req.Title,
Content: req.Content,
TargetType: req.TargetType,
ForcePopup: req.ForcePopup,
LoopPopup: req.LoopPopup,
CreatedBy: adminID,
}
if err := database.DB.Create(&announcement).Error; err != nil {
slog.Error("创建公告失败", "error", err)
return utils.FailServer(c, "创建公告失败")
}
slog.Info("创建公告", "id", announcement.ID, "title", req.Title, "targetType", req.TargetType)
return utils.Success(c, announcement)
}
// AnnouncementListItem 公告列表项(含统计)
type AnnouncementListItem struct {
model.Announcement
ReadCount int `json:"readCount"`
UnreadCount int `json:"unreadCount"`
TotalTargets int64 `json:"totalTargets"`
}
// ListAnnouncements 公告列表(含已读/未读统计)
func ListAnnouncements(c *fiber.Ctx) error {
var announcements []model.Announcement
database.DB.Order("created_at DESC").Find(&announcements)
// 获取目标总数
var totalAgents int64
var totalUsers int64
database.DB.Model(&model.AgentUser{}).Count(&totalAgents)
database.DB.Model(&model.SysUser{}).Count(&totalUsers)
items := make([]AnnouncementListItem, 0, len(announcements))
for _, a := range announcements {
var readCount int64
database.DB.Model(&model.AnnouncementRead{}).
Where("announcement_id = ?", a.ID).
Count(&readCount)
var totalTargets int64
if a.TargetType == "agent" {
totalTargets = totalAgents
} else {
totalTargets = totalUsers
}
items = append(items, AnnouncementListItem{
Announcement: a,
ReadCount: int(readCount),
UnreadCount: int(max(0, totalTargets-readCount)),
TotalTargets: totalTargets,
})
}
return utils.Success(c, items)
}
// GetAnnouncement 公告详情(含已读用户列表)
func GetAnnouncement(c *fiber.Ctx) error {
id := c.Params("id")
var announcement model.Announcement
if err := database.DB.First(&announcement, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return utils.Fail(c, 404, "公告不存在")
}
return utils.FailServer(c, "查询失败")
}
// 查询已读记录(带用户信息)
type ReadUser struct {
UserID uint `json:"userId"`
Username string `json:"username"`
ReadAt time.Time `json:"readAt"`
}
var reads []model.AnnouncementRead
database.DB.Where("announcement_id = ?", announcement.ID).Find(&reads)
readUsers := make([]ReadUser, 0, len(reads))
for _, r := range reads {
username := ""
if r.UserType == "agent" {
var agent model.AgentUser
if err := database.DB.First(&agent, r.UserID).Error; err == nil {
username = agent.Username
}
} else {
var user model.SysUser
if err := database.DB.First(&user, r.UserID).Error; err == nil {
username = user.Username
}
}
readUsers = append(readUsers, ReadUser{
UserID: r.UserID,
Username: username,
ReadAt: r.ReadAt,
})
}
return utils.Success(c, fiber.Map{
"announcement": announcement,
"readUsers": readUsers,
})
}
// DeleteAnnouncement 删除公告
func DeleteAnnouncement(c *fiber.Ctx) error {
id := c.Params("id")
result := database.DB.Delete(&model.Announcement{}, id)
if result.Error != nil {
return utils.FailServer(c, "删除失败")
}
if result.RowsAffected == 0 {
return utils.Fail(c, 404, "公告不存在")
}
// 同时删除已读记录
database.DB.Where("announcement_id = ?", id).Delete(&model.AnnouncementRead{})
slog.Info("删除公告", "id", id)
return utils.Success(c, nil)
}