427 lines
12 KiB
Go
427 lines
12 KiB
Go
package controllers
|
||
|
||
import (
|
||
"strings"
|
||
"time"
|
||
|
||
"server/models"
|
||
"server/pkg/jwtutil"
|
||
|
||
beego "github.com/beego/beego/v2/server/web"
|
||
"github.com/beego/beego/v2/client/orm"
|
||
)
|
||
|
||
// =============================================================
|
||
// 平台官网 - 站点公告 /platform/website/notice/*
|
||
// 表:yz_platform_website_notice
|
||
// 建表 SQL:docs/sql/create_platform_website_notice.sql
|
||
//
|
||
// 公告状态:0 草稿 / 1 已发布 / 2 已下线。
|
||
// 前台 GET /site/notices 只返回「已发布」且当前时间处于有效期内的公告,
|
||
// 见 index_website.go 的 noticeVisibleCond。
|
||
// =============================================================
|
||
|
||
const (
|
||
noticeStatusDraft int8 = 0
|
||
noticeStatusPublished int8 = 1
|
||
noticeStatusOffline int8 = 2
|
||
)
|
||
|
||
type PlatformWebsiteNoticeController struct {
|
||
beego.Controller
|
||
}
|
||
|
||
type platformWebsiteNoticePayload struct {
|
||
Title *string `json:"title"`
|
||
Summary *string `json:"summary"`
|
||
Content *string `json:"content"`
|
||
Type *int8 `json:"type"`
|
||
LinkURL *string `json:"link_url"`
|
||
IsPopup *int8 `json:"is_popup"`
|
||
Top *int8 `json:"top"`
|
||
Sort *int `json:"sort"`
|
||
Status *int8 `json:"status"`
|
||
StartTime *string `json:"start_time"`
|
||
EndTime *string `json:"end_time"`
|
||
}
|
||
|
||
// parseNoticeTime 解析前端时间字符串,支持 "2006-01-02 15:04:05"、RFC3339、"2006-01-02";
|
||
// 空串或无法识别时返回 nil(对应数据库 NULL,表示不限制时间)。
|
||
func parseNoticeTime(raw *string) *time.Time {
|
||
if raw == nil {
|
||
return nil
|
||
}
|
||
v := strings.TrimSpace(*raw)
|
||
if v == "" {
|
||
return nil
|
||
}
|
||
for _, layout := range []string{"2006-01-02 15:04:05", time.RFC3339, "2006-01-02"} {
|
||
if t, err := time.ParseInLocation(layout, v, time.Local); err == nil {
|
||
return &t
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// noticeVisibleCond 前台可见条件:未删除 + 已发布 + 当前时间落在 [start_time, end_time] 内
|
||
// start_time / end_time 为 NULL 表示该侧不限制。
|
||
func noticeVisibleCond() *orm.Condition {
|
||
now := time.Now()
|
||
return orm.NewCondition().
|
||
And("delete_time__isnull", true).
|
||
And("status", noticeStatusPublished).
|
||
AndCond(orm.NewCondition().Or("start_time__isnull", true).Or("start_time__lte", now)).
|
||
AndCond(orm.NewCondition().Or("end_time__isnull", true).Or("end_time__gte", now))
|
||
}
|
||
|
||
// noticeFromPayload 依据请求体构造新公告,缺省:类型=通知公告、状态=草稿
|
||
func noticeFromPayload(p platformWebsiteNoticePayload) models.PlatformWebsiteNotice {
|
||
row := models.PlatformWebsiteNotice{
|
||
Type: 1,
|
||
Status: noticeStatusDraft,
|
||
}
|
||
if p.Title != nil {
|
||
row.Title = strings.TrimSpace(*p.Title)
|
||
}
|
||
if p.Summary != nil {
|
||
row.Summary = strings.TrimSpace(*p.Summary)
|
||
}
|
||
if p.Content != nil {
|
||
row.Content = *p.Content
|
||
}
|
||
if p.Type != nil {
|
||
row.Type = *p.Type
|
||
}
|
||
if p.LinkURL != nil {
|
||
row.LinkURL = strings.TrimSpace(*p.LinkURL)
|
||
}
|
||
if p.IsPopup != nil {
|
||
row.IsPopup = *p.IsPopup
|
||
}
|
||
if p.Top != nil {
|
||
row.Top = *p.Top
|
||
}
|
||
if p.Sort != nil {
|
||
row.Sort = *p.Sort
|
||
}
|
||
if p.Status != nil {
|
||
row.Status = *p.Status
|
||
}
|
||
row.StartTime = parseNoticeTime(p.StartTime)
|
||
row.EndTime = parseNoticeTime(p.EndTime)
|
||
return row
|
||
}
|
||
|
||
// noticeUpdateMap 把请求体转成待更新字段,只包含显式传入的键(未传的字段保持原值)
|
||
func noticeUpdateMap(p platformWebsiteNoticePayload) map[string]interface{} {
|
||
up := map[string]interface{}{}
|
||
if p.Title != nil {
|
||
up["title"] = strings.TrimSpace(*p.Title)
|
||
}
|
||
if p.Summary != nil {
|
||
up["summary"] = strings.TrimSpace(*p.Summary)
|
||
}
|
||
if p.Content != nil {
|
||
up["content"] = *p.Content
|
||
}
|
||
if p.Type != nil {
|
||
up["type"] = *p.Type
|
||
}
|
||
if p.LinkURL != nil {
|
||
up["link_url"] = strings.TrimSpace(*p.LinkURL)
|
||
}
|
||
if p.IsPopup != nil {
|
||
up["is_popup"] = *p.IsPopup
|
||
}
|
||
if p.Top != nil {
|
||
up["top"] = *p.Top
|
||
}
|
||
if p.Sort != nil {
|
||
up["sort"] = *p.Sort
|
||
}
|
||
if p.Status != nil {
|
||
up["status"] = *p.Status
|
||
}
|
||
if p.StartTime != nil {
|
||
up["start_time"] = parseNoticeTime(p.StartTime)
|
||
}
|
||
if p.EndTime != nil {
|
||
up["end_time"] = parseNoticeTime(p.EndTime)
|
||
}
|
||
return up
|
||
}
|
||
|
||
// List GET /platform/website/notice/list
|
||
// query: keyword(标题/摘要)、type、status、is_popup、page、pageSize
|
||
func (c *PlatformWebsiteNoticeController) List() {
|
||
if _, ok := websiteAuth(&c.Controller); !ok {
|
||
return
|
||
}
|
||
page, pageSize := websitePaging(&c.Controller)
|
||
|
||
cond := orm.NewCondition().And("delete_time__isnull", true)
|
||
if kw := strings.TrimSpace(c.GetString("keyword")); kw != "" {
|
||
cond = cond.AndCond(orm.NewCondition().
|
||
Or("title__icontains", kw).
|
||
Or("summary__icontains", kw))
|
||
}
|
||
if tp := websiteIntParam(&c.Controller, "type"); tp >= 0 {
|
||
cond = cond.And("type", tp)
|
||
}
|
||
if st := websiteStatusParam(&c.Controller); st >= 0 {
|
||
cond = cond.And("status", st)
|
||
}
|
||
if pop := websiteIntParam(&c.Controller, "is_popup"); pop >= 0 {
|
||
cond = cond.And("is_popup", pop)
|
||
}
|
||
|
||
qs := models.Orm.QueryTable(new(models.PlatformWebsiteNotice)).SetCond(cond)
|
||
total, err := qs.Count()
|
||
if err != nil {
|
||
jsonErr(&c.Controller, 500, 500, "获取失败: "+err.Error())
|
||
return
|
||
}
|
||
// 列表不返回富文本正文,减小响应体
|
||
var rows []models.PlatformWebsiteNotice
|
||
if _, err = qs.OrderBy("-top", "sort", "-id").
|
||
Limit(pageSize, (page-1)*pageSize).
|
||
All(&rows, "ID", "Title", "Summary", "Type", "LinkURL", "IsPopup", "Top",
|
||
"Sort", "Status", "StartTime", "EndTime", "Views",
|
||
"PublishTime", "CreateTime", "UpdateTime"); err != nil {
|
||
jsonErr(&c.Controller, 500, 500, "获取失败: "+err.Error())
|
||
return
|
||
}
|
||
websiteOK(&c.Controller, map[string]interface{}{"list": rows, "total": total})
|
||
}
|
||
|
||
// Get GET /platform/website/notice/:id — 含正文,供编辑回显
|
||
func (c *PlatformWebsiteNoticeController) Get() {
|
||
if _, ok := websiteAuth(&c.Controller); !ok {
|
||
return
|
||
}
|
||
id, ok := websiteID(&c.Controller)
|
||
if !ok {
|
||
return
|
||
}
|
||
var row models.PlatformWebsiteNotice
|
||
if err := models.Orm.QueryTable(new(models.PlatformWebsiteNotice)).
|
||
Filter("id", id).
|
||
Filter("delete_time__isnull", true).
|
||
One(&row); err != nil {
|
||
jsonErr(&c.Controller, 404, 404, "公告不存在")
|
||
return
|
||
}
|
||
websiteOK(&c.Controller, row)
|
||
}
|
||
|
||
// Create POST /platform/website/notice
|
||
func (c *PlatformWebsiteNoticeController) Create() {
|
||
claims, ok := websiteAuth(&c.Controller)
|
||
if !ok {
|
||
return
|
||
}
|
||
var p platformWebsiteNoticePayload
|
||
if !websiteBind(&c.Controller, &p) {
|
||
return
|
||
}
|
||
if p.Title == nil || strings.TrimSpace(*p.Title) == "" {
|
||
jsonErr(&c.Controller, 400, 400, "公告标题不能为空")
|
||
return
|
||
}
|
||
|
||
row := noticeFromPayload(p)
|
||
// 直接提交为已发布时同步补发布时间与发布人
|
||
if row.Status == noticeStatusPublished {
|
||
now := time.Now()
|
||
uid := uint64(claims.UserID)
|
||
row.PublishTime = &now
|
||
row.PublisherID = &uid
|
||
}
|
||
|
||
id, err := models.Orm.Insert(&row)
|
||
if err != nil {
|
||
jsonErr(&c.Controller, 500, 500, "创建失败: "+err.Error())
|
||
return
|
||
}
|
||
websiteOK(&c.Controller, map[string]interface{}{"id": id})
|
||
}
|
||
|
||
// Update POST /platform/website/notice/:id
|
||
func (c *PlatformWebsiteNoticeController) Update() {
|
||
claims, ok := websiteAuth(&c.Controller)
|
||
if !ok {
|
||
return
|
||
}
|
||
id, ok := websiteID(&c.Controller)
|
||
if !ok {
|
||
return
|
||
}
|
||
var p platformWebsiteNoticePayload
|
||
if !websiteBind(&c.Controller, &p) {
|
||
return
|
||
}
|
||
if p.Title != nil && strings.TrimSpace(*p.Title) == "" {
|
||
jsonErr(&c.Controller, 400, 400, "公告标题不能为空")
|
||
return
|
||
}
|
||
|
||
// 先取原记录:既用于 404 判断,也用于判断是否「首次转为已发布」
|
||
var row models.PlatformWebsiteNotice
|
||
if err := models.Orm.QueryTable(new(models.PlatformWebsiteNotice)).
|
||
Filter("id", id).
|
||
Filter("delete_time__isnull", true).
|
||
One(&row, "ID", "Status"); err != nil {
|
||
jsonErr(&c.Controller, 404, 404, "公告不存在")
|
||
return
|
||
}
|
||
|
||
up := noticeUpdateMap(p)
|
||
if len(up) == 0 {
|
||
jsonErr(&c.Controller, 400, 400, "无更新字段")
|
||
return
|
||
}
|
||
if p.Status != nil && *p.Status == noticeStatusPublished && row.Status != noticeStatusPublished {
|
||
// 首次发布才写发布时间,避免每次保存覆盖发布时间
|
||
now := time.Now()
|
||
uid := uint64(claims.UserID)
|
||
up["publish_time"] = now
|
||
up["publisher_id"] = uid
|
||
}
|
||
up["update_time"] = time.Now()
|
||
|
||
if _, err := models.Orm.QueryTable(new(models.PlatformWebsiteNotice)).
|
||
Filter("id", id).
|
||
Filter("delete_time__isnull", true).
|
||
Update(up); err != nil {
|
||
jsonErr(&c.Controller, 500, 500, "更新失败: "+err.Error())
|
||
return
|
||
}
|
||
websiteOK(&c.Controller, nil)
|
||
}
|
||
|
||
// noticeToggle 更新单个开关字段(status / top / is_popup)
|
||
func (c *PlatformWebsiteNoticeController) noticeToggle(claims *jwtutil.Claims, field string, value int8) {
|
||
id, ok := websiteID(&c.Controller)
|
||
if !ok {
|
||
return
|
||
}
|
||
up := map[string]interface{}{field: value, "update_time": time.Now()}
|
||
// 发布时补发布时间与发布人(其余开关无需登录身份,claims 可能为 nil)
|
||
if field == "status" && value == noticeStatusPublished {
|
||
up["publish_time"] = time.Now()
|
||
if claims != nil {
|
||
up["publisher_id"] = uint64(claims.UserID)
|
||
}
|
||
}
|
||
n, err := models.Orm.QueryTable(new(models.PlatformWebsiteNotice)).
|
||
Filter("id", id).
|
||
Filter("delete_time__isnull", true).
|
||
Update(up)
|
||
if err != nil {
|
||
jsonErr(&c.Controller, 500, 500, "操作失败: "+err.Error())
|
||
return
|
||
}
|
||
if n == 0 {
|
||
jsonErr(&c.Controller, 404, 404, "公告不存在")
|
||
return
|
||
}
|
||
websiteOK(&c.Controller, nil)
|
||
}
|
||
|
||
// Publish POST /platform/website/notice/:id/publish — 状态置为已发布
|
||
func (c *PlatformWebsiteNoticeController) Publish() {
|
||
claims, ok := websiteAuth(&c.Controller)
|
||
if !ok {
|
||
return
|
||
}
|
||
c.noticeToggle(claims, "status", noticeStatusPublished)
|
||
}
|
||
|
||
// UnPublish POST /platform/website/notice/:id/unpublish — 状态置为已下线
|
||
func (c *PlatformWebsiteNoticeController) UnPublish() {
|
||
if _, ok := websiteAuth(&c.Controller); !ok {
|
||
return
|
||
}
|
||
c.noticeToggle(nil, "status", noticeStatusOffline)
|
||
}
|
||
|
||
// Top POST /platform/website/notice/:id/top
|
||
func (c *PlatformWebsiteNoticeController) Top() {
|
||
if _, ok := websiteAuth(&c.Controller); !ok {
|
||
return
|
||
}
|
||
c.noticeToggle(nil, "top", 1)
|
||
}
|
||
|
||
// UnTop POST /platform/website/notice/:id/untop
|
||
func (c *PlatformWebsiteNoticeController) UnTop() {
|
||
if _, ok := websiteAuth(&c.Controller); !ok {
|
||
return
|
||
}
|
||
c.noticeToggle(nil, "top", 0)
|
||
}
|
||
|
||
// Popup POST /platform/website/notice/:id/popup — 标记为弹窗公告
|
||
func (c *PlatformWebsiteNoticeController) Popup() {
|
||
if _, ok := websiteAuth(&c.Controller); !ok {
|
||
return
|
||
}
|
||
c.noticeToggle(nil, "is_popup", 1)
|
||
}
|
||
|
||
// UnPopup POST /platform/website/notice/:id/unpopup
|
||
func (c *PlatformWebsiteNoticeController) UnPopup() {
|
||
if _, ok := websiteAuth(&c.Controller); !ok {
|
||
return
|
||
}
|
||
c.noticeToggle(nil, "is_popup", 0)
|
||
}
|
||
|
||
// Delete DELETE /platform/website/notice/:id — 软删除
|
||
func (c *PlatformWebsiteNoticeController) Delete() {
|
||
if _, ok := websiteAuth(&c.Controller); !ok {
|
||
return
|
||
}
|
||
id, ok := websiteID(&c.Controller)
|
||
if !ok {
|
||
return
|
||
}
|
||
now := time.Now()
|
||
n, err := models.Orm.QueryTable(new(models.PlatformWebsiteNotice)).
|
||
Filter("id", id).
|
||
Filter("delete_time__isnull", true).
|
||
Update(map[string]interface{}{"delete_time": now, "update_time": now})
|
||
if err != nil {
|
||
jsonErr(&c.Controller, 500, 500, "删除失败: "+err.Error())
|
||
return
|
||
}
|
||
if n == 0 {
|
||
jsonErr(&c.Controller, 404, 404, "公告不存在")
|
||
return
|
||
}
|
||
websiteOK(&c.Controller, nil)
|
||
}
|
||
|
||
// BatchDelete POST /platform/website/notice/batchdelete — body { ids: [] }
|
||
func (c *PlatformWebsiteNoticeController) BatchDelete() {
|
||
if _, ok := websiteAuth(&c.Controller); !ok {
|
||
return
|
||
}
|
||
var p struct {
|
||
IDs []uint64 `json:"ids"`
|
||
}
|
||
if !websiteBind(&c.Controller, &p) || len(p.IDs) == 0 {
|
||
jsonErr(&c.Controller, 400, 400, "请选择要删除的公告")
|
||
return
|
||
}
|
||
now := time.Now()
|
||
if _, err := models.Orm.QueryTable(new(models.PlatformWebsiteNotice)).
|
||
Filter("id__in", p.IDs).
|
||
Filter("delete_time__isnull", true).
|
||
Update(map[string]interface{}{"delete_time": now, "update_time": now}); err != nil {
|
||
jsonErr(&c.Controller, 500, 500, "删除失败: "+err.Error())
|
||
return
|
||
}
|
||
websiteOK(&c.Controller, nil)
|
||
}
|