Files
mengyastore/mengyastore-backend/internal/handlers/order.go
2026-03-20 20:58:24 +08:00

184 lines
5.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package handlers
import (
"fmt"
"log"
"net/http"
"net/url"
"strings"
"github.com/gin-gonic/gin"
"mengyastore-backend/internal/auth"
"mengyastore-backend/internal/models"
"mengyastore-backend/internal/storage"
)
const qrSize = "320x320"
type OrderHandler struct {
productStore *storage.JSONStore
orderStore *storage.OrderStore
authClient *auth.SproutGateClient
}
type checkoutPayload struct {
ProductID string `json:"productId"`
Quantity int `json:"quantity"`
}
func NewOrderHandler(productStore *storage.JSONStore, orderStore *storage.OrderStore, authClient *auth.SproutGateClient) *OrderHandler {
return &OrderHandler{productStore: productStore, orderStore: orderStore, authClient: authClient}
}
func (h *OrderHandler) tryExtractUser(c *gin.Context) (string, string) {
authHeader := c.GetHeader("Authorization")
if authHeader == "" || !strings.HasPrefix(authHeader, "Bearer ") {
log.Println("[Order] 无 Authorization header匿名下单")
return "", ""
}
userToken := strings.TrimPrefix(authHeader, "Bearer ")
log.Printf("[Order] 检测到用户 token正在验证 (长度=%d)", len(userToken))
result, err := h.authClient.VerifyToken(userToken)
if err != nil {
log.Printf("[Order] 验证 token 失败: %v", err)
return "", ""
}
if !result.Valid {
log.Println("[Order] token 验证返回 valid=false")
return "", ""
}
if result.User == nil {
log.Println("[Order] token 验证成功但 user 为空")
return "", ""
}
log.Printf("[Order] 用户身份验证成功: account=%s username=%s", result.User.Account, result.User.Username)
return result.User.Account, result.User.Username
}
func (h *OrderHandler) CreateOrder(c *gin.Context) {
userAccount, userName := h.tryExtractUser(c)
var payload checkoutPayload
if err := c.ShouldBindJSON(&payload); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid payload"})
return
}
payload.ProductID = strings.TrimSpace(payload.ProductID)
if payload.ProductID == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "missing required fields"})
return
}
if payload.Quantity <= 0 {
payload.Quantity = 1
}
product, err := h.productStore.GetByID(payload.ProductID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
return
}
if !product.Active {
c.JSON(http.StatusBadRequest, gin.H{"error": "product is not available"})
return
}
if product.Quantity < payload.Quantity {
c.JSON(http.StatusBadRequest, gin.H{"error": "库存不足"})
return
}
deliveredCodes, ok := extractCodes(&product, payload.Quantity)
if !ok {
c.JSON(http.StatusBadRequest, gin.H{"error": "卡密不足"})
return
}
product.Quantity = len(product.Codes)
updatedProduct, err := h.productStore.Update(product.ID, product)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
order := models.Order{
ProductID: updatedProduct.ID,
ProductName: updatedProduct.Name,
UserAccount: userAccount,
UserName: userName,
Quantity: payload.Quantity,
DeliveredCodes: deliveredCodes,
Status: "pending",
}
created, err := h.orderStore.Create(order)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
qrPayload := fmt.Sprintf("order:%s:%s", created.ID, created.ProductID)
qrURL := fmt.Sprintf("https://api.qrserver.com/v1/create-qr-code/?size=%s&data=%s", qrSize, url.QueryEscape(qrPayload))
c.JSON(http.StatusOK, gin.H{
"data": gin.H{
"orderId": created.ID,
"qrCodeUrl": qrURL,
"productId": created.ProductID,
"productQty": created.Quantity,
"viewCount": updatedProduct.ViewCount,
"status": created.Status,
},
})
}
func (h *OrderHandler) ConfirmOrder(c *gin.Context) {
orderID := c.Param("id")
order, err := h.orderStore.Confirm(orderID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{
"data": gin.H{
"orderId": order.ID,
"status": order.Status,
"deliveredCodes": order.DeliveredCodes,
},
})
}
func (h *OrderHandler) ListMyOrders(c *gin.Context) {
authHeader := c.GetHeader("Authorization")
if authHeader == "" || !strings.HasPrefix(authHeader, "Bearer ") {
c.JSON(http.StatusUnauthorized, gin.H{"error": "请先登录"})
return
}
userToken := strings.TrimPrefix(authHeader, "Bearer ")
result, err := h.authClient.VerifyToken(userToken)
if err != nil || !result.Valid || result.User == nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "登录已过期,请重新登录"})
return
}
orders, err := h.orderStore.ListByAccount(result.User.Account)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"data": orders})
}
func extractCodes(product *models.Product, count int) ([]string, bool) {
if count <= 0 {
return nil, false
}
if len(product.Codes) < count {
return nil, false
}
delivered := make([]string, count)
copy(delivered, product.Codes[:count])
product.Codes = product.Codes[count:]
return delivered, true
}