package handlers import ( "bufio" "fmt" "log/slog" "platform/pkg/env" "platform/web/auth" "platform/web/core" g "platform/web/globals" m "platform/web/models" s "platform/web/services" "time" "github.com/gofiber/fiber/v2" "github.com/valyala/fasthttp" ) type TradeCreateReq struct { s.CreateTradeData Type m.TradeType `json:"type" validate:"required"` Resource *s.CreateResourceData `json:"resource,omitempty"` Recharge *s.RechargeProductInfo `json:"recharge,omitempty"` } type TradeCreateResp struct { PayUrl string `json:"pay_url"` TradeNo string `json:"trade_no"` } func TradeCreate(c *fiber.Ctx) error { // 检查权限 authCtx, err := auth.GetAuthCtx(c).PermitUser() if err != nil { return err } // 解析请求参数 req := new(TradeCreateReq) if err := g.Validator.ParseBody(c, req); err != nil { return err } switch req.Type { case m.TradeTypePurchase: if req.Resource == nil { return core.NewBizErr("购买信息不能为空") } req.Product = req.Resource case m.TradeTypeRecharge: if req.Recharge == nil { return core.NewBizErr("充值信息不能为空") } req.Product = req.Recharge } // 创建交易 result, err := s.Trade.CreateTrade(authCtx.User.ID, time.Now(), &req.CreateTradeData) if err != nil { slog.Error("创建交易失败", "error", err) return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "创建交易失败"}) } return c.JSON(&TradeCreateResp{ PayUrl: result.PaymentUrl, TradeNo: result.TradeNo, }) } type TradeCompleteReq struct { s.ModifyTradeData } func TradeComplete(c *fiber.Ctx) error { // 检查权限 _, err := auth.GetAuthCtx(c).PermitUser() if err != nil { return err } // 解析请求参数 req := new(TradeCompleteReq) if err := g.Validator.ParseBody(c, req); err != nil { return err } // 检查订单状态 err = s.Trade.CompleteTrade(&req.ModifyTradeData) if err != nil { return err } return c.SendStatus(fiber.StatusNoContent) } type TradeCancelReq struct { s.ModifyTradeData } func TradeCancel(c *fiber.Ctx) error { // 检查权限 _, err := auth.GetAuthCtx(c).PermitUser() if err != nil { return err } // 解析请求参数 req := new(TradeCancelReq) if err := g.Validator.ParseBody(c, req); err != nil { return err } // 取消交易 err = s.Trade.CancelTrade(&req.ModifyTradeData, time.Now()) if err != nil { slog.Error("取消交易失败", "trade_no", req.TradeNo, "error", err) return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "取消交易失败"}) } return c.SendStatus(fiber.StatusNoContent) } type TradeCheckReq struct { s.ModifyTradeData } func TradeCheck(c *fiber.Ctx) error { // 解析请求参数 req := new(TradeCheckReq) if err := g.Validator.ParseQuery(c, req); err != nil { return err } c.Set(fiber.HeaderContentType, "text/event-stream") c.Set(fiber.HeaderCacheControl, "no-cache") c.Set(fiber.HeaderConnection, "keep-alive") c.Set(fiber.HeaderTransferEncoding, "chunked") c.Context().SetBodyStreamWriter(fasthttp.StreamWriter(func(w *bufio.Writer) { expire := env.TradeExpire interval := 5 for range expire / interval { // 检查订单状态 result, err := s.Trade.CheckTrade(&req.ModifyTradeData) if err != nil { slog.Error("检查订单状态失败", "trade_no", req.TradeNo, "error", err) return } // 写入订单状态 _, err = fmt.Fprintf(w, "data: %d\n\n", result.Status) if err != nil { slog.Error("写入订单状态失败", "trade_no", req.TradeNo, "error", err) return } err = w.Flush() if err != nil { return } // 当订单离开支付状态后结束查询 if result.Status != m.TradeStatusPending { return } time.Sleep(time.Duration(interval) * time.Second) } })) return nil }