Files
kami_gateway/internal/service/pay_service.go
danial e88ff05a14 refactor(trace): 重命名 otel 包为 otelTrace并更新相关引用
- 将内部使用的 otel 包重命名为 otelTrace
- 更新了所有引用该包的文件中的导入路径
- 修改了部分函数和变量名称以适应新的包名
2025-02-23 21:56:29 +08:00

426 lines
16 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 service
import (
"context"
"errors"
"fmt"
"gateway/internal/config"
"gateway/internal/otelTrace"
"gateway/internal/service/supplier"
"gateway/internal/models/merchant"
"gateway/internal/models/merchant_deploy"
"gateway/internal/models/order"
"gateway/internal/models/road"
"gateway/internal/schema/request"
"gateway/internal/schema/response"
"gateway/internal/utils"
"strconv"
"time"
"github.com/duke-git/lancet/v2/convertor"
"github.com/beego/beego/v2/core/logs"
"github.com/shopspring/decimal"
)
// ChooseRoadV2 选择通道通过productCode选择对应的通道
func ChooseRoadV2(ctx context.Context, c *response.PayBaseResp) *response.PayBaseResp {
orderPrice, err := strconv.ParseFloat(convertor.ToString(c.Params["orderPrice"]), 64)
if err != nil {
c.Code = -1
c.Msg = "输入金额错误!"
return c
}
roadInfo := road.GetRoadInfoByProductCode(ctx, convertor.ToString(c.Params["productCode"]))
if roadInfo.Id == 0 {
c.Code = -1
c.Msg = "未配置支付通道!"
return c
}
if msg, ok := RoadIsValid(ctx, roadInfo, c); !ok {
c.Code = -1
c.Msg = msg
return c
}
merchantUid := c.MerchantInfo.MerchantUid
// 通道配置信息
deployInfo := merchant_deploy.GetMerchantDeployByUidAndRoadUid(ctx, merchantUid, roadInfo.RoadUid)
if deployInfo.MerchantUid == "" {
c.Code = -1
c.Msg = "该商户没有配置通道信息"
return c
}
rate, err2 := deployInfo.GetSingleRoadPlatformRateByPrice(orderPrice)
if err2 != nil {
c.Code = -1
c.Msg = "当前通道面额存在问题"
return c
}
if !deployInfo.CheckSingleRoadPlatformRate(orderPrice) {
c.Code = -1
c.Msg = "当前面额不被允许"
return c
}
c.RoadInfo = roadInfo
c.PlatformRate = rate
c.AgentRate = deployInfo.SingleRoadAgentRate
c.RoadPoolInfo = road.GetRoadPoolByRoadPoolCode(ctx, deployInfo.RollRoadCode)
return c
}
// RoadIsValid 判断通道是否是合法的
func RoadIsValid(ctx context.Context, roadInfo road.RoadInfo, c *response.PayBaseResp) (string, bool) {
if roadInfo.RoadUid == "" || len(roadInfo.RoadUid) == 0 {
return "参数缺失", false
}
FORMAT := fmt.Sprintf("该通道:%s;", roadInfo.RoadName)
if roadInfo.Status != "active" {
logs.Notice(FORMAT + "不是激活状态")
return "通道未激活", false
}
hour := time.Now().Hour()
s := roadInfo.StarHour
e := roadInfo.EndHour
if hour < s || hour > e {
logs.Notice(FORMAT)
return "当前未处在交易区间内", false
}
if roadInfo.SingleMinLimit > c.OrderAmount || roadInfo.SingleMaxLimit < c.OrderAmount {
otelTrace.Logger.WithContext(ctx).Error(FORMAT + "订单金额超限制")
return "订单金额超过交易限制", false
}
todayLimit := roadInfo.TodayLimit
totalLimit := roadInfo.TotalLimit
todayIncome := roadInfo.TodayIncome
totalIncome := roadInfo.TotalIncome
if (todayIncome + c.OrderAmount) > todayLimit {
otelTrace.Logger.WithContext(ctx).Error(FORMAT + "达到了每天金额上限")
return "订单金额超过金额限制", false
}
if (totalIncome + c.OrderAmount) > totalLimit {
otelTrace.Logger.WithContext(ctx).Error(FORMAT + "达到了总量限制")
return "订单金额达到总量限制", false
}
// 如果通道被选中,那么总请求数+1
roadInfo.RequestAll = roadInfo.RequestAll + 1
roadInfo.TodayRequestAll = roadInfo.RequestAll + 1
roadInfo.UpdateTime = time.Now()
road.UpdateRoadInfo(ctx, roadInfo)
return "", true
}
// GenerateOrderInfo 获取基本订单记录
func GenerateOrderInfo(c *response.PayBaseResp) order.OrderInfo {
// 6666是自己系统订单号
// 获取支付类型的名称,例如支付宝扫码等
orderInfo := order.OrderInfo{
MerchantUid: c.MerchantInfo.MerchantUid,
MerchantName: c.MerchantInfo.MerchantName,
MerchantOrderId: convertor.ToString(c.Params["orderNo"]),
BankOrderId: "666" + utils.GenerateId(),
OrderAmount: c.OrderAmount,
FactAmount: c.OrderAmount,
ShowAmount: c.OrderAmount,
RollPoolCode: c.RoadPoolInfo.RoadPoolCode,
RollPoolName: c.RoadPoolInfo.RoadPoolName,
RoadUid: c.RoadInfo.RoadUid,
RoadName: c.RoadInfo.RoadName,
PayProductName: c.RoadInfo.ProductName,
ShopName: convertor.ToString(c.Params["productName"]),
Freeze: config.NO,
Refund: config.NO,
Unfreeze: config.NO,
Ip: c.ClientIP,
PayProductCode: c.RoadInfo.ProductUid,
Status: config.WAIT,
NotifyUrl: convertor.ToString(c.Params["notifyUrl"]),
OrderPeriod: convertor.ToString(c.Params["orderPeriod"]),
CreateTime: time.Now(),
UpdateTime: time.Now(),
ExValue: convertor.ToString(c.Params["exValue"]),
}
if c.MerchantInfo.BelongAgentUid != "" || c.AgentRate > config.ZERO {
orderInfo.AgentUid = c.MerchantInfo.BelongAgentUid
orderInfo.AgentName = c.MerchantInfo.BelongAgentName
}
return orderInfo
}
// CreateOrderInfo 创建订单
func CreateOrderInfo(createdOrder request.CreatedOrder, info merchant.MerchantInfo, roadPoolInfo road.RoadPoolInfo, roadInfo road.RoadInfo) (orderInfo order.OrderInfo, err error) {
// 6666是自己系统订单号
// 获取支付类型的名称,例如支付宝扫码等
orderInfo = order.OrderInfo{
MerchantUid: info.MerchantUid,
MerchantName: info.MerchantName,
MerchantOrderId: createdOrder.OrderNo,
BankOrderId: "666" + utils.GenerateId(),
OrderAmount: createdOrder.OrderPrice,
FactAmount: createdOrder.OrderPrice,
ShowAmount: createdOrder.OrderPrice,
RollPoolCode: roadPoolInfo.RoadPoolCode,
RollPoolName: roadPoolInfo.RoadPoolName,
RoadUid: roadInfo.RoadUid,
RoadName: roadInfo.RoadName,
PayProductName: roadInfo.ProductName,
Freeze: config.NO,
Refund: config.NO,
Unfreeze: config.NO,
PayProductCode: roadInfo.ProductUid,
Status: config.Created,
NotifyUrl: createdOrder.NotifyUrl,
OrderPeriod: strconv.Itoa(createdOrder.OrderPeriod),
CreateTime: time.Now(),
TransactionType: roadInfo.TransactionType,
}
return
}
// GenerateOrderProfit 计算收益,平台利润,代理利润
func GenerateOrderProfit(ctx context.Context, orderInfo order.OrderInfo, c *response.PayBaseResp) order.OrderProfitInfo {
// 因为所有的手续费率都是百分率所以需要除以100
supplierProfit := decimal.NewFromFloat(orderInfo.OrderAmount).
Div(decimal.NewFromInt(100)).
Mul(decimal.NewFromFloat(c.RoadInfo.BasicFee))
platformProfit := decimal.NewFromFloat(orderInfo.OrderAmount).
Div(decimal.NewFromInt(100)).
Mul(decimal.NewFromFloat(c.PlatformRate))
agentProfit := decimal.NewFromFloat(orderInfo.OrderAmount).
Div(decimal.NewFromInt(100)).
Mul(decimal.NewFromFloat(c.AgentRate))
// 如果用户没有设置代理那么代理利润为0.000
if c.MerchantInfo.BelongAgentUid == "" || len(c.MerchantInfo.BelongAgentUid) == 0 {
agentProfit = decimal.NewFromFloat(config.ZERO)
}
allProfit := supplierProfit.Add(platformProfit).Add(agentProfit)
if allProfit.GreaterThanOrEqual(decimal.NewFromFloat(orderInfo.OrderAmount)) {
otelTrace.Logger.WithContext(ctx).Error(fmt.Sprintf("手续费已经超过订单金额bankOrderId = %s", orderInfo.BankOrderId))
c.Msg = "手续费已经超过了订单金额"
c.Code = -1
}
orderProfit := order.OrderProfitInfo{
PayProductCode: c.RoadInfo.ProductUid,
PayProductName: c.RoadInfo.ProductName,
Status: config.WAIT,
MerchantOrderId: convertor.ToString(c.Params["orderNo"]),
BankOrderId: orderInfo.BankOrderId,
OrderAmount: c.OrderAmount,
FactAmount: c.OrderAmount,
ShowAmount: c.OrderAmount,
AllProfit: allProfit.InexactFloat64(),
UserInAmount: decimal.NewFromFloat(orderInfo.OrderAmount).Sub(allProfit).InexactFloat64(),
SupplierProfit: supplierProfit.InexactFloat64(),
PlatformProfit: platformProfit.InexactFloat64(),
AgentProfit: agentProfit.InexactFloat64(),
CreateTime: time.Now(),
UpdateTime: time.Now(),
MerchantUid: c.MerchantInfo.MerchantUid,
MerchantName: orderInfo.MerchantName,
SupplierRate: c.RoadInfo.BasicFee,
PlatformRate: c.PlatformRate,
AgentRate: c.AgentRate,
AgentName: orderInfo.AgentName,
AgentUid: orderInfo.AgentUid,
}
// 如果该条订单设置了代理利率,并且设置了代理
if c.MerchantInfo.BelongAgentUid != "" || c.AgentRate > config.ZERO {
orderProfit.AgentUid = c.MerchantInfo.BelongAgentUid
orderProfit.AgentName = c.MerchantInfo.BelongAgentName
}
return orderProfit
}
func CreateOrderProfitInfo(ctx context.Context, createdOrder request.CreatedOrder, merchantInfo merchant.MerchantInfo, merchantDeployInfo merchant_deploy.MerchantDeployInfo, orderInfo order.OrderInfo, roadInfo road.RoadInfo) (orderProfitInfo order.OrderProfitInfo, err error) {
// 因为所有的手续费率都是百分率所以需要除以100
platFormRate, err := merchantDeployInfo.GetSingleRoadPlatformRateByPrice(createdOrder.OrderPrice)
if err != nil {
platFormRate = 0.0
}
supplierProfit := decimal.NewFromFloat(orderInfo.OrderAmount).
Div(decimal.NewFromInt(100)).
Mul(decimal.NewFromFloat(roadInfo.BasicFee))
platformProfit := decimal.NewFromFloat(orderInfo.OrderAmount).
Div(decimal.NewFromInt(100)).
Mul(decimal.NewFromFloat(platFormRate))
agentProfit := decimal.NewFromFloat(orderInfo.OrderAmount).
Div(decimal.NewFromInt(100)).
Mul(decimal.NewFromFloat(merchantDeployInfo.SingleRoadAgentRate))
// 如果用户没有设置代理那么代理利润为0.000
if merchantInfo.BelongAgentUid == "" || len(merchantInfo.BelongAgentUid) == 0 {
agentProfit = decimal.NewFromFloat(config.ZERO)
}
allProfit := supplierProfit.Add(platformProfit).Add(agentProfit)
if allProfit.GreaterThanOrEqual(decimal.NewFromFloat(orderInfo.OrderAmount)) {
otelTrace.Logger.WithContext(ctx).Error(fmt.Sprintf("手续费已经超过订单金额bankOrderId = %s", orderInfo.BankOrderId))
err = errors.New("手续费已经超过订单金额")
return
}
orderProfitInfo = order.OrderProfitInfo{
PayProductCode: roadInfo.ProductUid,
PayProductName: roadInfo.ProductName,
Status: config.Created,
MerchantOrderId: createdOrder.OrderNo,
BankOrderId: orderInfo.BankOrderId,
OrderAmount: orderInfo.OrderAmount,
FactAmount: orderInfo.FactAmount,
ShowAmount: orderInfo.ShowAmount,
AllProfit: allProfit.InexactFloat64(),
UserInAmount: decimal.NewFromFloat(orderInfo.OrderAmount).Sub(allProfit).InexactFloat64(),
SupplierProfit: supplierProfit.InexactFloat64(),
PlatformProfit: platformProfit.InexactFloat64(),
AgentProfit: agentProfit.InexactFloat64(),
CreateTime: time.Now(),
UpdateTime: time.Now(),
MerchantUid: merchantInfo.MerchantUid,
MerchantName: orderInfo.MerchantName,
SupplierRate: roadInfo.BasicFee,
PlatformRate: platFormRate,
AgentRate: merchantDeployInfo.SingleRoadAgentRate,
AgentName: orderInfo.AgentName,
AgentUid: orderInfo.AgentUid,
}
return
}
// GenerateRecord 生成订单一系列的记录
func GenerateRecord(ctx context.Context, c *response.PayBaseResp) (order.OrderInfo, order.OrderProfitInfo, error) {
// 获取订单和订单利润表
orderInfo := order.GetOrderByMerchantOrderId(ctx, convertor.ToString(c.Params["orderNo"]))
orderProfitInfo := order.GetOrderProfitByMerchantOrderId(ctx, convertor.ToString(c.Params["orderNo"]))
if orderProfitInfo.Id == 0 || orderInfo.MerchantOrderId == "" {
//生成订单记录,订单利润利润
orderInfo = GenerateOrderInfo(c)
orderProfit := GenerateOrderProfit(ctx, orderInfo, c)
if c.Code == -1 {
return orderInfo, orderProfit, errors.New("订单数据插入失败")
}
//插入订单记录和订单利润记录
if !order.InsertOrderAndOrderProfit(ctx, orderInfo, orderProfit) {
c.Code = -1
return orderInfo, orderProfit, errors.New("订单数据插入失败")
}
orderInfo = order.GetOrderByMerchantOrderId(ctx, convertor.ToString(c.Params["orderNo"]))
orderProfitInfo = order.GetOrderProfitByBankOrderId(ctx, orderProfitInfo.BankOrderId)
otelTrace.Logger.WithContext(ctx).Info("插入支付订单记录和支付利润记录成功")
return orderInfo, orderProfit, nil
}
if orderInfo.Status != config.Created {
err := errors.New("当前订单已存在,请等待处理结果或手动查询")
return orderInfo, orderProfitInfo, err
}
if !order.InsertOrderExValue(convertor.ToString(c.Params["orderNo"]), convertor.ToString(c.Params["exValue"])) {
err := errors.New("订单数据插入失败")
return orderInfo, orderProfitInfo, err
}
if !order.InsertClientIP(convertor.ToString(c.Params["orderNo"]), c.ClientIP) {
err := errors.New("订单数据插入失败")
return orderInfo, orderProfitInfo, err
}
if !order.InsertPayTime(convertor.ToString(c.Params["orderNo"])) {
err := errors.New("订单数据插入失败")
return orderInfo, orderProfitInfo, err
}
if !order.SwitchOrderAndOrderProfitStatus(ctx, convertor.ToString(c.Params["orderNo"]), config.WAIT) {
err := errors.New("订单状态转换失败")
return orderInfo, orderProfitInfo, err
}
orderInfo = order.GetOrderByMerchantOrderId(ctx, convertor.ToString(c.Params["orderNo"]))
orderProfitInfo = order.GetOrderProfitByBankOrderId(ctx, orderProfitInfo.BankOrderId)
otelTrace.Logger.WithContext(ctx).Info("插入支付订单记录和支付利润记录成功")
return orderInfo, orderProfitInfo, nil
}
func GenerateSuccessData(scanData supplier.ScanData, c *response.PayBaseResp) *response.ScanSuccessData {
params := map[string]any{
"statusCode": "00",
"orderNo": scanData.BankNo,
"orderPrice": scanData.OrderPrice,
}
sign := utils.GetMD5SignMF(params, c.MerchantInfo.MerchantSecret)
scanSuccessData := &response.ScanSuccessData{
OrderNo: scanData.OrderNo,
Sign: sign,
OrderPrice: scanData.OrderPrice,
StatusCode: "00",
Msg: "请求成功",
Code: 0,
PayUrl: scanData.PayUrl,
}
if scanSuccessData.StatusCode == "00" {
scanSuccessData.Code = 0
} else {
scanSuccessData.Code = -1
}
return scanSuccessData
}
func CreateOrderInfoAndOrderProfitInfo(ctx context.Context, createdOrder request.CreatedOrder, info merchant.MerchantInfo) (orderInfo order.OrderInfo, err error) {
roadInfo := road.GetRoadInfoByProductCode(ctx, createdOrder.ProductCode)
if roadInfo.Id == 0 {
err = errors.New("未配置支付方式")
return
}
if msg, ok := RoadIsValid(ctx, roadInfo, &response.PayBaseResp{
OrderAmount: createdOrder.OrderPrice,
}); !ok {
err = errors.New(msg)
return
}
merchantDeploy := merchant_deploy.GetMerchantDeployByUidAndRoadUid(
ctx,
info.MerchantUid,
roadInfo.RoadUid,
)
if merchantDeploy.Id == 0 {
err = errors.New("当前用户未配置支付方式")
return
}
roadPoolInfo := road.GetRoadPoolByRoadPoolCode(ctx, merchantDeploy.RollRoadCode)
if roadPoolInfo.Id == 0 {
err = errors.New("当前用户未配置支付方式")
return
}
orderInfo, err = CreateOrderInfo(createdOrder, info, roadPoolInfo, roadInfo)
if err != nil {
err = errors.Join(err, errors.New("创建订单失败"))
return
}
orderProfitInfo, err := CreateOrderProfitInfo(ctx, createdOrder, info, *merchantDeploy, orderInfo, roadInfo)
if err != nil {
err = errors.Join(err, errors.New("创建订单失败"))
return
}
if !order.InsertOrderAndOrderProfit(ctx, orderInfo, orderProfitInfo) {
err = errors.Join(err, errors.New("创建订单失败"))
return
}
return
}
func GerMerchantDeployInfoByUidAndProductCode(ctx context.Context, uid, productCode string) merchant_deploy.MerchantDeployInfo {
roadInfo := road.GetRoadInfoByProductCode(ctx, productCode)
merchantDeploy := merchant_deploy.GetMerchantDeployByUidAndRoadUid(ctx, uid, roadInfo.RoadUid)
return *merchantDeploy
}