366 lines
8.6 KiB
Go
366 lines
8.6 KiB
Go
package handlers
|
||
|
||
import (
|
||
"bufio"
|
||
"fmt"
|
||
"log/slog"
|
||
"platform/pkg/env"
|
||
"platform/pkg/u"
|
||
"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(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 {
|
||
time := u.DateHead(*req.CreatedAtStart)
|
||
do = do.Where(q.Trade.CreatedAt.Gte(time))
|
||
}
|
||
if req.CreatedAtEnd != nil {
|
||
time := u.DateTail(*req.CreatedAtEnd)
|
||
do = do.Where(q.Trade.CreatedAt.Lte(time))
|
||
}
|
||
|
||
// 查询用户列表
|
||
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 {
|
||
time := u.DateHead(*req.CreatedAtStart)
|
||
do = do.Where(q.Trade.CreatedAt.Gte(time))
|
||
}
|
||
if req.CreatedAtEnd != nil {
|
||
time := u.DateTail(*req.CreatedAtEnd)
|
||
do = do.Where(q.Trade.CreatedAt.Lte(time))
|
||
}
|
||
|
||
// 查询订单列表
|
||
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
|
||
}
|
||
|
||
// 解析请求参数
|
||
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 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 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.TradeRef)
|
||
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.TradeRef
|
||
}
|
||
|
||
// ============================================================
|
||
|
||
// 检查订单
|
||
func TradeCheck(c *fiber.Ctx) error {
|
||
// 检查权限:sse 接口暂时不检查权限
|
||
|
||
// 解析请求参数
|
||
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.TradeRef)
|
||
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.TradeRef
|
||
}
|