one-api/controller/order.go
2024-06-05 13:30:12 +08:00

224 lines
5.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package controller
import (
"errors"
"fmt"
"net/http"
"sync"
"one-api/common"
"one-api/common/config"
"one-api/common/logger"
"one-api/common/utils"
"one-api/model"
"one-api/payment"
"one-api/payment/types"
"github.com/gin-gonic/gin"
)
type OrderRequest struct {
UUID string `json:"uuid" binding:"required"`
Amount int `json:"amount" binding:"required"`
}
type OrderResponse struct {
TradeNo string `json:"trade_no"`
*types.PayRequest
}
// CreateOrder
func CreateOrder(c *gin.Context) {
var orderReq OrderRequest
if err := c.ShouldBindJSON(&orderReq); err != nil {
common.APIRespondWithError(c, http.StatusOK, errors.New("invalid request"))
return
}
if orderReq.Amount <= 0 || orderReq.Amount < config.PaymentMinAmount {
common.APIRespondWithError(c, http.StatusOK, fmt.Errorf("金额必须大于等于 %d", config.PaymentMinAmount))
return
}
userId := c.GetInt("id")
// 关闭用户未完成的订单
go model.CloseUnfinishedOrder()
paymentService, err := payment.NewPaymentService(orderReq.UUID)
if err != nil {
common.APIRespondWithError(c, http.StatusOK, err)
return
}
// 获取手续费和支付金额
fee, payMoney := calculateOrderAmount(paymentService.Payment, orderReq.Amount)
// 开始支付
tradeNo := utils.GenerateTradeNo()
payRequest, err := paymentService.Pay(tradeNo, payMoney)
if err != nil {
common.APIRespondWithError(c, http.StatusOK, errors.New("创建支付失败,请稍后再试"))
return
}
// 创建订单
order := &model.Order{
UserId: userId,
TradeNo: tradeNo,
Amount: orderReq.Amount,
OrderAmount: payMoney,
OrderCurrency: paymentService.Payment.Currency,
Fee: fee,
Status: model.OrderStatusPending,
Quota: orderReq.Amount * int(config.QuotaPerUnit),
}
err = order.Insert()
if err != nil {
common.APIRespondWithError(c, http.StatusOK, errors.New("创建订单失败,请稍后再试"))
return
}
orderResp := &OrderResponse{
TradeNo: tradeNo,
PayRequest: payRequest,
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "",
"data": orderResp,
})
}
// tradeNo lock
var orderLocks sync.Map
var createLock sync.Mutex
// LockOrder 尝试对给定订单号加锁
func LockOrder(tradeNo string) {
lock, ok := orderLocks.Load(tradeNo)
if !ok {
createLock.Lock()
defer createLock.Unlock()
lock, ok = orderLocks.Load(tradeNo)
if !ok {
lock = new(sync.Mutex)
orderLocks.Store(tradeNo, lock)
}
}
lock.(*sync.Mutex).Lock()
}
// UnlockOrder 释放给定订单号的锁
func UnlockOrder(tradeNo string) {
lock, ok := orderLocks.Load(tradeNo)
if ok {
lock.(*sync.Mutex).Unlock()
}
}
func PaymentCallback(c *gin.Context) {
uuid := c.Param("uuid")
paymentService, err := payment.NewPaymentService(uuid)
if err != nil {
common.APIRespondWithError(c, http.StatusOK, errors.New("payment not found"))
return
}
payNotify, err := paymentService.HandleCallback(c, paymentService.Payment.Config)
if err != nil {
return
}
LockOrder(payNotify.GatewayNo)
defer UnlockOrder(payNotify.GatewayNo)
order, err := model.GetOrderByTradeNo(payNotify.TradeNo)
if err != nil {
logger.SysError(fmt.Sprintf("gateway callback failed to find order, trade_no: %s,", payNotify.TradeNo))
return
}
fmt.Println(order.Status, order.Status != model.OrderStatusPending)
if order.Status != model.OrderStatusPending {
return
}
order.GatewayNo = payNotify.GatewayNo
order.Status = model.OrderStatusSuccess
err = order.Update()
if err != nil {
logger.SysError(fmt.Sprintf("gateway callback failed to update order, trade_no: %s,", payNotify.TradeNo))
return
}
err = model.IncreaseUserQuota(order.UserId, order.Quota)
if err != nil {
logger.SysError(fmt.Sprintf("gateway callback failed to increase user quota, trade_no: %s,", payNotify.TradeNo))
return
}
model.RecordLog(order.UserId, model.LogTypeTopup, fmt.Sprintf("在线充值成功充值quota: %d支付金额%.2f %s", order.Quota, order.OrderAmount, order.OrderCurrency))
}
func CheckOrderStatus(c *gin.Context) {
tradeNo := c.Query("trade_no")
userId := c.GetInt("id")
success := false
if tradeNo != "" {
order, err := model.GetUserOrder(userId, tradeNo)
if err == nil {
if order.Status == model.OrderStatusSuccess {
success = true
}
}
}
c.JSON(http.StatusOK, gin.H{
"success": success,
"message": "",
})
}
func calculateOrderAmount(payment *model.Payment, amount int) (fee, payMoney float64) {
if payment.PercentFee > 0 {
fee = utils.Decimal(float64(amount)*payment.PercentFee, 2)
} else if payment.FixedFee > 0 {
fee = payment.FixedFee
}
total := utils.Decimal(float64(amount)+fee, 2)
if payment.Currency == model.CurrencyTypeUSD {
payMoney = total
} else {
payMoney = utils.Decimal(total*config.PaymentUSDRate, 2)
}
return
}
func GetOrderList(c *gin.Context) {
var params model.SearchOrderParams
if err := c.ShouldBindQuery(&params); err != nil {
common.APIRespondWithError(c, http.StatusOK, err)
return
}
payments, err := model.GetOrderList(&params)
if err != nil {
common.APIRespondWithError(c, http.StatusOK, err)
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "",
"data": payments,
})
}