Compare commits

...

2 Commits

12 changed files with 406 additions and 47 deletions

View File

@ -29,6 +29,7 @@ type SmsRenewalLog struct {
ApiKey string `json:"apiKey" gorm:"->"`
BillingCycleId string `json:"billingCycleId" gorm:"->"`
ActivationId string `json:"activationId" gorm:"->"`
PlatformState string `json:"platformState" gorm:"-"`
models.ModelTime
models.ControlBy
}

View File

@ -138,15 +138,16 @@ type ManualDeductReq struct {
}
type ManualDeductResp struct {
ActivationId string `json:"activationId" comment:"激活id"`
TradeOrderNo string `json:"tradeOrderNo" comment:"交易订单号"`
PayOrderNo string `json:"payOrderNo" comment:"支付订单号"`
BeginTime time.Time `json:"beginTime" comment:"开始时间"`
EndTime *time.Time `json:"endTime" comment:"结束时间"`
Status int `json:"status" comment:"状态 1-预扣费 2-成功 3-失败"`
ActivationId string `json:"activationId" comment:"激活id"`
TradeOrderNo string `json:"tradeOrderNo" comment:"交易订单号"`
PayOrderNo string `json:"payOrderNo" comment:"支付订单号"`
BeginTime time.Time `json:"beginTime" comment:"开始时间"`
EndTime *time.Time `json:"endTime" comment:"结束时间"`
Status int `json:"status" comment:"状态 1-预扣费 2-成功 3-失败"`
PlatformState string `json:"platformState" comment:"平台状态 verificationPending┃verificationCompleted┃verificationCanceled┃verificationTimedOut┃verificationReported┃verificationRefunded┃verificationReused┃verificationReactivated┃renewableActive┃renewableOverdue┃renewableExpired┃renewableRefunded┃nonrenewableActive┃nonrenewableExpired┃nonrenewableRefunded"`
}
// 续费详情
type ManualDeductDetailReq struct {
TradeOrderNo string `json:"tradeOrderNo" comment:"交易订单号"`
TradeOrderNo string `json:"tradeOrderNo" form:"tradeOrderNo" query:"tradeOrderNo" comment:"交易订单号"`
}

View File

