Update mengyastore
This commit is contained in:
@@ -9,7 +9,7 @@ import (
|
||||
"mengyastore-backend/internal/models"
|
||||
)
|
||||
|
||||
type productPayload struct {
|
||||
type ProductPayload struct {
|
||||
Name string `json:"name"`
|
||||
Price float64 `json:"price"`
|
||||
DiscountPrice float64 `json:"discountPrice"`
|
||||
@@ -17,6 +17,7 @@ type productPayload struct {
|
||||
CoverURL string `json:"coverUrl"`
|
||||
Codes []string `json:"codes"`
|
||||
ScreenshotURLs []string `json:"screenshotUrls"`
|
||||
PaymentQrURLs []string `json:"paymentQrUrls"`
|
||||
Description string `json:"description"`
|
||||
Active *bool `json:"active"`
|
||||
RequireLogin bool `json:"requireLogin"`
|
||||
@@ -28,7 +29,7 @@ type productPayload struct {
|
||||
ShowContact bool `json:"showContact"`
|
||||
}
|
||||
|
||||
func normalizeFulfillmentPayload(payload *productPayload) string {
|
||||
func normalizeFulfillmentPayload(payload *ProductPayload) string {
|
||||
ft := strings.TrimSpace(strings.ToLower(payload.FulfillmentType))
|
||||
if ft == "fixed" {
|
||||
return "fixed"
|
||||
@@ -36,16 +37,26 @@ func normalizeFulfillmentPayload(payload *productPayload) string {
|
||||
return "card"
|
||||
}
|
||||
|
||||
type togglePayload struct {
|
||||
type TogglePayload struct {
|
||||
Active bool `json:"active"`
|
||||
}
|
||||
|
||||
// VerifyAdminToken checks whether the supplied token is correct.
|
||||
// Returns {"valid": true/false} without leaking the real token value.
|
||||
// AdminVerifyTokenRequest 管理端校验令牌(Body,非 Header)。
|
||||
type AdminVerifyTokenRequest struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
// VerifyAdminToken 校验请求中的令牌是否正确。
|
||||
// @Summary 校验管理员令牌
|
||||
// @Description 响应为 {"valid": true/false},不泄露真实口令。
|
||||
// @Tags 管理端-认证
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param body body AdminVerifyTokenRequest true "待校验 token"
|
||||
// @Success 200 {object} SwaggerValidBody
|
||||
// @Router /api/admin/verify [post]
|
||||
func (h *AdminHandler) VerifyAdminToken(c *gin.Context) {
|
||||
var payload struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
var payload AdminVerifyTokenRequest
|
||||
if err := c.ShouldBindJSON(&payload); err != nil || payload.Token == "" {
|
||||
c.JSON(http.StatusOK, gin.H{"valid": false})
|
||||
return
|
||||
@@ -53,6 +64,15 @@ func (h *AdminHandler) VerifyAdminToken(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"valid": payload.Token == h.cfg.AdminToken})
|
||||
}
|
||||
|
||||
// ListAllProducts 管理端商品全量列表(含下架与卡密等敏感字段)。
|
||||
// @Summary 全部商品(管理)
|
||||
// @Tags 管理端-商品
|
||||
// @Produce json
|
||||
// @Security AdminToken
|
||||
// @Success 200 {object} SwaggerProductListBody
|
||||
// @Failure 401 {object} SwaggerErrorBody
|
||||
// @Failure 500 {object} SwaggerErrorBody
|
||||
// @Router /api/admin/products [get]
|
||||
func (h *AdminHandler) ListAllProducts(c *gin.Context) {
|
||||
if !h.requireAdmin(c) {
|
||||
return
|
||||
@@ -65,18 +85,35 @@ func (h *AdminHandler) ListAllProducts(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"data": items})
|
||||
}
|
||||
|
||||
// CreateProduct 创建商品。
|
||||
// @Summary 创建商品
|
||||
// @Tags 管理端-商品
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security AdminToken
|
||||
// @Param body body ProductPayload true "商品字段"
|
||||
// @Success 200 {object} SwaggerProductOneBody
|
||||
// @Failure 400 {object} SwaggerErrorBody
|
||||
// @Failure 401 {object} SwaggerErrorBody
|
||||
// @Failure 500 {object} SwaggerErrorBody
|
||||
// @Router /api/admin/products [post]
|
||||
func (h *AdminHandler) CreateProduct(c *gin.Context) {
|
||||
if !h.requireAdmin(c) {
|
||||
return
|
||||
}
|
||||
var payload productPayload
|
||||
var payload ProductPayload
|
||||
if err := c.ShouldBindJSON(&payload); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数有误"})
|
||||
return
|
||||
}
|
||||
screenshotURLs, valid := normalizeScreenshotURLs(payload.ScreenshotURLs)
|
||||
if !valid {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "截图链接最多 5 条"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "截图链接最多 6 条"})
|
||||
return
|
||||
}
|
||||
paymentQrURLs, valid := normalizePaymentQrURLs(payload.PaymentQrURLs)
|
||||
if !valid {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "萌芽收款渠道链接最多 6 条"})
|
||||
return
|
||||
}
|
||||
active := true
|
||||
@@ -100,6 +137,7 @@ func (h *AdminHandler) CreateProduct(c *gin.Context) {
|
||||
CoverURL: strings.TrimSpace(payload.CoverURL),
|
||||
Codes: payload.Codes,
|
||||
ScreenshotURLs: screenshotURLs,
|
||||
PaymentQrURLs: paymentQrURLs,
|
||||
Description: payload.Description,
|
||||
Active: active,
|
||||
RequireLogin: payload.RequireLogin,
|
||||
@@ -118,19 +156,38 @@ func (h *AdminHandler) CreateProduct(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"data": created})
|
||||
}
|
||||
|
||||
// UpdateProduct 更新商品。
|
||||
// @Summary 更新商品
|
||||
// @Tags 管理端-商品
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security AdminToken
|
||||
// @Param id path string true "商品 ID"
|
||||
// @Param body body ProductPayload true "商品字段"
|
||||
// @Success 200 {object} SwaggerProductOneBody
|
||||
// @Failure 400 {object} SwaggerErrorBody
|
||||
// @Failure 401 {object} SwaggerErrorBody
|
||||
// @Failure 404 {object} SwaggerErrorBody
|
||||
// @Failure 500 {object} SwaggerErrorBody
|
||||
// @Router /api/admin/products/{id} [put]
|
||||
func (h *AdminHandler) UpdateProduct(c *gin.Context) {
|
||||
if !h.requireAdmin(c) {
|
||||
return
|
||||
}
|
||||
id := c.Param("id")
|
||||
var payload productPayload
|
||||
var payload ProductPayload
|
||||
if err := c.ShouldBindJSON(&payload); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数有误"})
|
||||
return
|
||||
}
|
||||
screenshotURLs, valid := normalizeScreenshotURLs(payload.ScreenshotURLs)
|
||||
if !valid {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "截图链接最多 5 条"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "截图链接最多 6 条"})
|
||||
return
|
||||
}
|
||||
paymentQrURLs, valid := normalizePaymentQrURLs(payload.PaymentQrURLs)
|
||||
if !valid {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "萌芽收款渠道链接最多 6 条"})
|
||||
return
|
||||
}
|
||||
active := false
|
||||
@@ -154,6 +211,7 @@ func (h *AdminHandler) UpdateProduct(c *gin.Context) {
|
||||
CoverURL: strings.TrimSpace(payload.CoverURL),
|
||||
Codes: payload.Codes,
|
||||
ScreenshotURLs: screenshotURLs,
|
||||
PaymentQrURLs: paymentQrURLs,
|
||||
Description: payload.Description,
|
||||
Active: active,
|
||||
RequireLogin: payload.RequireLogin,
|
||||
@@ -172,12 +230,25 @@ func (h *AdminHandler) UpdateProduct(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"data": updated})
|
||||
}
|
||||
|
||||
// ToggleProduct 上架/下架切换。
|
||||
// @Summary 切换上架状态
|
||||
// @Tags 管理端-商品
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security AdminToken
|
||||
// @Param id path string true "商品 ID"
|
||||
// @Param body body TogglePayload true "{active}"
|
||||
// @Success 200 {object} SwaggerProductOneBody
|
||||
// @Failure 400 {object} SwaggerErrorBody
|
||||
// @Failure 401 {object} SwaggerErrorBody
|
||||
// @Failure 404 {object} SwaggerErrorBody
|
||||
// @Router /api/admin/products/{id}/status [patch]
|
||||
func (h *AdminHandler) ToggleProduct(c *gin.Context) {
|
||||
if !h.requireAdmin(c) {
|
||||
return
|
||||
}
|
||||
id := c.Param("id")
|
||||
var payload togglePayload
|
||||
var payload TogglePayload
|
||||
if err := c.ShouldBindJSON(&payload); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数有误"})
|
||||
return
|
||||
@@ -190,6 +261,16 @@ func (h *AdminHandler) ToggleProduct(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"data": updated})
|
||||
}
|
||||
|
||||
// DeleteProduct 删除商品。
|
||||
// @Summary 删除商品
|
||||
// @Tags 管理端-商品
|
||||
// @Produce json
|
||||
// @Security AdminToken
|
||||
// @Param id path string true "商品 ID"
|
||||
// @Success 200 {object} SwaggerBoolOKWrap
|
||||
// @Failure 401 {object} SwaggerErrorBody
|
||||
// @Failure 500 {object} SwaggerErrorBody
|
||||
// @Router /api/admin/products/{id} [delete]
|
||||
func (h *AdminHandler) DeleteProduct(c *gin.Context) {
|
||||
if !h.requireAdmin(c) {
|
||||
return
|
||||
@@ -202,6 +283,26 @@ func (h *AdminHandler) DeleteProduct(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"data": gin.H{"ok": true}})
|
||||
}
|
||||
|
||||
const maxScreenshotURLsAdmin = 6
|
||||
const maxPaymentQrURLsAdmin = 6
|
||||
|
||||
func normalizePaymentQrURLs(urls []string) ([]string, bool) {
|
||||
cleaned := make([]string, 0, len(urls))
|
||||
seen := map[string]bool{}
|
||||
for _, u := range urls {
|
||||
trimmed := strings.TrimSpace(u)
|
||||
if trimmed == "" || seen[trimmed] {
|
||||
continue
|
||||
}
|
||||
seen[trimmed] = true
|
||||
cleaned = append(cleaned, trimmed)
|
||||
if len(cleaned) > maxPaymentQrURLsAdmin {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
return cleaned, true
|
||||
}
|
||||
|
||||
func normalizeScreenshotURLs(urls []string) ([]string, bool) {
|
||||
cleaned := make([]string, 0, len(urls))
|
||||
for _, url := range urls {
|
||||
@@ -210,7 +311,7 @@ func normalizeScreenshotURLs(urls []string) ([]string, bool) {
|
||||
continue
|
||||
}
|
||||
cleaned = append(cleaned, trimmed)
|
||||
if len(cleaned) > 5 {
|
||||
if len(cleaned) > maxScreenshotURLsAdmin {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user