spark-server/internal/transport/http/handler/admin/moderation.go
2026-09-29 16:37:36 +08:00

158 lines
3.8 KiB
Go

package adminhandler
import (
"errors"
"net/http"
"strconv"
"time"
"github.com/gin-gonic/gin"
modlogic "spark-server/internal/module/moderation/logic"
"spark-server/internal/pkg/errs"
"spark-server/internal/pkg/reqctx"
"spark-server/internal/pkg/validate"
"spark-server/internal/transport/http/respond"
)
type ModerationHandler struct {
svc *modlogic.Service
}
func NewModeration(svc *modlogic.Service) *ModerationHandler {
return &ModerationHandler{svc: svc}
}
func (h *ModerationHandler) ready(c *gin.Context) bool {
if h == nil || h.svc == nil || !h.svc.Ready() {
Fail(c, http.StatusServiceUnavailable, "moderation 服务未就绪")
return false
}
return true
}
func (h *ModerationHandler) ListWords(c *gin.Context) {
if !h.ready(c) {
return
}
page, _ := strconv.Atoi(c.DefaultQuery("page", "0"))
size, _ := strconv.Atoi(c.DefaultQuery("size", "50"))
cat, _ := strconv.Atoi(c.Query("category"))
var enabled *bool
if v := c.Query("enabled"); v == "1" || v == "true" {
enabled = ptrBool(true)
} else if v == "0" || v == "false" {
enabled = ptrBool(false)
}
list, total, err := h.svc.ListWords(c.Request.Context(), c.Query("q"), cat, enabled, page, size)
if err != nil {
Fail(c, http.StatusInternalServerError, err.Error())
return
}
OK(c, gin.H{"items": list, "total": total})
}
func (h *ModerationHandler) CreateWord(c *gin.Context) {
if !h.ready(c) {
return
}
var in modlogic.CreateWordInput
if err := respond.BindJSON(c, &in); err != nil {
return
}
if err := validate.StructLang(reqctx.Lang(c.Request.Context()), &in); err != nil {
respond.JSONError(c, err)
return
}
out, err := h.svc.CreateWord(c.Request.Context(), &in)
if err != nil {
failModeration(c, err)
return
}
OK(c, out)
}
func (h *ModerationHandler) PatchWord(c *gin.Context) {
if !h.ready(c) {
return
}
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil || id <= 0 {
Fail(c, http.StatusBadRequest, "invalid id")
return
}
var in modlogic.PatchWordInput
if err := respond.BindJSON(c, &in); err != nil {
return
}
if err := h.svc.PatchWord(c.Request.Context(), id, &in); err != nil {
failModeration(c, err)
return
}
OK(c, gin.H{"ok": true})
}
func (h *ModerationHandler) DeleteWord(c *gin.Context) {
if !h.ready(c) {
return
}
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil || id <= 0 {
Fail(c, http.StatusBadRequest, "invalid id")
return
}
if err := h.svc.DeleteWord(c.Request.Context(), id); err != nil {
failModeration(c, err)
return
}
OK(c, gin.H{"ok": true})
}
func (h *ModerationHandler) Reload(c *gin.Context) {
if !h.ready(c) {
return
}
if err := h.svc.Reload(c.Request.Context()); err != nil {
Fail(c, http.StatusInternalServerError, err.Error())
return
}
OK(c, gin.H{"ok": true})
}
func (h *ModerationHandler) ListHits(c *gin.Context) {
if !h.ready(c) {
return
}
page, _ := strconv.Atoi(c.DefaultQuery("page", "0"))
size, _ := strconv.Atoi(c.DefaultQuery("size", "50"))
uid, _ := strconv.ParseInt(c.Query("uid"), 10, 64)
cat, _ := strconv.Atoi(c.Query("category"))
var since, until *time.Time
if s := c.Query("since"); s != "" {
if t, err := time.Parse(time.RFC3339, s); err == nil {
since = &t
}
}
if s := c.Query("until"); s != "" {
if t, err := time.Parse(time.RFC3339, s); err == nil {
until = &t
}
}
list, total, err := h.svc.ListHits(c.Request.Context(), uid, cat, since, until, page, size)
if err != nil {
Fail(c, http.StatusInternalServerError, err.Error())
return
}
OK(c, gin.H{"items": list, "total": total})
}
func failModeration(c *gin.Context, err error) {
if errors.Is(err, errs.ErrInvalidArgument) || errors.Is(err, errs.ErrDuplicate) || errors.Is(err, errs.ErrNotFound) {
respond.JSONError(c, err)
return
}
Fail(c, http.StatusInternalServerError, err.Error())
}
func ptrBool(v bool) *bool { return &v }