@ -1,10 +1,12 @@
package service
import (
"database/sql"
"errors"
"fmt"
"net/http"
"strconv"
"sync"
"time"
"github.com/go-admin-team/go-admin-core/sdk/service"
@ -712,9 +714,10 @@ func (e *SmsPhone) ChangeAutoRenewManage(platform, apiKey string, activationId s
return service.ChangeAutoRenewForApi(activationId, status, &apiInfo)
case global.SmsPlatformTextVerified:
service := SmsTextVerified{Service: e.Service}
// service := SmsTextVerified{Service: e.Service}
return service.Renew(activationId, status, &apiInfo)
// return service.Renew(activationId, status, &apiInfo)
return statuscode.Success
default:
return statuscode.SmsPlatformUnavailable
}
@ -919,3 +922,116 @@ func (e *SmsPhone) syncLoop(nextUrl string, textVerifiedService SmsTextVerified,
}
return rentalList, nil
}
func (e *SmsPhone) GetPhoneActualEnd() error {
var phones []models.SmsPhone
// 查询待处理的号码
if err := e.Orm.Model(&models.SmsPhone{}).
Joins("LEFT JOIN sms_phone_judge AS s ON sms_phone.id = s.phone_id").
Where("sms_phone.platform_code = ? AND sms_phone.expire_time > ? AND s.id IS NULL",
global.SmsPlatformTextVerified, "2025-11-17").
Select("sms_phone.*").
Find(&phones).Error; err != nil {
e.Log.Errorf("查询手机号码失败 err:%v", err)
return err
}
if len(phones) == 0 {
return nil
}
workerCount := 4 // 开 8 个并发 worker
e.runWorkerPool(phones, workerCount)
return nil
}
func (e *SmsPhone) runWorkerPool(phones []models.SmsPhone, workerCount int) {
phoneChan := make(chan models.SmsPhone, workerCount*2)
var wg sync.WaitGroup
wg.Add(workerCount)
// 开 worker
for i := 0; i < workerCount; i++ {
go func() {
defer wg.Done()
e.workerProcess(phoneChan)
}()
}
// 投递任务
for _, p := range phones {
phoneChan <- p
}
close(phoneChan)
// 等待全部 worker 完成
wg.Wait()
}
func (e *SmsPhone) workerProcess(ch <-chan models.SmsPhone) {
service := SmsTextVerified{Service: e.Service}
smsPlatformKeyRedis := NewSmsPlatformKeyRedis(e.Orm, e.Log)
for item := range ch {
// 获取 API info
apiInfo, err := smsPlatformKeyRedis.GetApiInfo(item.PlatformCode, item.ApiKey)
if err != nil {
e.Log.Errorf("获取短信平台密钥失败 [PlatformCode: %s]: %v", item.PlatformCode, err)
continue
}
// 获取账单周期
cycleInfo, err := service.GetBillingCycle(item.BillingCycleId, &apiInfo)
if err != nil {
e.Log.Errorf("获取账单周期失败 [Phone: %s]: %v", item.Phone, err)
continue
}
// 插入判断表
if err := e.Orm.Exec(`
INSERT INTO sms_phone_judge(phone_id, phone, expire_time, end_time)
VALUES(@phone_id, @phone, @expire_time, @endTime)
`,
sql.Named("phone_id", item.Id),
sql.Named("phone", item.Phone),
sql.Named("expire_time", item.ExpireTime),
sql.Named("endTime", cycleInfo.RenewedThrough),
).Error; err != nil {
e.Log.Errorf("插入 sms_phone_judge 失败 [Phone: %s]: %v", item.Phone, err)
continue
}
}
}
// 设置为平台手动续费
func (e *SmsPhone) SetManualRenewal() error {
var phones []models.SmsPhone
if err := e.Orm.Model(&models.SmsPhone{}).
Where("platform_auto =1 and auto_renewal = 1 and platform_code = ?", global.SmsPlatformTextVerified).
Find(&phones).Error; err != nil {
e.Log.Errorf("查询手机号码失败 err:%v", err)
return err
}
smsPlatformKeyRedis := NewSmsPlatformKeyRedis(e.Orm, e.Log)
for _, item := range phones {
apiInfo, err := smsPlatformKeyRedis.GetApiInfo(item.PlatformCode, item.ApiKey)
if err != nil {
e.Log.Errorf("获取短信平台密钥失败 [PlatformCode: %s]: %v", item.PlatformCode, err)
continue
}
service := SmsTextVerified{Service: e.Service}
if err := service.Renew(item.ActivationId, false, &apiInfo); err != statuscode.Success {
e.Log.Errorf("设置为平台手动续费失败 [ActivationId: %s]: %v", item.ActivationId, err)
continue
}
if err := e.Orm.Model(&item).
Where("activation_id = ?", item.ActivationId).
Update("platform_auto", 2).Error; err != nil {
e.Log.Errorf("更新手动续费id失败 err:%v", err)
continue
}
}
return nil
}

View File

