package handlers import ( "bufio" "fmt" "log/slog" "platform/pkg/env" "platform/web/auth" "platform/web/core" g "platform/web/globals" m "platform/web/models" q "platform/web/queries" s "platform/web/services" "time" "github.com/gofiber/fiber/v2" "github.com/valyala/fasthttp" ) // PageTradeByAdmin 分页查询所有订单 func PageTradeByAdmin(c *fiber.Ctx) error { // 检查权限 _, err := auth.GetAuthCtx(c).PermitAdmin() if err != nil { return err } // 解析请求参数 req := new(core.PageReq) if err := g.Validator.ParseBody(c, req); err != nil { return err } // 查询用户列表 list, total, err := q.Trade. Joins(q.Trade.User). Select( q.Trade.ALL, q.User.As("User").Phone.As("User__phone"), q.User.As("User").Name.As("User__name"), ). FindByPage(req.GetOffset(), req.GetLimit()) if err != nil { return err } // 返回结果 return c.JSON(core.PageResp{ List: list, Total: int(total), Page: req.GetPage(), Size: req.GetSize(), }) } // 创建订单 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 } var product s.ProductInfo switch req.Type { case m.TradeTypePurchase: if req.Resource == nil { return core.NewBizErr("购买信息不能为空") } product, err = s.NewCreateResourceByTradeData(req.Resource) if err != nil { return core.NewServErr("处理购买产品信息失败", err) } case m.TradeTypeRecharge: if req.Recharge == nil { return core.NewBizErr("充值信息不能为空") } product = req.Recharge } // 创建交易 result, err := s.Trade.CreateTrade(authCtx.User.ID, time.Now(), &req.CreateTradeData, product) 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 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 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 TradeCompleteReq 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 TradeCancelReq 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 } type TradeCheckReq struct { s.ModifyTradeData }