Files
kami_backend/internal/logic/camel_oil/cron_tasks.go
danial 15e2426e85 feat(camel_oil): 新增骆驼加油账号管理模块
- 实现账号增删改查接口和逻辑
- 支持账号状态更新及状态历史记录功能
- 提供账号列表、历史和统计信息查询API
- 实现账号轮询机制,支持按使用时间轮询获取账号
- 增加账号登录流程及批量登录功能,集成接码平台和平台API
- 管理账号订单容量,支持容量检查与账号登录触发
- 提供账号池状态统计接口
- 账号历史记录查询支持多种变更类型文本展示
- 密码等敏感信息采用脱敏展示
- 完善日志记录和错误处理机制,保证业务稳定运行
2025-11-21 00:49:50 +08:00

242 lines
7.5 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 camel_oil
import (
"context"
"fmt"
"kami/internal/consts"
"kami/internal/dao"
"kami/internal/model/do"
"kami/internal/model/entity"
"kami/utility/config"
"kami/utility/integration/camel_oil_api"
"kami/utility/integration/pig"
"github.com/gogf/gf/v2/os/glog"
"github.com/gogf/gf/v2/os/gtime"
)
// CronAccountLoginTask 账号登录任务 - 由cron调度器定期调用
func (s *sCamelOil) CronAccountLoginTask(ctx context.Context) error {
// 检查可用订单容量
capacity, err := s.GetAvailableOrderCapacity(ctx)
if err != nil {
glog.Error(ctx, "获取可用容量失败:", err)
return err
}
glog.Infof(ctx, "当前可用订单容量: %d", capacity)
// 如果容量低于50继续登录新账号
if capacity <= 50 {
err = s.CheckAndTriggerAccountLogin(ctx)
if err != nil {
glog.Error(ctx, "触发账号登录失败:", err)
return err
}
}
return nil
}
// CronOrderPaymentCheckTask 订单支付状态检测任务 - 由cron调度器定期调用
func (s *sCamelOil) CronOrderPaymentCheckTask(ctx context.Context) error {
glog.Info(ctx, "开始执行订单支付状态检测任务")
// 查询待支付订单创建时间在24小时内
var orders []*entity.V1CamelOilOrder
err := dao.V1CamelOilOrder.Ctx(ctx).DB(config.GetDatabaseV1()).
Where(dao.V1CamelOilOrder.Columns().PayStatus, consts.CamelOilPaymentStatusUnpaid).
WhereGTE(dao.V1CamelOilOrder.Columns().CreatedAt, gtime.Now().AddDate(0, 0, -1)).
Scan(&orders)
if err != nil {
glog.Error(ctx, "查询待支付订单失败:", err)
return err
}
if len(orders) == 0 {
glog.Debug(ctx, "无待支付订单")
return nil
}
glog.Infof(ctx, "查询到 %d 个待支付订单", len(orders))
// 检测每个订单的支付状态(使用假数据)
paidCount := 0
timeoutCount := 0
for _, order := range orders {
accountInfo, err2 := s.GetAccountInfo(ctx, order.AccountId)
if err2 != nil {
glog.Error(ctx, "获取账号信息失败:", err2)
return err2
}
ok, err := camel_oil_api.NewClient().QueryOrder(ctx, accountInfo.Phone, accountInfo.Token, order.PlatformOrderNo)
if err != nil {
glog.Error(ctx, "查询订单状态失败:", err)
}
if ok {
// 更新状态
_, _ = dao.V1CamelOilOrder.Ctx(ctx).DB(config.GetDatabaseV1()).
Where(dao.V1CamelOilOrder.Columns().Id, order.Id).
Update(&do.V1CamelOilOrder{
PaidAt: gtime.Now(),
PayStatus: int(consts.CamelOilPaymentStatusPaid),
})
}
// 模拟支付状态检测
// 如果订单创建超过1小时标记为超时
if gtime.Now().Sub(order.CreatedAt).Hours() > 1 {
// 更新为超时
_, _ = dao.V1CamelOilOrder.Ctx(ctx).DB(config.GetDatabaseV1()).
Where(dao.V1CamelOilOrder.Columns().Id, order.Id).
Update(&do.V1CamelOilOrder{
PayStatus: int(consts.CamelOilPaymentStatusTimeout),
FailureReason: "支付时间超过一个小时,支付超时",
})
_ = s.RecordOrderHistory(ctx, order.OrderNo, "payment_timeout", "", "订单支付超时")
timeoutCount++
}
}
glog.Infof(ctx, "订单支付状态检测完成: 已支付=%d, 超时=%d", paidCount, timeoutCount)
return nil
}
// CronAccountDailyResetTask 账号日重置任务 - 由cron调度器在每日00:05调用
func (s *sCamelOil) CronAccountDailyResetTask(ctx context.Context) error {
glog.Info(ctx, "开始执行账号日重置任务")
yesterday := gtime.Now().AddDate(0, 0, -1)
_, _ = dao.V1CamelOilAccount.Ctx(ctx).DB(config.GetDatabaseV1()).
Where(dao.V1CamelOilAccount.Columns().Status, consts.CamelOilAccountStatusOnline).
Update(&do.V1CamelOilAccount{
DailyOrderCount: 0,
DailyOrderDate: gtime.Now(),
})
// 查询所有暂停的账号
var accounts []*entity.V1CamelOilAccount
err := dao.V1CamelOilAccount.Ctx(ctx).DB(config.GetDatabaseV1()).
Where(dao.V1CamelOilAccount.Columns().Status, consts.CamelOilAccountStatusPaused).
Scan(&accounts)
if err != nil {
glog.Error(ctx, "查询暂停账号失败:", err)
return err
}
glog.Infof(ctx, "查询到 %d 个暂停账号", len(accounts))
resumedCount := 0
invalidCount := 0
for _, account := range accounts {
// 检查是否是昨日的记录
if account.DailyOrderDate == nil || account.DailyOrderDate.Format("Y-m-d") != yesterday.Format("Y-m-d") {
continue
}
if account.DailyOrderCount >= 10 {
// 正常完成,重置为在线
_ = s.UpdateAccountStatus(ctx, account.Id, consts.CamelOilAccountStatusOnline,
consts.CamelOilAccountChangeTypeResume, "次日重置,恢复使用")
// 同时重置日算信息
_, _ = dao.V1CamelOilAccount.Ctx(ctx).DB(config.GetDatabaseV1()).
Where(dao.V1CamelOilAccount.Columns().Id, account.Id).
Update(&do.V1CamelOilAccount{
DailyOrderCount: 0,
DailyOrderDate: gtime.Now(),
})
resumedCount++
} else {
// 单日下单不足10个标记为失效
_ = s.UpdateAccountStatus(ctx, account.Id, consts.CamelOilAccountStatusInvalid,
consts.CamelOilAccountChangeTypeInvalidate, "单日下单不足10个账号失效")
invalidCount++
}
}
glog.Infof(ctx, "账号日重置任务完成: 恢复=%d, 失效=%d", resumedCount, invalidCount)
return nil
}
func (s *sCamelOil) CronVerifyCodeCheckTask(ctx context.Context) error {
glog.Info(ctx, "开始执行验证码检测任务")
var accounts []*entity.V1CamelOilAccount
err := dao.V1CamelOilAccount.Ctx(ctx).DB(config.GetDatabaseV1()).
Where(dao.V1CamelOilAccount.Columns().Status, consts.CamelOilAccountStatusSendCode).
Scan(&accounts)
if err != nil {
glog.Error(ctx, "查询待验证码账号失败:", err)
return err
}
glog.Infof(ctx, "查询到 %d 个待验证码账号", len(accounts))
successCount := 0
failCount := 0
pigClient := pig.NewClient()
camelClient := camel_oil_api.NewClient()
for _, account := range accounts {
// 从野猪平台检测验证码是否已接收
verifyCode, received, err := pigClient.CheckVerifyCode(ctx, account.Phone)
if err != nil {
glog.Warningf(ctx, "检测验证码失败账号ID: %d, 手机号: %s, 错误: %v", account.Id, account.Phone, err)
failCount++
continue
}
// 验证码未接收,继续等待
if !received {
continue
}
// 验证码已接收,执行登录
glog.Infof(ctx, "验证码已接收开始执行登录账号ID: %d, 手机号: %s", account.Id, account.Phone)
// 调用骆驼加油平台执行登录
token, err := camelClient.LoginWithCaptcha(ctx, account.Phone, verifyCode)
if err != nil {
glog.Errorf(ctx, "登录失败账号ID: %d, 手机号: %s, 错误: %v", account.Id, account.Phone, err)
// 标记账号失效
_ = s.UpdateAccountStatus(ctx, account.Id, consts.CamelOilAccountStatusInvalid,
consts.CamelOilAccountChangeTypeLoginFail, fmt.Sprintf("登录失败: %v", err))
failCount++
continue
}
// 保存 token
_, err = dao.V1CamelOilAccount.Ctx(ctx).DB(config.GetDatabaseV1()).
Where(dao.V1CamelOilAccount.Columns().Id, account.Id).
Update(&do.V1CamelOilAccount{
Token: token,
LastLoginAt: gtime.Now(),
})
if err != nil {
glog.Errorf(ctx, "保存 token 失败账号ID: %d, 错误: %v", account.Id, err)
failCount++
continue
}
// 调用 UpdateAccountStatus 更新账号状态为在线
_ = s.UpdateAccountStatus(ctx, account.Id, consts.CamelOilAccountStatusOnline,
consts.CamelOilAccountChangeTypeLogin, fmt.Sprintf("登录成功,手机号: %s", account.Phone))
glog.Infof(ctx, "账号登录成功ID: %d, 手机号: %s, Token: %s", account.Id, account.Phone, token)
successCount++
}
glog.Infof(ctx, "验证码检测任务完成: 成功=%d, 失败=%d", successCount, failCount)
return nil
}