@ -83,7 +83,7 @@ func TestManualRenewal(t *testing.T) {
service := SmsRenewalLog{}
service.Orm = db
service.Log = logger.NewHelper(logger.DefaultLogger)
activationId := "lr_01K5DP71G06SFX84S7W99D61F9"
activationId := "lr_01KA8J2AKDN480QE5HGTQGEXEW"
if _, err := service.ManualDeduct(&dto.ManualDeductReq{
ActivationId: activationId,
@ -92,3 +92,32 @@ func TestManualRenewal(t *testing.T) {
t.Error(err)
}
}
func TestGetPhoneActualEnd(t *testing.T) {
db := initSetting()
service := SmsPhone{}
service.Orm = db
service.Log = logger.NewHelper(logger.DefaultLogger)
// if err := service.Orm.Exec("truncate table sms_phone_judge").Error; err != nil {
// t.Error(err)
// }
if err := service.GetPhoneActualEnd(); err != nil {
t.Error(err)
}
}
// 测试修改为手动续期
func TestSetManualRenewal(t *testing.T) {
db := initSetting()
service := SmsPhone{}
service.Orm = db
service.Log = logger.NewHelper(logger.DefaultLogger)
if err := service.SetManualRenewal(); err != nil {
t.Fatalf("SetManualRenewal failed: %v", err)
}
}

View File

@ -14,7 +14,7 @@ import (
)
func initSetting() *gorm.DB {
dsn := "root:123456@tcp(127.0.0.1:3306)/proxy_server_prod?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms"
dsn := "root:123456@tcp(127.0.0.1:3306)/proxy_server?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms"
db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})
sdk.Runtime.SetDb("default", db)
// config.ExtConfig.

View File

@ -182,6 +182,7 @@ func (e *SmsRenewalLog) ManualDeduct(req *dto.ManualDeductReq, userId int) (dto.
result.PayOrderNo = renewLog.PayOrderNo
result.BeginTime = req.BeginTime
result.EndTime = renewLog.TargetTime
result.PlatformState = renewLog.PlatformState
}
return result, statuscode.Success
@ -192,6 +193,10 @@ func (e *SmsRenewalLog) GetRenewalDetailByTradeOrderNo(req *dto.ManualDeductDeta
var entity models.SmsRenewalLog
result := dto.ManualDeductResp{}
if req.TradeOrderNo == "" {
return result, statuscode.InvalidParams
}
if err := e.Orm.Model(entity).
Joins("LEFT JOIN sms_phone s on s.id = sms_renewal_log.phone_id").
Where("sms_renewal_log.trade_order_no =? and sms_renewal_log.user_id =?", req.TradeOrderNo, userId).
@ -206,6 +211,7 @@ func (e *SmsRenewalLog) GetRenewalDetailByTradeOrderNo(req *dto.ManualDeductDeta
result.PayOrderNo = entity.PayOrderNo
result.BeginTime = entity.BeforeTime
result.EndTime = entity.TargetTime
result.Status = entity.Status
return result, statuscode.Success
}
@ -386,18 +392,20 @@ func (e *SmsPhone) getRenewalConfigs() (map[string]models.SmsServices, map[strin
func (e *SmsRenewalLog) processPhoneRenewal(phone models.SmsPhone, serviceMap map[string]models.SmsServices,
premiumMap map[string]dto.GetSysConfigByKEYForServiceResp, tradeOrderNo string, admin bool) (models.SmsRenewalLog, error) {
var renewLog models.SmsRenewalLog
var platformState string
smsPlatformKeyRedis := NewSmsPlatformKeyRedis(e.Orm, e.Log)
apiInfo, err := smsPlatformKeyRedis.GetApiInfo(phone.PlatformCode, phone.ApiKey)
if err != nil {
return renewLog, fmt.Errorf("获取短信平台密钥失败 [PlatformCode: %s]: %v", phone.PlatformCode, err)
}
//续期id为空 重新获取
if phone.BillingCycleId == "" {
switch phone.PlatformCode {
case global.SmsPlatformTextVerified:
switch phone.PlatformCode {
case global.SmsPlatformTextVerified:
textVerifiedService := SmsTextVerified{Service: e.Service}
//续期id为空 重新获取
if phone.BillingCycleId == "" {
//可以手动续费
textVerifiedService := SmsTextVerified{Service: e.Service}
detail, code := textVerifiedService.GetRentalDetail(phone.ActivationId, &apiInfo)
if code != statuscode.Success {
@ -406,9 +414,57 @@ func (e *SmsRenewalLog) processPhoneRenewal(phone models.SmsPhone, serviceMap ma
}
phone.BillingCycleId = detail.BillingCycleId
case global.SmsPlatformDaisysms:
//只有自动续费的 用定时服务查询状态
platformState = detail.State
} else {
var billingCycle dto.VerificationRentalDetailResp
if err := utility.Retry(4, time.Millisecond*150, func() error {
var newErr int
billingCycle, newErr = textVerifiedService.GetRentalDetail(phone.ActivationId, &apiInfo)
if newErr != statuscode.Success {
return fmt.Errorf("错误code:%d", newErr)
} else {
return nil
}
}); err != nil {
logger.Errorf("获取短信验证码续费详情失败 [BillingCycleID: %s]: %v", phone.BillingCycleId, err)
return renewLog, fmt.Errorf("获取短信验证码续费详情失败 [BillingCycleID: %s]: %v", phone.BillingCycleId, err)
}
renewLog.ActivationId = billingCycle.ID
platformState = billingCycle.State
}
case global.SmsPlatformDaisysms:
//只有自动续费的 用定时服务查询状态
}
// 已取消、已过期、已逾期 都视为续费失败
if platformState == "renewableRefunded" ||
platformState == "renewableExpired" ||
platformState == "renewableOverdue" {
var remark string
if platformState == "renewableRefunded" {
remark = "已取消"
} else if platformState == "renewableExpired" {
remark = "已过期"
} else if platformState == "renewableOverdue" {
remark = "已逾期"
}
if err := e.Orm.Model(&phone).Where("id =?", phone.Id).
Updates(map[string]interface{}{
"auto_renewal": 2,
"remark": remark,
}).Error; err != nil {
logger.Errorf("更新短信验证码续费状态失败 [PhoneID: %d]: %v", phone.Id, err)
} else {
logger.Errorf("短信验证码续费状态为 %s续费失败", platformState)
}
renewLog.PlatformState = platformState
return renewLog, nil
}
// 获取服务价格
@ -425,6 +481,7 @@ func (e *SmsRenewalLog) processPhoneRenewal(phone models.SmsPhone, serviceMap ma
// 创建续费日志
renewLog = e.createRenewalLog(phone, renewalPrice, tradeOrderNo)
renewLog.PlatformState = platformState
// 执行续费事务
if err := e.executeRenewalTransaction(phone, renewalPrice, &renewLog, &apiInfo, admin); err != nil {
@ -521,6 +578,13 @@ func (e *SmsRenewalLog) executeRenewalTransaction(phone models.SmsPhone, price d
if code == statuscode.NothingToRenew {
if renewCode := textVerifiedService.Renew(phone.ActivationId, true, apiInfo); renewCode == statuscode.Success {
code = textVerifiedService.ManualRenewal(phone.BillingCycleId, apiInfo)
if code == statuscode.Success {
//再取消自动续费
if renewCode := textVerifiedService.Renew(phone.ActivationId, false, apiInfo); renewCode != statuscode.Success {
logger.Errorf("取消自动续费失败 [ActivationID: %s]: %v", phone.ActivationId, renewCode)
}
}
}
}

View File

@ -403,6 +403,7 @@ func (e *SmsTextVerified) GetNumberAndWakeUp(tye int, serviceCode string, price
// period 时长(月=30天)
func (e *SmsTextVerified) GetNumberForApi(typ int, serviceCode string, price decimal.Decimal, period int, apiInfo *dto.SmsPlatformKeyQueueDto) (dto.SmsPhoneGetPhoneResp, int) {
//这个平台的所有号码都是美国号码 默认国家代码都是 +1
var bytes []byte
var err error
var createResp dto.TextVerifiedResp
var result dto.SmsPhoneGetPhoneResp
@ -412,7 +413,7 @@ func (e *SmsTextVerified) GetNumberForApi(typ int, serviceCode string, price dec
createResp, code = e.CreateVerification(serviceCode, apiInfo)
if code == statuscode.Success && createResp.Href != "" {
bytes, err := e.doRequest(&createResp, apiInfo)
bytes, err = e.doRequest(&createResp, apiInfo)
if err != nil {
e.Log.Errorf("短效号码 获取号码失败:%v", e)
@ -421,7 +422,7 @@ func (e *SmsTextVerified) GetNumberForApi(typ int, serviceCode string, price dec
resp := dto.VerificationDTO{}
if err := sonic.Unmarshal(bytes, &resp); err != nil {
if err = sonic.Unmarshal(bytes, &resp); err != nil {
e.Log.Errorf("短效号码 获取号码失败:%v", e)
return result, 0
}
@ -443,15 +444,28 @@ func (e *SmsTextVerified) GetNumberForApi(typ int, serviceCode string, price dec
if err == nil && createResp.Href != "" {
saleId := getIdByUrl(createResp.Href)
bytes, err := e.doRequest(&createResp, apiInfo)
if err != nil {
if err = utility.Retry(6, time.Millisecond*150, func() error {
newBytes, newErr := e.doRequest(&createResp, apiInfo)
if newErr != nil {
return fmt.Errorf("获取长效号码失败:%v", newErr)
}
bytes = newBytes
return nil
}); err != nil {
e.Log.Errorf("通过销售id [%s] 获取号码失败:%v", saleId, err)
}
if len(bytes) == 0 {
e.Log.Errorf("获取长效号码失败,没有返回值")
return result, statuscode.ServerError
}
resp := dto.SaleResponse{}
if err := sonic.Unmarshal(bytes, &resp); err != nil {
if err = sonic.Unmarshal(bytes, &resp); err != nil {
e.Log.Errorf("反序列化失败:%v", err)
return result, statuscode.ServerError
@ -461,7 +475,7 @@ func (e *SmsTextVerified) GetNumberForApi(typ int, serviceCode string, price dec
for _, v := range resp.Reservations {
if v.ID != "" {
result.Id = v.ID
continue
break
}
}
@ -470,20 +484,36 @@ func (e *SmsTextVerified) GetNumberForApi(typ int, serviceCode string, price dec
return result, statuscode.ServerError
}
detail, code := e.GetRentalDetail(result.Id, apiInfo)
var detail dto.VerificationRentalDetailResp
if code != statuscode.Success {
return result, code
//带重试的获取详情
if err = utility.Retry(6, time.Millisecond*150, func() error {
dDetail, dCode := e.GetRentalDetail(result.Id, apiInfo)
if dCode != statuscode.Success {
return fmt.Errorf("code :%d", dCode)
}
detail = dDetail
return nil
}); err != nil {
e.Log.Errorf("获取长效号码失败:%v", err)
return result, statuscode.ServerError
}
result.Phone = "1" + detail.Number
result.BillingCycleId = detail.BillingCycleId
endTime := resp.UpdatedAt.AddDate(0, 0, 30)
result.EndAt = &(endTime) // 30天后过期
if detail.ID == "" {
e.Log.Errorf("获取长效号码失败,没有返回值")
return result, statuscode.ServerError
} else {
result.Phone = "1" + detail.Number
result.BillingCycleId = detail.BillingCycleId
endTime := resp.UpdatedAt.AddDate(0, 0, 30)
result.EndAt = &(endTime) // 30天后过期
}
return result, statuscode.Success
}
} else if err == nil && createResp.Href == "" {
e.Log.Errorf("获取长效号码失败,没有返回值")
return result, statuscode.ServerError
@ -499,10 +529,6 @@ func (e *SmsTextVerified) GetNumberForApi(typ int, serviceCode string, price dec
return result, statuscode.SmsPlatformUnavailable
}
if err != nil {
return result, statuscode.ServerError
}
return result, statuscode.Success
}

View File

@ -2,6 +2,7 @@ package service
import (
"go-admin/common/global"
"go-admin/common/statuscode"
"testing"
"github.com/go-admin-team/go-admin-core/logger"
@ -82,3 +83,100 @@ func TestInitSmsLogs(t *testing.T) {
t.Log("InitSmsLogs succeeded")
}
}
func TestGetRentalDetail(t *testing.T) {
db := initSetting()
s := SmsTextVerified{}
s.Orm = db
s.Log = logger.NewHelper(logger.DefaultLogger)
ids := []string{
"lr_01K5YP0KD77813BGHEVNBHFSV4",
"lr_01K7SKT1X3DM8VW4C131KFJKKP",
"lr_01K7SRS4JZEWK7K479YG74WSMW",
"lr_01K7SSPHC13ZY6DPD8M3X2M47H",
"lr_01K7SV8DP7DMBVTH599DA3AE61",
"lr_01K7W84KPA3M31KWZ06GXE1YGN",
"lr_01K7W8VXEA15GBPXNWQ7EP3MTN",
"lr_01K7WBYG9G25VAX7GJQHG8AXV2",
"lr_01K7X5ZGRE9YMWGPTA9EEXNY6F",
"lr_01K7YG5FVJM03WPRNA8R1RYDHQ",
"lr_01K81MR0DJXF4QAQKR03APT1MR",
"lr_01K81Q1BZ05EW7VECPB4016CJH",
"lr_01K81YRE55T41J8CNT8SYPVRQB",
"lr_01K849VP722Q67CTS69WNPF3CV",
"lr_01K849W7N74ATK14DFBGPRPFT5",
"lr_01K86KYAPFRM776K9H63HC142M",
"lr_01K5YP0KD77813BGHEVNBHFSV4",
"lr_01K88R5F04HXDM3T7TA6Y101FV",
"lr_01K88S2SQ9R0AKM37NX6N9Q3K4",
"lr_01K88S3XBP3VWTBCQ51R1NPPXM",
"lr_01K88SG5FGYCQSH2K0CDRGGXJV",
"lr_01K88SR36PQ20SDCAZT45HZXH6",
"lr_01K88SVWPP1AQDYG8MJNEG12WQ",
"lr_01K8BM8W13TF487G1QR0N3DPXQ",
"lr_01K8BQV5V7HD38NBBMYNQ46ATE",
"lr_01K8BR1TAMPNJ2NZ8A5YN4XRP7",
"lr_01K8BRB8TX1NTQ8F99Q8VJEWQY",
"lr_01K8BSNK4PB5DT4E5BRQP9FT89",
"lr_01K8BT9W2HGVJHB09WN5Q8ZAFW",
"lr_01K8CKGF1SBPJNHQR11Q8TKG34",
"lr_01K8CMMW8EGHD8GZTYARMZ15YC",
"lr_01K8E4CVMPMVF61NZ7J0PN92VF",
"lr_01K8E6Z10C463TMGZ5WNQE151B",
"lr_01K8EJAH4BKTAF6DGSEHWB658S",
"lr_01K8EJGRHVJBTCR4FT5QVD01BK",
"lr_01K8EPKWZDAW6C9BFKXZJ9ZXXS",
"lr_01K8EPMNRY3DKAWNT5AHCQV5FP",
"lr_01K8KZMPDE39S2AKKKY215PPQC",
"lr_01K8MAPDCNMR2WVR8QKQ1DBN8Z",
"lr_01K8RBQJYEGS28AMW57PTKD2RQ",
"lr_01K8RPCC5GAQ6YD5WK8P0FVEN6",
"lr_01K8RR7F4H4RRZE5TSES6RXD08",
"lr_01K8RRQ7WBTY4AC4Q1WP1KAFNN",
"lr_01K8S5F3HPHTXV17W68P4M5CK0",
"lr_01K8S5G5JV9A69WH4SNM79AEYJ",
"lr_01K907JVTN6C9QC17AN97RM8TC",
"lr_01K907Z508W78B2PYZZ8E3PWJX",
"lr_01K909WET271Z8JEYRKGEJJBXG",
"lr_01K90C2V8RQ06914J6VBEV9MV6",
"lr_01K90G3WCFB8RS8SWNS73KW189",
"lr_01K90PJTX2GRXZMTZ1ZTN7SEEB",
"lr_01K92FTF3WJRC3YVYEK62Y0PV2",
"lr_01K95G9WHTX8ASMT1N6RVJAW0Z",
"lr_01K95GK2762Z6V9WFM43VVMRE8",
"lr_01K98WR5PGNYVDYWYK8RMHXM1D",
"lr_01K9ANC9ANW2TM54J4EM9TM7H0",
"lr_01K9AW71BGJAV6VQP7REC023K4",
"lr_01K9B0MA8A7E3KD75DA2JQZ8MA",
"lr_01K9B0YX9MBB22XY7NAQHJ6W68",
"lr_01K9DABMP1XJP82HHE114B0C5H",
"lr_01K9DCY25FRM0V85XRW7QJ5MVQ",
"lr_01K9QJD4RQ5DK95WV55PVMWYRK",
"lr_01K9SQVZ0QBXAT4MKZG0ZFV8NQ",
"lr_01K9TQ9GYP29X2Q7YPN5BH79ZY",
"lr_01K9X7ABQZ1QGVXYXMY00PNP4F",
"lr_01K9Z50GG4E50CV6X7HFF6H671",
"lr_01K9Z5GZEF8Q5Z3TWMFMKT8ME1",
"lr_01K9ZBADB7AASXZF0FARN94FAN",
"lr_01K9ZBPTVAER3EHC20YJ3BWJBB",
"lr_01K9ZC5V6E29R2W798FJTCHQZG",
}
smsPlatformKeyRedis := NewSmsPlatformKeyRedis(s.Orm, s.Log)
apiInfo, err := smsPlatformKeyRedis.GetApiInfo(global.SmsPlatformTextVerified, "ZQ0swXnsaPpeGdwa3c7gT9U9I1Oh9WoDHx0amuYovvaHuqd5u6B4NBBUSUBjR")
if err != nil {
s.Log.Errorf("获取API信息失败: %v", err)
t.Error("获取API信息失败", err)
}
for _, item := range ids {
rentalDetail, errCode := s.GetRentalDetail(item, &apiInfo)
if errCode != statuscode.Success {
t.Errorf("GetRentalDetail failed: %v", errCode)
} else {
t.Logf("activationId:%s 状态:%s", rentalDetail.ID, rentalDetail.State)
}
}
}

View File

@ -10,16 +10,17 @@ import (
// 字典 key 可以配置到 自动任务 调用目标 中;
func InitJob() {
jobList = map[string]JobExec{
"ExamplesOne": ExamplesOne{},
"TrxPaymentJob": TrxPaymentJob{},
"CliProxyTrafficsJob": CliProxyJob{},
"RenewalJob": RenewalJob{}, //长效ip定时续期
"ExpireProxyJob": ExpireProxyJob{}, //定时过期代理
"CleanExpiredOrderJob": CleanExpiredOrderJob{}, //清理过期订单
"SmsJob": SmsJob{}, //短信定时查询验证码
"SmsRenewalJob": SmsRenewalJob{}, //短信定时自动续期
"AutoDeleteJob": AutoDeleteJob{}, //定时删除任务
"SmsPriceJob": SmsPriceJob{}, // 短信价格定时同步
"ExamplesOne": ExamplesOne{},
"TrxPaymentJob": TrxPaymentJob{},
"CliProxyTrafficsJob": CliProxyJob{},
"RenewalJob": RenewalJob{}, //长效ip定时续期
"ExpireProxyJob": ExpireProxyJob{}, //定时过期代理
"CleanExpiredOrderJob": CleanExpiredOrderJob{}, //清理过期订单
"SmsJob": SmsJob{}, //短信定时查询验证码
"SmsRenewalJob": SmsRenewalJob{}, //短信定时自动续期
"AutoDeleteJob": AutoDeleteJob{}, //定时删除任务
"SmsPriceJob": SmsPriceJob{}, // 短信价格定时同步
"SmsCancelPlatformAuto": SmsCancelPlatformAuto{}, // 短信定时取消自动续期
// ...
}
}

View File

@ -8,6 +8,7 @@ import (
type SmsJob struct{}
type SmsPriceJob struct{}
type SmsCancelPlatformAuto struct{}
// 定时查询结果
func (j SmsJob) Exec(args interface{}) error {
@ -31,3 +32,11 @@ func (j SmsPriceJob) Exec(args interface{}) error {
return nil
}
func (j SmsCancelPlatformAuto) Exec(args interface{}) error {
phoneService := service.SmsPhone{}
phoneService.Orm = GetDb()
phoneService.Log = logger.NewHelper(logger.DefaultLogger)
return phoneService.SetManualRenewal()
}

View File

@ -31,7 +31,7 @@ settings:
# sqlserver: sqlserver://用户名:密码@地址?database=数据库名
driver: mysql
# 数据库连接字符串 mysql 缺省信息 charset=utf8&parseTime=True&loc=Local&timeout=1000ms
source: root:123456@tcp(127.0.0.1:3306)/proxy_server?charset=utf8&parseTime=True&loc=Local&timeout=1000ms
source: root:123456@tcp(127.0.0.1:3306)/proxy_server_prod?charset=utf8&parseTime=True&loc=Local&timeout=1000ms
# databases:
# 'locaohost:8000':
# driver: mysql

View File

@ -5,6 +5,7 @@ import (
"runtime"
"runtime/debug"
"strings"
"time"
"github.com/go-admin-team/go-admin-core/logger"
)
@ -42,3 +43,16 @@ func SafeGoParam[T any](fn func(T), param T) {
fn(param) // 执行传入的函数
}()
}
// Retry 重试执行函数,直到成功或达到最大重试次数
func Retry(max int, delay time.Duration, fn func() error) error {
var err error
for i := 0; i < max; i++ {
err = fn()
if err == nil {
return nil
}
time.Sleep(delay * time.Duration(i+1))
}
return err
}