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

487 lines
19 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 gateway
import (
"context"
"encoding/json"
"errors"
"fmt"
"gateway/internal/config"
"gateway/internal/consts"
"gateway/internal/dto"
"gateway/internal/models/merchant"
"gateway/internal/models/merchant_deploy"
"gateway/internal/models/order"
"gateway/internal/models/road"
"gateway/internal/otelTrace"
"gateway/internal/schema/request"
"gateway/internal/schema/response"
"gateway/internal/service"
"gateway/internal/service/backend"
"gateway/internal/service/supplier"
"gateway/internal/service/supplier/t_mall_game"
"gateway/internal/service/supplier/third_party"
"gateway/internal/utils"
"strconv"
"strings"
"time"
"github.com/allegro/bigcache/v3"
"github.com/beego/beego/v2/core/validation"
"github.com/bytedance/gopkg/util/gopool"
"github.com/duke-git/lancet/v2/convertor"
"github.com/duke-git/lancet/v2/random"
"github.com/duke-git/lancet/v2/structs"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
"go.uber.org/zap"
)
var (
delayPool = gopool.NewPool("delayHandler", 20, gopool.NewConfig())
idCache, _ = bigcache.New(otelTrace.InitCtx, bigcache.DefaultConfig(10*time.Second))
)
type ScanController struct {
BaseGateway
}
// Scan 处理扫码的请求
func (c *ScanController) Scan() {
// 获取所有请求参数
// 获取客户端的ip
p := service.GetMerchantInfo(c.Ctx.Request.Context(), map[string]any{
"exValue": strings.TrimSpace(c.GetString("exValue")),
"orderNo": strings.TrimSpace(c.GetString("orderNo")),
"orderPeriod": strings.TrimSpace(c.GetString("orderPeriod")),
"productCode": strings.TrimSpace(c.GetString("productCode")),
"orderPrice": strings.TrimSpace(c.GetString("orderPrice")),
"notifyUrl": strings.TrimSpace(c.GetString("notifyUrl")),
"payKey": strings.TrimSpace(c.GetString("payKey")),
"timestamp": strings.TrimSpace(c.GetString("timestamp")),
"sign": strings.TrimSpace(c.GetString("sign")),
"ip": strings.TrimSpace(c.GetString("ip")),
"deviceId": strings.TrimSpace(c.GetString("deviceId")),
})
ctx, cancel := otelTrace.Span(c.Ctx.Request.Context(), "页面订单", "ScanController", trace.WithAttributes(
attribute.String("exValue", p.Params["exValue"].(string)),
attribute.String("orderNo", p.Params["orderNo"].(string)),
attribute.String("productCode", p.Params["productCode"].(string)),
attribute.String("sign", p.Params["sign"].(string)),
))
defer cancel()
c.Ctx.Request = c.Ctx.Request.WithContext(ctx)
p.ClientIP = strings.TrimSpace(c.GetString("ip"))
cacheId, _ := idCache.Get(strings.TrimSpace(c.GetString("orderNo")))
if len(cacheId) != 0 {
otelTrace.Logger.WithContext(c.Ctx.Request.Context()).Info(fmt.Sprintf("订单已经提交订单信息id=%s", p.Params["orderNo"]), zap.Any("orderInfo", p.Params))
p.Msg = "订单已经提交!"
p.Code = -1
c.SolveFailJSON(p)
return
}
_ = idCache.Set(strings.TrimSpace(c.GetString("orderNo")), []byte("1"))
p = service.JudgeParams(c.Ctx.Request.Context(), p)
p = service.OrderIsValid(c.Ctx.Request.Context(), p)
p = service.NotifyUrlIsValid(c.Ctx.Request.Context(), p)
p = service.OrderPeriodIsValid(c.Ctx.Request.Context(), p)
p = service.OrderPriceIsValid(c.Ctx.Request.Context(), p)
p = service.ExValueIsValid(c.Ctx.Request.Context(), p)
if p.Code == -1 {
c.SolveFailJSON(p)
return
}
otelTrace.Logger.WithContext(c.Ctx.Request.Context()).Info("【BaseGateway】获取商户请求过来的参数", zap.Any("params", p.Params))
// 签名验证
if !utils.Md5MFVerify(c.Ctx.Request.Context(), p.Params, p.MerchantInfo.MerchantSecret) &&
!utils.Md5TMPMFVerify(c.Ctx.Request.Context(), p.Params, p.MerchantInfo.MerchantSecret) &&
!utils.Md5Verify(p.Params, p.MerchantInfo.MerchantSecret) {
p.Code = -1
p.Msg = "签名异常"
c.SolveFailJSON(p)
return
}
p = service.ChooseRoadV2(c.Ctx.Request.Context(), p)
if p.Code == -1 {
c.SolveFailJSON(p)
return
}
mt := merchant_deploy.GetMerchantDeployByUidAndRoadUid(c.Ctx.Request.Context(), p.MerchantInfo.MerchantUid, p.RoadInfo.RoadUid)
if mt.Id == 0 {
p.Msg = "当前用户没有开通该通道"
c.SolveFailJSON(p)
return
}
orderPrice, err := strconv.ParseFloat(convertor.ToString(p.Params["orderPrice"]), 64)
if err != nil {
p.Code = -1
p.Msg = fmt.Sprintf("订单金额转换失败:%v", err.Error())
c.SolveFailJSON(p)
return
}
pm, err := mt.GetShowMMValue(c.Ctx.Request.Context(), orderPrice)
if err != nil {
p.Code = -1
p.Msg = fmt.Sprintf("获取展示比例失败:%v", err.Error())
c.SolveFailJSON(p)
return
}
p.Params["exValue"], err = service.CompleteRedeemExValue(convertor.ToString(p.Params["exValue"]), strconv.FormatFloat(pm.ShowLabel, 'f', 0, 64))
if err != nil {
p.Code = -1
p.Msg = fmt.Sprintf("订单金额转换失败:%v", err.Error())
c.SolveFailJSON(p)
return
}
// TODO: 生成订单记录
orderInfo, _, err := service.GenerateRecord(c.Ctx.Request.Context(), p)
if err != nil {
p.Msg = fmt.Sprintf("生成订单失败:%v", err.Error())
c.SolveFailJSON(p)
return
}
if p.Code == -1 {
c.SolveFailJSON(p)
return
}
hiddenCfg := service.GetOrderHidden(c.Ctx.Request.Context(), &orderInfo)
if hiddenCfg != nil {
hiddenCfg.Strategy = int(random.RandFromGivenSlice([]consts.StealRuleType{
consts.StealRuleTypeStealDelay,
consts.StealRuleTypeStealBlank,
}))
//延迟时间,两个数字之间的随机数
delayDuration := random.RandInt(hiddenCfg.DelayDurationMin, hiddenCfg.DelayDurationMax)
//1. 新的空白记录
if hiddenCfg.Strategy == int(consts.StealRuleTypeStealBlank) {
newBankOrderId, err2 := service.CreateHiddenBlankOrder(c.Ctx.Request.Context(), &orderInfo, int64(delayDuration))
if err2 != nil {
otelTrace.Logger.WithContext(c.Ctx.Request.Context()).Error("添加订单关联失败【偷卡1】", zap.Error(err2))
}
//添加订单关联
if err2 = service.CreateRelateHideOrderRecord(newBankOrderId, &orderInfo, hiddenCfg, delayDuration); err2 != nil {
otelTrace.Logger.WithContext(c.Ctx.Request.Context()).Error("添加订单关联失败【偷卡2】", zap.Error(err2))
}
//错误订单回调上游
//oldBankOrderId := orderInfo.BankOrderId
//delayPool.Go(func() {
// time.Sleep(time.Duration(delayDuration) * time.Second)
// service.SolvePayFail(oldBankOrderId, orderInfo.BankTransId, hiddenCfg.ExtraReturnInfo)
//})
orderInfo.BankOrderId = newBankOrderId
}
//2.新的错误记录
if hiddenCfg.Strategy == int(consts.StealRuleTypeStealDelay) {
newBankOrderId, err2 := service.CreateHiddenErrorOrder(c.Ctx.Request.Context(), &orderInfo, int64(delayDuration))
if err2 != nil {
otelTrace.Logger.WithContext(c.Ctx.Request.Context()).Error("添加订单关联失败【偷卡1】", zap.Error(err2))
}
//添加订单关联
if err2 = service.CreateRelateHideOrderRecord(newBankOrderId, &orderInfo, hiddenCfg, delayDuration); err2 != nil {
otelTrace.Logger.WithContext(c.Ctx.Request.Context()).Error("添加订单关联失败【偷卡2】", zap.Error(err2))
}
oldBankOrderId := orderInfo.BankOrderId
//错误订单回调上游
delayPool.CtxGo(context.Background(), func() {
ctx2, span := otelTrace.NewSchedulerTrace().Start(otelTrace.InitCtx, "Span")
defer span.End()
service.SolvePayFail(ctx2, oldBankOrderId, orderInfo.BankTransId, hiddenCfg.ExtraReturnInfo)
})
orderInfo.BankOrderId = newBankOrderId
}
}
otelTrace.Logger.WithContext(c.Ctx.Request.Context()).Info("【BaseGateway】生成订单记录", zap.Any("orderInfo", orderInfo))
cdata := supplier.RedeemCardInfo{}
err = json.Unmarshal([]byte(orderInfo.ExValue), &cdata)
if err != nil {
otelTrace.Logger.WithContext(c.Ctx.Request.Context()).Error("格式化数据失败", zap.Error(err), zap.String("ExValue", orderInfo.ExValue))
p.Msg = fmt.Sprintf("格式化数据失败:%v", orderInfo.ExValue)
c.SolveFailJSON(p)
}
isAllowed, err := backend.GetIPIsRestricted(c.Ctx.Request.Context(), p.ClientIP, mt.Id, orderInfo.BankOrderId, cdata.Data, convertor.ToString(p.Params["deviceId"]))
order.UpdateIpRestricted(c.Ctx.Request.Context(), orderInfo.BankOrderId, isAllowed)
otelTrace.Logger.WithContext(c.Ctx.Request.Context()).Info("IP是否允许", zap.Bool("isAllowed", isAllowed))
// otel.Logger.WithContext(c.Ctx.Request.Context()).Info("IP是否允许%v", isAllowed)
if !isAllowed {
otelTrace.Logger.WithContext(c.Ctx.Request.Context()).Info(fmt.Sprintf("IP被限制无法兑换 %s", p.ClientIP), zap.String("ClientIP", p.ClientIP))
c.Data["json"] = response.CommonErr(-1, errors.New("提交失败").Error())
service.SolvePayFail(c.Ctx.Request.Context(), orderInfo.BankOrderId, "", "IP限制无法兑换")
_ = c.ServeJSON()
return
}
otelTrace.Logger.WithContext(c.Ctx.Request.Context()).Info("【BaseGateway】生成订单记录", zap.Any("orderInfo", orderInfo))
if mt.AutoSettle == config.NO {
params := map[string]any{
"orderNo": orderInfo.BankOrderId,
"orderPrice": strconv.FormatFloat(orderInfo.OrderAmount, 'f', 2, 64),
"statusCode": "00",
}
sign := utils.GetMD5SignMF(params, p.MerchantInfo.MerchantSecret)
c.Data["json"] = response.ScanSuccessData{
OrderNo: orderInfo.BankOrderId,
OrderPrice: strconv.FormatFloat(orderInfo.OrderAmount, 'f', 2, 64),
StatusCode: "00",
Sign: sign,
Msg: "请求成功,请等待兑换!",
Code: 0,
PayUrl: "",
}
_ = c.ServeJSON()
return
}
// 获取到对应的上游
supplierCode := p.RoadInfo.ProductUid
supplierByCode := third_party.GetPaySupplierByCode(supplierCode)
if supplierByCode == nil {
// 插入处理失败的动账通知
service.SolvePayFail(c.Ctx.Request.Context(), orderInfo.BankOrderId, "", "")
otelTrace.Logger.WithContext(c.Ctx.Request.Context()).Error("获取上游渠道失败,请联系客服", zap.String("supplierCode", supplierCode))
c.Data["json"] = response.CommonErr(-1, errors.New("获取上游渠道失败,请联系客服").Error())
_ = c.ServeJSON()
c.StopRun()
return
}
otelTrace.Logger.WithContext(c.Ctx.Request.Context()).Info("【BaseGateway】获取到对应的上游", zap.Any("supplierByCode", supplierByCode))
scanData := supplierByCode.Scan(c.Ctx.Request.Context(), orderInfo, p.RoadInfo, p.MerchantInfo)
order.InsertCardReturnData(scanData.BankNo, scanData.ReturnData)
if scanData.Status == "00" {
scanSuccessData := service.GenerateSuccessData(scanData, p)
c.Data["json"] = scanSuccessData
_ = c.ServeJSON()
return
}
// 插入处理失败的动账通知
service.SolvePayFail(c.Ctx.Request.Context(), orderInfo.BankOrderId, "", scanData.Msg)
p.Msg = scanData.Msg
p.Code = -1
c.SolveFailJSON(p)
return
}
// SolveFailJSON 处理错误的返回
func (c *ScanController) SolveFailJSON(p *response.PayBaseResp) {
c.Data["json"] = response.ScanFailData{
StatusCode: "01",
PayKey: convertor.ToString(p.Params["payKey"]),
Msg: p.Msg,
Code: -1,
}
_ = c.ServeJSON()
c.StopRun()
}
func (c *ScanController) GetAllowedMM() {
payKey := strings.TrimSpace(c.GetString("payKey"))
showMMValue, err := c.GetFloat("showMMValue")
productCode := strings.TrimSpace(c.GetString("productCode"))
if payKey == "" || showMMValue == 0 || productCode == "" {
res := response.CommonErr(-1, "获取面额失败,参数缺失")
c.Data["json"] = res
_ = c.ServeJSON()
return
}
if err != nil {
c.Data["json"] = response.CommonErr(-1, err.Error())
_ = c.ServeJSON()
c.StopRun()
}
merchantInfo, err := service.GetMerchantInfoByPayKey(c.Ctx.Request.Context(), payKey)
if err != nil || merchantInfo.Id == 0 {
c.Data["json"] = response.CommonErr(-1, "获取面额失败,获取商户信息出错")
_ = c.ServeJSON()
c.StopRun()
}
merchantDeployInfo := service.GerMerchantDeployInfoByUidAndProductCode(c.Ctx.Request.Context(), merchantInfo.MerchantUid, productCode)
if merchantDeployInfo.Id == 0 {
res := response.CommonErr(-1, "获取面额失败,当前通道不存在")
c.Data["json"] = res
_ = c.ServeJSON()
return
}
profitMarginList, err := merchantDeployInfo.GetFactMMValue(c.Ctx.Request.Context(), showMMValue)
if err != nil {
c.Data["json"] = response.CommonErr(-1, err.Error())
_ = c.ServeJSON()
c.StopRun()
}
type profitMarginStruct struct {
Sort int `json:"sort" description:"排序"`
FactLabel float64 `json:"factLabel" description:"实际面值"`
ShowLabel float64 `json:"showLabel" description:"展示面额"`
PlatformLabel string `json:"platformLabel" description:"平台"`
IsLinkSingle bool `json:"isLinkSingle" description:"链接是否单独放置"`
LinkID string `json:"linkID" description:"链接"`
}
otelTrace.Logger.WithContext(c.Ctx.Request.Context()).Info("当前请求面额数据", zap.Any("profitMarginList", profitMarginList))
resData := make([]*profitMarginStruct, 0)
for _, v := range profitMarginList {
if v.ShowLabel != 0 || v.FactLabel != 0 {
resData = append(resData, &profitMarginStruct{
FactLabel: v.FactLabel,
ShowLabel: v.ShowLabel,
PlatformLabel: v.PlatformLabel,
IsLinkSingle: v.IsLinkSingle,
LinkID: v.LinkID,
Sort: v.Sort,
})
}
}
otelTrace.Logger.WithContext(c.Ctx.Request.Context()).Info("当前请求面额数据", zap.Any("profitMarginList", profitMarginList))
c.Data["json"] = response.Ok(resData)
_ = c.ServeJSON()
}
// CreateOrder 创建订单
func (c *ScanController) CreateOrder() {
createdOrder := request.CreatedOrder{}
_ = c.Bind(&createdOrder)
otelTrace.Logger.WithContext(c.Ctx.Request.Context()).Info("创建订单:", zap.Any("createdOrder", createdOrder))
valid := validation.Validation{}
b, err := valid.Valid(&createdOrder)
if err != nil || !b {
otelTrace.Logger.WithContext(c.Ctx.Request.Context()).Info("创建订单错误:", zap.Error(err))
res := response.CommonErr(-1, "创建订单失败,参数错误")
c.Data["json"] = res
_ = c.ServeJSON()
return
}
ctx, cancel := otelTrace.Span(c.Ctx.Request.Context(), "API订单", "ScanController", trace.WithAttributes(
attribute.String("PayKey", createdOrder.PayKey),
attribute.String("ProductCode", createdOrder.ProductCode),
attribute.String("productCode", createdOrder.Sign),
))
defer cancel()
c.Ctx.Request = c.Ctx.Request.WithContext(ctx)
merchantInfo := merchant.GetMerchantByPasskey(c.Ctx.Request.Context(), createdOrder.PayKey)
if merchantInfo.Id == 0 {
otelTrace.Logger.WithContext(c.Ctx.Request.Context()).Info("创建订单错误:", zap.Error(err))
c.Data["json"] = response.CommonErr(-1, "创建订单错误,商户不存在")
_ = c.ServeJSON()
return
}
if !utils.Md5MFVerify(c.Ctx.Request.Context(), createdOrder.ToMap(), merchantInfo.MerchantSecret) && !utils.Md5Verify(createdOrder.ToMap(), merchantInfo.MerchantSecret) {
otelTrace.Logger.WithContext(c.Ctx.Request.Context()).Info("创建订单错误sign校验失败")
c.Data["json"] = response.CommonErr(-1, "sign验证错误")
_ = c.ServeJSON()
return
}
orderInfo := order.GetOrderByMerchantOrderId(c.Ctx.Request.Context(), createdOrder.OrderNo)
roadInfo := road.GetRoadInfoByProductCode(c.Ctx.Request.Context(), createdOrder.ProductCode)
if orderInfo.Id != 0 {
res := response.Ok(struct {
ProductCode string `json:"productCode"`
PaymentName string `json:"paymentName"`
TransactionType string `json:"transactionType"`
PayUrl string `json:"payUrl"`
MerchantOrderNo string `json:"merchantOrderNo"`
OrderNo string `json:"orderNo"`
}{
ProductCode: createdOrder.ProductCode,
PaymentName: roadInfo.PaymentHtml,
TransactionType: roadInfo.TransactionType,
PayUrl: orderInfo.PayUrl,
OrderNo: orderInfo.BankOrderId,
MerchantOrderNo: createdOrder.OrderNo,
})
c.Data["json"] = res
_ = c.ServeJSON()
return
}
// 获取到对应的上游
supplierCode := roadInfo.ProductUid
supplierByCode := third_party.GetPaySupplierByCode(supplierCode)
if supplierByCode == nil {
// 插入处理失败的动账通知
otelTrace.Logger.WithContext(c.Ctx.Request.Context()).Error("获取上游渠道失败,请联系客服", zap.String("supplierCode", supplierCode))
err = errors.New("获取上游渠道失败,请联系客服")
c.Data["json"] = response.CommonErr(-1, err.Error())
_ = c.ServeJSON()
return
}
// 创建订单记录
orderInfo, err = service.CreateOrderInfoAndOrderProfitInfo(c.Ctx.Request.Context(), createdOrder, merchantInfo)
if err != nil {
res := response.CommonErr(-1, "创建订单失败,创建订单记录失败")
otelTrace.Logger.WithContext(c.Ctx.Request.Context()).Error("创建订单错误:", zap.Error(err))
c.Data["json"] = res
_ = c.ServeJSON()
return
}
otelTrace.Logger.WithContext(c.Ctx.Request.Context()).Info("获取供应商信息", zap.Any("supplierCode", supplierCode))
payUrl := ""
bankTransId := ""
//TODO: 区分自有渠道和三方渠道
if supplierByCode.HasDependencyHTML() {
scanData := supplierByCode.Scan(c.Ctx.Request.Context(), orderInfo, roadInfo, merchantInfo)
payUrl = scanData.PayUrl
bankTransId = scanData.UpStreamOrderNo
} else {
orderParams := dto.Params{
GeneratedTime: time.Now().Unix(),
Duration: createdOrder.OrderPeriod,
PayKey: createdOrder.PayKey,
OrderNo: createdOrder.OrderNo,
ProductCode: createdOrder.ProductCode,
ShowMMValue: createdOrder.OrderPrice,
NotifyUrl: createdOrder.NotifyUrl,
}
payUrl = config.GetConfig().ShopAddr() + "?sign=" + orderParams.Encrypt()
}
if err = order.UpdatePayUrlAndTime(orderInfo.BankOrderId, payUrl, bankTransId, ""); err != nil {
otelTrace.Logger.WithContext(c.Ctx.Request.Context()).Error("更新订单支付链接失败:", zap.Error(err))
c.Data["json"] = response.CommonErr(-1, "更新订单支付链接失败")
return
}
resp := struct {
ProductCode string `json:"productCode"`
PaymentName string `json:"paymentName"`
TransactionType string `json:"transactionType"`
PayUrl string `json:"PayUrl"`
OrderNo string `json:"OrderNo"`
MerchantOrderNo string `json:"MerchantOrderNo"`
Sign string `json:"sign"`
}{
ProductCode: createdOrder.ProductCode,
MerchantOrderNo: createdOrder.OrderNo,
PaymentName: roadInfo.PaymentHtml,
TransactionType: roadInfo.TransactionType,
PayUrl: payUrl,
OrderNo: orderInfo.BankOrderId,
Sign: "",
}
respMap, err := structs.New(resp).ToMap()
if err != nil {
otelTrace.Logger.WithContext(c.Ctx.Request.Context()).Error("创建订单错误:", zap.Error(err))
c.Data["json"] = response.CommonErr(-1, err.Error())
_ = c.ServeJSON()
return
}
resp.Sign = utils.GetMD5SignMF(respMap, merchantInfo.MerchantSecret)
c.Data["json"] = response.Ok(resp)
_ = c.ServeJSON()
}
func (c *ScanController) QueryAccountInfo() {
accountInfo := request.ThirdPartyAccountInfo{}
channelName := c.Ctx.Input.Param("channel")
if channelName == "TMallGame" {
_ = c.BindJSON(&accountInfo)
err := t_mall_game.QueryTMallGameAccountInfo(context.Background(), request.ThirdPartyAccountInfo{})
_ = err
}
return
}