Files
kami_scripts/verification/internal/services/order_service.go
danial 0fe17c807f feat(client): 优化HeePay卡密查询逻辑及配置更新
- 新增配置项 QueryURL,支持自定义查询地址
- 修改QueryCardInput结构体,调整字段名更准确
- 使用resty替代beego httplib,提升请求稳定性和重试能力
- 调整请求URL为kami-spider-monorepo服务地址
- 优化错误重试逻辑,针对验证码识别失败进行重试
- 调整响应解析和错误处理逻辑
- 更新order_service调用,传递新配置和请求参数
- 升级多个依赖包版本,提升模块稳定性和安全性
- 修正配置文件config.yaml中字段及格式,使配置更合理
2025-11-16 17:07:37 +08:00

165 lines
4.5 KiB
Go

package services
import (
"context"
"database/sql"
"encoding/csv"
"encoding/json"
"order/internal/client"
"os"
"strings"
"time"
"github.com/duke-git/lancet/v2/convertor"
"github.com/pkg/errors"
"order/internal/config"
"order/internal/interfaces"
"github.com/duke-git/lancet/v2/slice"
)
// OrderServiceImpl 订单服务实现
type OrderServiceImpl struct {
repo interfaces.OrderRepository
sender interfaces.OrderSender
logger interfaces.Logger
config *config.Config
}
// NewOrderService 创建订单服务实例
func NewOrderService(
repo interfaces.OrderRepository,
sender interfaces.OrderSender,
logger interfaces.Logger,
config *config.Config,
) interfaces.OrderService {
return &OrderServiceImpl{
repo: repo,
sender: sender,
logger: logger,
config: config,
}
}
// ProcessOrders 处理订单
func (s *OrderServiceImpl) ProcessOrders(ctx context.Context) error {
// 判断这个文件有没有,如果有,打开,没有就创建一个
filePath := "order.csv"
if _, err := os.Stat(filePath); os.IsNotExist(err) {
_, _ = os.Create(filePath)
}
// 从 csv 中读取最后一个匹配的 road_uid 和 bank_order_id
csvFile, err := os.OpenFile("order.csv", os.O_APPEND|os.O_CREATE|os.O_RDWR, 0644)
if err != nil {
s.logger.Error("打开文件失败", "error", err)
return err
}
defer func(csvFile *os.File) {
_ = csvFile.Close()
}(csvFile)
orders, err := s.repo.FindOrdersByReason(ctx, "不支持的骏卡", time.Now().Add(-2*time.Minute), time.Now().Add(1*time.Minute))
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
s.logger.Error("获取订单失败", "error", err)
}
return err
}
if len(orders) == 0 {
return nil
}
csvReader := csv.NewReader(csvFile)
// 读取所有数据
records, err2 := csvReader.ReadAll()
if err2 != nil {
s.logger.Error("读取文件失败", "error", err)
}
// 筛选符合条件的数据
existBankOrders := slice.Filter(slice.Map(records, func(index int, record []string) string {
if len(record) >= 1 {
return record[0]
}
return ""
}), func(index int, record string) bool {
return record != ""
})
existBankOrders = slice.Unique(existBankOrders)
for _, order := range orders {
if slice.Contain(existBankOrders, order.BankOrderID) {
continue
}
s.logger.Info("开始处理订单", "order", order, "已有订单数量", len(existBankOrders))
heePayClient := client.NewHeePayClient()
exRedeemValue := RedeemCardInfo{}
if err = json.Unmarshal([]byte(convertor.ToString(order.ExValue)), &exRedeemValue); err != nil {
s.logger.Error("解析exValue失败", "error", err)
continue
}
heePayResp, err3 := heePayClient.QueryCard(ctx, &client.QueryCardInput{
QueryURL: s.config.QueryURL,
OrderId: order.BankOrderID,
CardNumber: exRedeemValue.CardNo,
CardPassword: exRedeemValue.Data,
})
if err3 != nil {
s.logger.Error("查询卡密失败", "error", err)
continue
}
merchants := s.config.Merchants
merchant, ok := slice.FindBy(merchants, func(index int, item *struct {
Name string `mapstructure:"name"`
SubmitURL string `mapstructure:"submit_url"`
ProductCode string `mapstructure:"product_code"`
PayKey string `mapstructure:"pay_key"`
PaySecret string `mapstructure:"pay_secret"`
CardType string `mapstructure:"card_type"`
}) bool {
return strings.Contains(heePayResp.Data.CardType, item.CardType)
})
if !ok {
s.logger.Error("未找到匹配的商户", "error", heePayResp)
continue
}
if err = s.sender.Send(ctx, order, merchant.ProductCode, merchant.PayKey, merchant.PaySecret, merchant.SubmitURL); err != nil {
s.logger.Error("提交订单失败", "error", err)
continue
}
csvWriter := csv.NewWriter(csvFile)
if err = csvWriter.Write([]string{order.BankOrderID}); err != nil {
s.logger.Error("写入文件失败", "error", err)
}
csvWriter.Flush()
}
return nil
}
type RedeemCardInfo struct {
FaceType string `json:"faceType"` // 面额
RecoveryType string `json:"RecoveryType,omitempty"` // 类型 2 仅卡密 8 卡号卡密
Data string `json:"data"` // 卡密
CardNo string `json:"cardNo,omitempty"` // 卡号
}
func (d *RedeemCardInfo) GetFaceType() string {
return d.FaceType
}
func (d *RedeemCardInfo) GetFaceTypeFloat(ctx context.Context) float64 {
result, err := convertor.ToFloat(d.FaceType)
if err != nil {
return 0
}
return result
}
func (d *RedeemCardInfo) GetRecoveryType() string {
return d.RecoveryType
}
func (d *RedeemCardInfo) ToJson() string {
jsonStr, _ := json.Marshal(d)
return string(jsonStr)
}