package handlers import ( "encoding/json" "log/slog" "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/shopspring/decimal" ) // PageTradeByAdmin 分页查询所有订单 func PageTradeByAdmin(c *fiber.Ctx) error { // 检查权限 _, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeTradeRead) if err != nil { return err } // 解析请求参数 req := new(PageTradeByAdminReq) if err := g.Validator.ParseBody(c, req); err != nil { return err } // 构建查询语句 do := q.Trade.Where() if req.UserPhone != nil { do = do.Where(q.User.As("User").Phone.Eq(*req.UserPhone)) } if req.InnerNo != nil { do = do.Where(q.Trade.InnerNo.Eq(*req.InnerNo)) } if req.OuterNo != nil { do = do.Where(q.Trade.OuterNo.Eq(*req.OuterNo)) } if req.Method != nil { do = do.Where(q.Trade.Method.Eq(*req.Method)) } if req.Platform != nil { do = do.Where(q.Trade.Platform.Eq(*req.Platform)) } if req.Status != nil { do = do.Where(q.Trade.Status.Eq(*req.Status)) } if req.CreatedAtStart != nil { do = do.Where(q.Trade.CreatedAt.Gte(req.CreatedAtStart.UTC())) } if req.CreatedAtEnd != nil { do = do.Where(q.Trade.CreatedAt.Lte(req.CreatedAtEnd.UTC())) } // 查询用户列表 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"), ). Where(do). Order(q.Trade.CreatedAt.Desc()). 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(), }) } type PageTradeByAdminReq struct { core.PageReq UserPhone *string `json:"user_phone,omitempty"` InnerNo *string `json:"inner_no,omitempty"` OuterNo *string `json:"outer_no,omitempty"` Method *int `json:"method,omitempty"` Platform *int `json:"platform,omitempty"` Status *int `json:"status,omitempty"` CreatedAtStart *time.Time `json:"created_at_start,omitempty"` CreatedAtEnd *time.Time `json:"created_at_end,omitempty"` } // PageTradeOfUserByAdmin 分页查询指定用户的订单 func PageTradeOfUserByAdmin(c *fiber.Ctx) error { // 检查权限 _, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeTradeReadOfUser) if err != nil { return err } // 解析请求参数 req := new(PageTradeOfUserByAdminReq) if err := g.Validator.ParseBody(c, req); err != nil { return err } // 构建查询语句 do := q.Trade.Where(q.Trade.UserID.Eq(req.UserID)) if req.InnerNo != nil { do = do.Where(q.Trade.InnerNo.Eq(*req.InnerNo)) } if req.OuterNo != nil { do = do.Where(q.Trade.OuterNo.Eq(*req.OuterNo)) } if req.Method != nil { do = do.Where(q.Trade.Method.Eq(*req.Method)) } if req.Platform != nil { do = do.Where(q.Trade.Platform.Eq(*req.Platform)) } if req.Status != nil { do = do.Where(q.Trade.Status.Eq(*req.Status)) } if req.CreatedAtStart != nil { do = do.Where(q.Trade.CreatedAt.Gte(req.CreatedAtStart.UTC())) } if req.CreatedAtEnd != nil { do = do.Where(q.Trade.CreatedAt.Lte(req.CreatedAtEnd.UTC())) } // 查询订单列表 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"), ). Where(do). Order(q.Trade.CreatedAt.Desc()). 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(), }) } type PageTradeOfUserByAdminReq struct { core.PageReq UserID int32 `json:"user_id" validate:"required"` InnerNo *string `json:"inner_no,omitempty"` OuterNo *string `json:"outer_no,omitempty"` Method *int `json:"method,omitempty"` Platform *int `json:"platform,omitempty"` Status *int `json:"status,omitempty"` CreatedAtStart *time.Time `json:"created_at_start,omitempty"` CreatedAtEnd *time.Time `json:"created_at_end,omitempty"` } // ============================================================ // 创建订单 func TradeCreate(c *fiber.Ctx) error { // 检查权限 authCtx, err := auth.GetAuthCtx(c).PermitUser() if err != nil { return err } if authCtx.User.IDType == m.UserIDTypeUnverified { return core.NewBizErr("请先实名认证后再购买") } // 解析请求参数 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("购买信息不能为空") } case m.TradeTypeRecharge: if req.Recharge == nil { return core.NewBizErr("充值信息不能为空") } } // 处理订单 var result *s.CreateTradeResult switch req.Type { case m.TradeTypePurchase: result, err = s.Trade.Create(authCtx.User, req.CreateTradeData, req.Resource) case m.TradeTypeRecharge: result, err = s.Trade.Create(authCtx.User, req.CreateTradeData, req.Recharge) } if err != nil { return core.NewServErr("处理购买产品信息失败", err) } return c.JSON(result) } type TradeCreateReq struct { *s.CreateTradeData Type m.TradeType `json:"type" validate:"required"` Resource *s.CreateResourceData `json:"resource,omitempty"` Recharge *s.UpdateBalanceData `json:"recharge,omitempty"` } // ============================================================ // 更新订单备注 func TradeUpdateRemarkByAdmin(c *fiber.Ctx) error { // 检查权限 _, err := auth.GetAuthCtx(c).PermitAdmin() if err != nil { return err } // 解析请求参数 var req TradeUpdateRemarkReq if err := g.Validator.ParseBody(c, &req); err != nil { return err } // 更新订单备注 err = s.Trade.UpdateRemark(req.TradeNo, req.Remark) if err != nil { return err } return c.SendStatus(fiber.StatusNoContent) } type TradeUpdateRemarkReq struct { TradeNo string `json:"trade_no" validate:"required"` Remark string `json:"remark"` } // ============================================================ // 完成订单 func TradeComplete(c *fiber.Ctx) error { // 检查权限 authCtx, err := auth.GetAuthCtx(c).PermitUser() if err != nil { return err } // 解析请求参数 var req s.TradeRef if err := g.Validator.ParseBody(c, &req); err != nil { return err } // 检查订单状态 err = s.Trade.CompleteTrade(authCtx.User, &req) if err != nil { return err } return c.SendStatus(fiber.StatusNoContent) } // 管理员完成订单 func TradeCompleteByAdmin(c *fiber.Ctx) error { // 检查权限 _, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeTradeWriteComplete) if err != nil { return err } // 解析请求参数 var req struct { s.TradeRef UserID int32 `json:"user_id" validate:"required"` } if err := g.Validator.ParseBody(c, &req); err != nil { return err } // 获取用户信息 user, err := s.User.Get(q.Q, req.UserID) if err != nil { return err } // 完成订单 err = s.Trade.CompleteTrade(user, &req.TradeRef) if err != nil { return err } return c.SendStatus(fiber.StatusNoContent) } // 订单补余额 func TradeConvertByAdmin(c *fiber.Ctx) error { // 检查权限 authCtx, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeTradeWrite) if err != nil { return err } // 解析请求参数 var req s.TradeRef if err := g.Validator.ParseBody(c, &req); err != nil { return err } // 订单补余额 err = s.Trade.ConvertTradeToBalance(authCtx.Admin, &req) if err != nil { return err } return c.SendStatus(fiber.StatusNoContent) } // ============================================================ // 取消订单 func TradeCancel(c *fiber.Ctx) error { // 检查权限 _, err := auth.GetAuthCtx(c).PermitUser() if err != nil { return err } // 解析请求参数 req := new(s.TradeRef) if err := g.Validator.ParseBody(c, req); err != nil { return err } // 取消交易 err = s.Trade.CancelTrade(req) if err != nil && err != s.ErrTradeStatusIgnored { slog.Error("取消交易失败", "trade_no", req.TradeNo, "error", err) return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "取消交易失败"}) } return c.SendStatus(fiber.StatusNoContent) } // 管理员取消订单 func TradeCancelByAdmin(c *fiber.Ctx) error { // 解析请求参数 req := new(s.TradeRef) if err := g.Validator.ParseBody(c, req); err != nil { return err } // 取消交易 err := s.Trade.CancelTrade(req) if err != nil && err != s.ErrTradeStatusIgnored { slog.Error("取消交易失败", "trade_no", req.TradeNo, "error", err) return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "取消交易失败"}) } return c.SendStatus(fiber.StatusNoContent) } // ============================================================ // 结束订单:完成或取消订单 func TradeFinish(c *fiber.Ctx) error { // 检查权限 authCtx, err := auth.GetAuthCtx(c).PermitUser() if err != nil { return err } // 解析请求参数 var req s.TradeRef if err := g.Validator.ParseBody(c, &req); err != nil { return err } // 尝试取消交易 err = s.Trade.CancelTrade(&req) if err == s.ErrTradeStatusIgnored { // 尝试完成交易 err = s.Trade.CompleteTrade(authCtx.User, &req) if err != nil { return err } return c.JSON(map[string]m.TradeStatus{ "status": m.TradeStatusSuccess, }) } else if err != nil { return err } return c.JSON(map[string]m.TradeStatus{ "status": m.TradeStatusCanceled, }) } // ============================================================ // 检查订单 func TradeCheck(c *fiber.Ctx) error { _, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeTradeRead) if err != nil { return err } // 解析请求参数 var req s.TradeRef if err := g.Validator.ParseBody(c, &req); err != nil { return err } // 检查订单状态 result, err := s.Trade.CheckTrade(&req) if err != nil { return err } return c.JSON(result) } // ============================================================ // 交易回调 func TradeCallbackSft(c *fiber.Ctx) error { // 解析请求参数 var req TradeCallbackSftReq if err := g.Validator.ParseBody(c, &req); err != nil { return core.NewServErr("解析请求失败", err) } var bizData TradeCallbackSftReqBizData if err := json.Unmarshal([]byte(*req.BizData), &bizData); err != nil { return core.NewServErr("解析业务数据失败", err) } // 检查通知 if bizData.MsgType != string(g.SftNotifyTypeTrade) { return core.NewBizErr("消息类型处理异常") } if bizData.State != string(g.SftTradeSuccess) { return core.NewBizErr("订单状态处理异常") } // 更新订单状态 if bizData.PayType == nil { return core.NewBizErr("支付渠道解析错误") } var acquirer m.TradeAcquirer switch *bizData.PayType { case string(g.SftAlipay): acquirer = m.TradeAcquirerAlipay case string(g.SftWeChat): acquirer = m.TradeAcquirerWechat case string(g.SftUnionPay): acquirer = m.TradeAcquirerUnionPay default: return core.NewBizErr("支付渠道类型异常") } var time, err = time.Parse("2006-01-02 15:04:05", *bizData.PayTime) if err != nil { return core.NewBizErr("时间解析错误") } err = s.Trade.CompleteTradeByNotify(bizData.MchOrderNo, bizData.PayOrderId, &s.TradeSuccessResult{ Acquirer: acquirer, Actual: decimal.NewFromInt(bizData.Amount).Div(decimal.NewFromInt(100)), Time: time, }) if err != nil { return core.NewServErr("完成交易失败", err) } return c.Send([]byte("SUCCESS")) } type TradeCallbackSftReq struct { Code string `json:"code"` Msg *string `json:"msg"` Sign *string `json:"sign"` BizData *string `json:"bizData"` SignType *string `json:"signType"` Timestamp string `json:"timestamp"` } type TradeCallbackSftReqBizData struct { MsgType string `json:"msgType"` PayOrderId string `json:"payOrderId"` State string `json:"state"` MercNo string `json:"mercNo"` StoreId string `json:"storeId"` MchOrderNo string `json:"mchOrderNo"` IfCode string `json:"ifCode"` Amount int64 `json:"amount"` Currency string `json:"currency"` Subject string `json:"subject"` Body string `json:"body"` ChannelTradeNo *string `json:"channelTradeNo"` ChannelSendNo *string `json:"channelSendNo"` UserId *string `json:"userId"` ExtParam *string `json:"extParam"` PayTime *string `json:"payTime"` Note *string `json:"note"` PayType *string `json:"payType"` DrType *string `json:"drType"` TradeFee *int64 `json:"tradeFee"` CashFee *int64 `json:"cashFee"` RefundAmt *int64 `json:"refundAmt"` RefundState *int32 `json:"refundState"` SettlementType *string `json:"settlementType"` }