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 }