2025-04-17 18:29:44 +08:00
|
|
|
package handlers
|
|
|
|
|
|
|
|
|
|
import (
|
2026-07-10 17:11:39 +08:00
|
|
|
"encoding/json"
|
2025-04-18 16:22:38 +08:00
|
|
|
"log/slog"
|
2025-06-23 18:32:20 +08:00
|
|
|
"platform/web/auth"
|
2025-06-26 09:28:42 +08:00
|
|
|
"platform/web/core"
|
2025-04-17 18:29:44 +08:00
|
|
|
g "platform/web/globals"
|
2025-11-24 18:44:06 +08:00
|
|
|
m "platform/web/models"
|
2025-12-30 19:24:03 +08:00
|
|
|
q "platform/web/queries"
|
2025-04-17 18:29:44 +08:00
|
|
|
s "platform/web/services"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"github.com/gofiber/fiber/v2"
|
2026-07-10 17:11:39 +08:00
|
|
|
"github.com/shopspring/decimal"
|
2025-04-17 18:29:44 +08:00
|
|
|
)
|
|
|
|
|
|
2025-12-30 19:24:03 +08:00
|
|
|
// PageTradeByAdmin 分页查询所有订单
|
|
|
|
|
func PageTradeByAdmin(c *fiber.Ctx) error {
|
|
|
|
|
// 检查权限
|
2026-03-28 14:18:11 +08:00
|
|
|
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeTradeRead)
|
2025-12-30 19:24:03 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2025-06-03 09:53:50 +08:00
|
|
|
|
2025-12-30 19:24:03 +08:00
|
|
|
// 解析请求参数
|
2026-03-23 14:26:10 +08:00
|
|
|
req := new(PageTradeByAdminReq)
|
2025-12-30 19:24:03 +08:00
|
|
|
if err := g.Validator.ParseBody(c, req); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-23 14:26:10 +08:00
|
|
|
// 构建查询语句
|
|
|
|
|
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 {
|
2026-05-19 14:50:28 +08:00
|
|
|
do = do.Where(q.Trade.CreatedAt.Gte(req.CreatedAtStart.UTC()))
|
2026-03-23 14:26:10 +08:00
|
|
|
}
|
|
|
|
|
if req.CreatedAtEnd != nil {
|
2026-05-19 14:50:28 +08:00
|
|
|
do = do.Where(q.Trade.CreatedAt.Lte(req.CreatedAtEnd.UTC()))
|
2026-03-23 14:26:10 +08:00
|
|
|
}
|
|
|
|
|
|
2025-12-30 19:24:03 +08:00
|
|
|
// 查询用户列表
|
2026-03-20 14:37:41 +08:00
|
|
|
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"),
|
|
|
|
|
).
|
2026-03-23 14:26:10 +08:00
|
|
|
Where(do).
|
2026-03-30 14:59:35 +08:00
|
|
|
Order(q.Trade.CreatedAt.Desc()).
|
2026-03-20 14:37:41 +08:00
|
|
|
FindByPage(req.GetOffset(), req.GetLimit())
|
2025-12-30 19:24:03 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 返回结果
|
|
|
|
|
return c.JSON(core.PageResp{
|
|
|
|
|
List: list,
|
|
|
|
|
Total: int(total),
|
|
|
|
|
Page: req.GetPage(),
|
|
|
|
|
Size: req.GetSize(),
|
|
|
|
|
})
|
2025-06-23 18:32:20 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-23 14:26:10 +08:00
|
|
|
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"`
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-08 13:38:00 +08:00
|
|
|
// 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 {
|
2026-05-19 14:50:28 +08:00
|
|
|
do = do.Where(q.Trade.CreatedAt.Gte(req.CreatedAtStart.UTC()))
|
2026-04-08 13:38:00 +08:00
|
|
|
}
|
|
|
|
|
if req.CreatedAtEnd != nil {
|
2026-05-19 14:50:28 +08:00
|
|
|
do = do.Where(q.Trade.CreatedAt.Lte(req.CreatedAtEnd.UTC()))
|
2026-04-08 13:38:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 查询订单列表
|
|
|
|
|
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"`
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-11 14:10:44 +08:00
|
|
|
// ============================================================
|
|
|
|
|
|
2025-12-30 19:24:03 +08:00
|
|
|
// 创建订单
|
2025-06-26 09:28:42 +08:00
|
|
|
func TradeCreate(c *fiber.Ctx) error {
|
2025-06-23 18:32:20 +08:00
|
|
|
// 检查权限
|
2025-11-17 18:38:10 +08:00
|
|
|
authCtx, err := auth.GetAuthCtx(c).PermitUser()
|
2025-06-23 18:32:20 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2026-04-20 11:20:44 +08:00
|
|
|
if authCtx.User.IDType == m.UserIDTypeUnverified {
|
|
|
|
|
return core.NewBizErr("请先实名认证后再购买")
|
|
|
|
|
}
|
2025-06-03 09:53:50 +08:00
|
|
|
|
2025-06-23 18:32:20 +08:00
|
|
|
// 解析请求参数
|
2025-06-26 09:28:42 +08:00
|
|
|
req := new(TradeCreateReq)
|
2025-12-04 09:45:35 +08:00
|
|
|
if err := g.Validator.ParseBody(c, req); err != nil {
|
2025-06-23 18:32:20 +08:00
|
|
|
return err
|
|
|
|
|
}
|
2025-06-26 09:28:42 +08:00
|
|
|
switch req.Type {
|
2025-11-24 18:44:06 +08:00
|
|
|
case m.TradeTypePurchase:
|
2025-06-26 09:28:42 +08:00
|
|
|
if req.Resource == nil {
|
|
|
|
|
return core.NewBizErr("购买信息不能为空")
|
|
|
|
|
}
|
2025-11-24 18:44:06 +08:00
|
|
|
case m.TradeTypeRecharge:
|
2025-06-26 09:28:42 +08:00
|
|
|
if req.Recharge == nil {
|
|
|
|
|
return core.NewBizErr("充值信息不能为空")
|
2025-06-24 11:36:27 +08:00
|
|
|
}
|
2025-06-23 18:32:20 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-26 14:39:19 +08:00
|
|
|
// 处理订单
|
2026-03-30 14:59:35 +08:00
|
|
|
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)
|
|
|
|
|
}
|
2025-06-26 09:28:42 +08:00
|
|
|
if err != nil {
|
2026-03-26 14:39:19 +08:00
|
|
|
return core.NewServErr("处理购买产品信息失败", err)
|
2025-06-26 09:28:42 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-26 14:39:19 +08:00
|
|
|
return c.JSON(result)
|
2025-06-23 18:32:20 +08:00
|
|
|
}
|
2025-04-17 18:29:44 +08:00
|
|
|
|
2025-12-30 19:24:03 +08:00
|
|
|
type TradeCreateReq struct {
|
2026-03-26 14:39:19 +08:00
|
|
|
*s.CreateTradeData
|
|
|
|
|
Type m.TradeType `json:"type" validate:"required"`
|
|
|
|
|
Resource *s.CreateResourceData `json:"resource,omitempty"`
|
|
|
|
|
Recharge *s.UpdateBalanceData `json:"recharge,omitempty"`
|
2025-12-30 19:24:03 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-11 14:10:44 +08:00
|
|
|
// ============================================================
|
|
|
|
|
|
2026-06-18 12:15:30 +08:00
|
|
|
// 更新订单备注
|
|
|
|
|
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"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
2025-12-30 19:24:03 +08:00
|
|
|
// 完成订单
|
2025-06-26 09:28:42 +08:00
|
|
|
func TradeComplete(c *fiber.Ctx) error {
|
|
|
|
|
// 检查权限
|
2026-03-26 14:39:19 +08:00
|
|
|
authCtx, err := auth.GetAuthCtx(c).PermitUser()
|
2025-06-26 09:28:42 +08:00
|
|
|
if err != nil {
|
2025-04-18 16:22:38 +08:00
|
|
|
return err
|
|
|
|
|
}
|
2025-06-26 09:28:42 +08:00
|
|
|
|
|
|
|
|
// 解析请求参数
|
2026-04-11 14:10:44 +08:00
|
|
|
var req s.TradeRef
|
|
|
|
|
if err := g.Validator.ParseBody(c, &req); err != nil {
|
2025-04-18 16:22:38 +08:00
|
|
|
return err
|
|
|
|
|
}
|
2025-06-26 09:28:42 +08:00
|
|
|
|
|
|
|
|
// 检查订单状态
|
2026-04-11 14:10:44 +08:00
|
|
|
err = s.Trade.CompleteTrade(authCtx.User, &req)
|
2025-04-18 16:22:38 +08:00
|
|
|
if err != nil {
|
2025-08-15 18:11:35 +08:00
|
|
|
return err
|
2025-04-18 16:22:38 +08:00
|
|
|
}
|
2025-04-17 18:29:44 +08:00
|
|
|
|
2025-06-26 09:28:42 +08:00
|
|
|
return c.SendStatus(fiber.StatusNoContent)
|
|
|
|
|
}
|
2025-04-18 16:22:38 +08:00
|
|
|
|
2026-04-11 14:10:44 +08:00
|
|
|
// 管理员完成订单
|
|
|
|
|
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)
|
2025-06-26 09:28:42 +08:00
|
|
|
}
|
2025-04-18 16:22:38 +08:00
|
|
|
|
2026-06-18 12:15:30 +08:00
|
|
|
// 订单补余额
|
|
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-11 14:10:44 +08:00
|
|
|
// ============================================================
|
|
|
|
|
|
2025-12-30 19:24:03 +08:00
|
|
|
// 取消订单
|
2025-08-15 18:11:35 +08:00
|
|
|
func TradeCancel(c *fiber.Ctx) error {
|
|
|
|
|
// 检查权限
|
2025-11-17 18:38:10 +08:00
|
|
|
_, err := auth.GetAuthCtx(c).PermitUser()
|
2025-08-15 18:11:35 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2025-07-01 18:23:15 +08:00
|
|
|
|
2025-08-15 18:11:35 +08:00
|
|
|
// 解析请求参数
|
2026-06-18 12:15:30 +08:00
|
|
|
req := new(s.TradeRef)
|
2025-12-04 09:45:35 +08:00
|
|
|
if err := g.Validator.ParseBody(c, req); err != nil {
|
2025-08-15 18:11:35 +08:00
|
|
|
return err
|
|
|
|
|
}
|
2025-04-17 18:29:44 +08:00
|
|
|
|
2025-08-15 18:11:35 +08:00
|
|
|
// 取消交易
|
2026-06-18 12:15:30 +08:00
|
|
|
err = s.Trade.CancelTrade(req)
|
|
|
|
|
if err != nil && err != s.ErrTradeStatusIgnored {
|
2025-08-15 18:11:35 +08:00
|
|
|
slog.Error("取消交易失败", "trade_no", req.TradeNo, "error", err)
|
|
|
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "取消交易失败"})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return c.SendStatus(fiber.StatusNoContent)
|
|
|
|
|
}
|
2025-12-04 09:45:35 +08:00
|
|
|
|
2026-06-26 15:30:30 +08:00
|
|
|
// 管理员取消订单
|
|
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-18 12:15:30 +08:00
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
|
|
// 结束订单:完成或取消订单
|
|
|
|
|
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,
|
|
|
|
|
})
|
2025-12-04 09:45:35 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-11 14:10:44 +08:00
|
|
|
// ============================================================
|
|
|
|
|
|
2025-12-30 19:24:03 +08:00
|
|
|
// 检查订单
|
2025-12-04 09:45:35 +08:00
|
|
|
func TradeCheck(c *fiber.Ctx) error {
|
2026-06-18 12:15:30 +08:00
|
|
|
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeTradeRead)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2026-03-28 14:18:11 +08:00
|
|
|
|
2025-12-04 09:45:35 +08:00
|
|
|
// 解析请求参数
|
2026-06-18 12:15:30 +08:00
|
|
|
var req s.TradeRef
|
|
|
|
|
if err := g.Validator.ParseBody(c, &req); err != nil {
|
2025-12-04 09:45:35 +08:00
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-18 12:15:30 +08:00
|
|
|
// 检查订单状态
|
|
|
|
|
result, err := s.Trade.CheckTrade(&req)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2025-12-30 19:24:03 +08:00
|
|
|
|
2026-06-18 12:15:30 +08:00
|
|
|
return c.JSON(result)
|
2025-12-30 19:24:03 +08:00
|
|
|
}
|
2026-07-10 17:11:39 +08:00
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
|
|
// 交易回调
|
|
|
|
|
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"`
|
|
|
|
|
}
|