1、唤醒失败不影响获取

This commit is contained in:
2025-10-08 03:51:06 +08:00
parent cbefd85f25
commit 9c826f4966
10 changed files with 413 additions and 26 deletions

View File

@ -12,6 +12,8 @@ import (
"go-admin/app/admin/service"
"go-admin/app/admin/service/dto"
"go-admin/common/actions"
"go-admin/common/middleware"
"go-admin/common/statuscode"
)
type SmsAbnormalNumber struct {
@ -212,3 +214,101 @@ func (e SmsAbnormalNumber) SyncState(c *gin.Context) {
}
e.OK(nil, "同步成功")
}
// 重用号码
func (e SmsAbnormalNumber) ReUseAbnormalNumber(c *gin.Context) {
req := dto.ReUseAbnormalNumberReq{}
s := service.SmsAbnormalNumber{}
err := e.MakeContext(c).
MakeOrm().
Bind(&req).
MakeService(&s.Service).
Errors
if err != nil {
e.Logger.Error(err)
e.Error(500, err, err.Error())
return
}
lang := "en"
userId, err := middleware.GetUserIdByApiKey(c)
if err != nil {
e.Logger.Error(err)
e.Error(statuscode.Unauthorized, nil, statuscode.GetMsg(statuscode.Unauthorized, lang))
return
}
resp, code := s.ReUseAbnormalNumber(userId, &req)
if code != statuscode.Success {
e.Error(code, nil, statuscode.GetMsg(code, lang))
return
}
e.OK(resp, "success")
}
// 清空数据
func (e SmsAbnormalNumber) Clean(c *gin.Context) {
s := service.SmsAbnormalNumber{}
err := e.MakeContext(c).
MakeOrm().
MakeService(&s.Service).
Errors
if err != nil {
e.Logger.Error(err)
e.Error(500, err, err.Error())
return
}
lang := "en"
if err != nil {
e.Logger.Error(err)
e.Error(statuscode.Unauthorized, nil, statuscode.GetMsg(statuscode.Unauthorized, lang))
return
}
err = s.Clean()
if err != nil {
e.Error(500, err, fmt.Sprintf("清除数据失败\r\n失败信息 %s", err.Error()))
return
}
e.OK(nil, "success")
}
// 批量取消
func (e SmsAbnormalNumber) BatchCancel(c *gin.Context) {
req := dto.BatchCancelAbnormalNumberReq{}
s := service.SmsAbnormalNumber{}
err := e.MakeContext(c).
MakeOrm().
Bind(&req).
MakeService(&s.Service).
Errors
if err != nil {
e.Logger.Error(err)
e.Error(500, err, err.Error())
return
}
lang := "en"
if err != nil {
e.Logger.Error(err)
e.Error(statuscode.Unauthorized, nil, statuscode.GetMsg(statuscode.Unauthorized, lang))
return
}
err = s.BatchCancel(&req)
if err != nil {
e.Error(500, err, fmt.Sprintf("批量取消异常号码统计失败,\r\n失败信息 %s", err.Error()))
return
}
e.OK(nil, "success")
}

View File

@ -2,6 +2,7 @@ package apis
import (
"fmt"
"strings"
"github.com/gin-gonic/gin"
"github.com/go-admin-team/go-admin-core/sdk/api"
@ -747,3 +748,35 @@ func (e SmsPhone) OpenWeakUp(c *gin.Context) {
e.OK(resp, "success")
}
// 手动续费
func (e SmsPhone) ManualRenewal(c *gin.Context) {
req := dto.ManualRenewalReq{}
s := service.SmsPhone{}
err := e.MakeContext(c).
MakeOrm().
Bind(&req).
MakeService(&s.Service).
Errors
if err != nil {
e.Logger.Error(err)
e.Error(500, err, err.Error())
return
}
errorIds, err := s.ManualRenewal(req.ActivationIds)
if err != nil {
e.Logger.Error(err)
e.Error(500, err, err.Error())
return
}
var data string
if len(errorIds) > 0 {
data = strings.Join(errorIds, ",")
}
e.OK(data, "success")
}

View File

@ -1,23 +1,26 @@
package models
import (
"go-admin/common/models"
"time"
)
type SmsAbnormalNumber struct {
models.Model
models.Model
Account string `json:"account" gorm:"type:varchar(255);comment:账号"`
PlatformCode string `json:"platformCode" gorm:"type:varchar(20);comment:平台code"`
Phone string `json:"phone" gorm:"type:varchar(30);comment:电话号码"`
models.ModelTime
models.ControlBy
Account string `json:"account" gorm:"type:varchar(255);comment:账号"`
PlatformCode string `json:"platformCode" gorm:"type:varchar(20);comment:平台code"`
ApiKey string `json:"apiKey" gorm:"type:varchar(255);comment:api key"`
Phone string `json:"phone" gorm:"type:varchar(30);comment:电话号码"`
ActivationId string `json:"activationId" gorm:"type:varchar(255);comment:激活id"`
ReceivedTime time.Time `json:"receivedTime" gorm:"type:datetime;comment:接收号码时间"`
Reused int `json:"reused" gorm:"type:tinyint(1);default:2;comment:是否重用 1-是 2-否"`
models.ModelTime
models.ControlBy
}
func (SmsAbnormalNumber) TableName() string {
return "sms_abnormal_number"
return "sms_abnormal_number"
}
func (e *SmsAbnormalNumber) Generate() models.ActiveRecord {

View File

@ -16,14 +16,22 @@ func init() {
// registerSmsAbnormalNumberRouter
func registerSmsAbnormalNumberRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) {
api := apis.SmsAbnormalNumber{}
r := v1.Group("/sms-abnormal-number").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole())
r := v1.Group("/sms-abnormal-number").
Use(authMiddleware.MiddlewareFunc()).
Use(middleware.AuthCheckRole())
{
r.GET("", actions.PermissionAction(), api.GetPage)
r.GET("/:id", actions.PermissionAction(), api.Get)
r.POST("", api.Insert)
r.PUT("/:id", actions.PermissionAction(), api.Update)
r.DELETE("", api.Delete)
r.DELETE("clear", actions.PermissionAction(), api.Clean)
r.PUT("/batch-cancel", actions.PermissionAction(), api.BatchCancel)
}
r.POST("/sync-state", api.SyncState) //同步差异
r1 := v1.Group("/sms-abnormal-number")
{
r1.POST("/sync-state", api.SyncState) //同步差异
r1.PUT("/reuse", api.ReUseAbnormalNumber) //重用号码
}
}

View File

@ -45,6 +45,7 @@ func registerSmsPhoneRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddl
r.GET("/:id", actions.PermissionAction(), api.Get)
r.POST("", api.Insert)
r.PUT("/:id", actions.PermissionAction(), api.Update)
r.PUT("/manual-renewal", api.ManualRenewal)
r.DELETE("", api.Delete)
}

View File

@ -221,3 +221,18 @@ type OpenWakeUpReq struct {
PlatformCode string `json:"platformCode" comment:"短信平台"`
ActivationId string `json:"activationId" comment:"平台id"`
}
type ReUseAbnormalNumberReq struct {
BeginTime int64 `json:"beginTime" comment:"开始时间"`
PlatformCode string `json:"platformCode" comment:"短信平台"`
ServiceCode string `json:"serviceCode" comment:"服务code"`
}
// 批量取消
type BatchCancelAbnormalNumberReq struct {
Ids []int `json:"ids" comment:"短信号码id"`
}
type ManualRenewalReq struct {
ActivationIds []string `json:"activationIds" comment:"激活码id"`
}

View File

@ -131,6 +131,7 @@ type VerificationDTO struct {
Reuse ReuseInfo `json:"reuse"`
Sale TextVerifiedResp `json:"sale"`
ServiceName string `json:"serviceName"`
ApiKey string `json:"apiKey"`
// State 表示验证或服务的当前状态。
// 状态是以下枚举值之一,代表了从创建到完成或取消的生命周期:
// * verificationPending: 正在等待验证码。

View File

@ -5,6 +5,7 @@ import (
"time"
"github.com/go-admin-team/go-admin-core/sdk/service"
"github.com/shopspring/decimal"
"gorm.io/gorm"
"go-admin/app/admin/models"
@ -12,12 +13,193 @@ import (
"go-admin/common/actions"
cDto "go-admin/common/dto"
"go-admin/common/global"
"go-admin/common/statuscode"
"go-admin/utils/utility"
)
type SmsAbnormalNumber struct {
service.Service
}
func (e *SmsAbnormalNumber) Clean() error {
if err := e.Orm.Exec("truncate table sms_abnormal_number").Error; err != nil {
return err
}
return nil
}
// 批量取消
func (e *SmsAbnormalNumber) BatchCancel(req *dto.BatchCancelAbnormalNumberReq) error {
var datas []models.SmsAbnormalNumber
if err := e.Orm.Model(&models.SmsAbnormalNumber{}).Where("id in ?", req.Ids).Find(&datas).Error; err != nil {
return err
}
utility.SafeGo(func() {
CancelFunc(e, datas)
})
return nil
}
func CancelFunc(e *SmsAbnormalNumber, datas []models.SmsAbnormalNumber) {
phoneService := SmsPhone{Service: e.Service}
for _, item := range datas {
phoneService.ChangeAutoRenewManage(item.PlatformCode, item.ApiKey, item.ActivationId, false)
time.Sleep(200 * time.Millisecond)
}
}
// 重用号码
func (e *SmsAbnormalNumber) ReUseAbnormalNumber(userId int, req *dto.ReUseAbnormalNumberReq) (dto.OpenGetNumberResp, int) {
resp := dto.OpenGetNumberResp{}
balanceService := MemberBalance{Service: e.Service}
config := dto.GetSysConfigByKEYForServiceResp{}
configReq := dto.SysConfigByKeyReq{}
configService := SysConfig{Service: e.Service}
smsServices := SmsServices{Service: e.Service}
var price decimal.Decimal
serviceItem, err := smsServices.GetByCode(req.PlatformCode, req.ServiceCode)
if err != nil {
e.Log.Errorf("短信服务报错:%v", err)
return resp, statuscode.ServerError
}
if serviceItem.Code == "" {
e.Log.Error("短信服务不存在")
return resp, statuscode.ServerError
}
switch {
case req.PlatformCode == global.SmsPlatformDaisysms:
price = serviceItem.LongPrice
configReq.ConfigKey = "long_number_premium_daisysms"
case req.PlatformCode == global.SmsPlatformTextVerified:
price = serviceItem.LongPrice
configReq.ConfigKey = "long_number_premium_textverified"
}
err = configService.GetWithKey(&configReq, &config)
if err != nil {
return resp, statuscode.ServerError
}
if config.ConfigValue == "" {
e.Log.Errorf("短期长期租赁浮动百分比不能为空")
return resp, statuscode.ServerError
}
percent, err := decimal.NewFromString(config.ConfigValue)
if err != nil {
e.Log.Errorf("短期或长期租赁费用格式错误")
return resp, statuscode.ServerError
}
price = price.Mul(decimal.NewFromInt(100).Add(percent)).Div(decimal.NewFromInt(100))
if price.Cmp(decimal.Zero) <= 0 {
e.Log.Errorf("短期或长期租赁费用不能小于等于0")
return resp, statuscode.ServerError
}
balance := balanceService.GetBalance(userId)
if balance.LessThan(price) {
e.Log.Error("余额不足")
return resp, statuscode.BalanceNotEnough
}
abnomarlNumber, err := e.GetReused(req.PlatformCode, req.BeginTime)
if err != nil {
return resp, statuscode.ServerError
}
expireTime := abnomarlNumber.ReceivedTime.AddDate(0, 0, 30)
startTime := time.Now()
endTime := startTime
smsPhone := models.SmsPhone{}
smsPhone.PlatformCode = req.PlatformCode
smsPhone.Phone = abnomarlNumber.Phone
smsPhone.UserId = userId
smsPhone.Service = serviceItem.Name
smsPhone.ServiceCode = req.ServiceCode
smsPhone.Type = 1
smsPhone.Period = 1
smsPhone.ActivationId = abnomarlNumber.ActivationId
smsPhone.MessageId = abnomarlNumber.ActivationId
smsPhone.NewActivationId = abnomarlNumber.ActivationId
smsPhone.Actived = 1
smsPhone.StartTime = &startTime
smsPhone.EndTime = &endTime
// smsPhone.ApiKey = apiInfo.ApiKey
smsPhone.Price = price
smsPhone.ExpireTime = &expireTime
//平台不允许取消
if req.PlatformCode == global.SmsPlatformTextVerified {
smsPhone.Actived = 2
}
smsPhone.Status = 1
err = e.Orm.Transaction(func(tx *gorm.DB) error {
if err1 := tx.Save(&smsPhone).Error; err1 != nil {
e.Log.Errorf("获取手机号失败", err1)
return err1
}
if err1 := tx.Exec("UPDATE member_balance SET balance = balance -? WHERE user_id =?", price, userId).Error; err1 != nil {
e.Log.Errorf("更新余额失败", err1)
return err1
}
return nil
})
if err != nil {
return resp, statuscode.ServerError
}
resp.ActivationId = smsPhone.NewActivationId
resp.ExpireTime = smsPhone.ExpireTime
resp.Number = smsPhone.Phone
resp.StartTime = smsPhone.StartTime
resp.EndTime = smsPhone.EndTime
return resp, statuscode.Success
}
// 获取重用
func (e *SmsAbnormalNumber) GetReused(platformCode string, beginTime int64) (models.SmsAbnormalNumber, error) {
var smsAbnormalNumber models.SmsAbnormalNumber
query := e.Orm.Model(&smsAbnormalNumber).
Where("platform_code = ? AND reused = ?", platformCode, 2)
if beginTime > 0 {
query.Where("received_time >= ?", time.Unix(beginTime, 0))
}
err := query.Order("received_time asc").First(&smsAbnormalNumber).Error
if err != nil {
e.Log.Errorf("SmsAbnormalNumberService GetReused error:%s \r\n", err)
return smsAbnormalNumber, err
}
if err := e.Orm.Model(&models.SmsAbnormalNumber{}).
Where("id = ?", smsAbnormalNumber.Id).
Update("reused", 1).Error; err != nil {
e.Log.Errorf("SmsAbnormalNumberService GetReused error:%s \r\n", err)
return smsAbnormalNumber, err
}
return smsAbnormalNumber, nil
}
// GetPage 获取SmsAbnormalNumber列表
func (e *SmsAbnormalNumber) GetPage(c *dto.SmsAbnormalNumberGetPageReq, p *actions.DataPermission, list *[]models.SmsAbnormalNumber, count *int64) error {
var err error
@ -151,7 +333,6 @@ func (e *SmsAbnormalNumber) SyncState() error {
return nil
})
if err != nil {
e.Log.Errorf("同步后保存差异数据失败 %v", err)
}
@ -187,6 +368,9 @@ func (e *SmsAbnormalNumber) findMissingNumbers(platformNumbers map[string][]dto.
PlatformCode: global.SmsPlatformTextVerified,
Account: account,
Phone: v.Number,
ActivationId: v.ID,
ReceivedTime: v.CreatedAt,
ApiKey: v.ApiKey,
})
}
}

View File

@ -203,7 +203,7 @@ func (e *SmsPhone) WeakUpManage(req *models.SmsPhone, apiInfo *dto.SmsPlatformKe
return strconv.Itoa(id), "", nil, nil, code
case global.SmsPlatformTextVerified:
service := SmsTextVerified{Service: e.Service}
resp, code := service.getExtraActivation(req.NewActivationId, apiInfo)
resp, code := service.getExtraActivation(req.NewActivationId, apiInfo, 0)
return resp.ReservationId, resp.Id, resp.UsageWindowStart, resp.UsageWindowEnd, code
default:
return "", "", nil, nil, statuscode.SmsPlatformUnavailable
@ -594,6 +594,39 @@ func (e *SmsPhone) AutoRenewal() error {
return nil
}
// 手动续费
func (e *SmsPhone) ManualRenewal(activationIds []string) ([]string, error) {
if len(activationIds) == 0 {
return nil, errors.New("activationIds 不能为空")
}
var errorIds []string
var expiredPhones []models.SmsPhone
// 查询所有到期号码
if err := e.Orm.Model(&models.SmsPhone{}).
Where("activation_id IN ? and expire_time < ?", activationIds, time.Now()).
Find(&expiredPhones).Error; err != nil {
return nil, err
}
// 获取服务配置映射
serviceMap, premiumMap, err := e.getRenewalConfigs()
if err != nil {
return nil, err
}
// 处理每个到期号码
for _, phone := range expiredPhones {
if err := e.processPhoneRenewal(phone, serviceMap, premiumMap); err != nil {
e.Log.Errorf("处理号码续费失败 [PhoneID: %d]: %v", phone.Id, err)
errorIds = append(errorIds, fmt.Sprintf("处理号码续费失败 [PhoneID: %d]: %v", phone.Id, err))
continue
}
}
return errorIds, nil
}
// getExpiredPhones 获取即将到期的自动续费号码
func (e *SmsPhone) getExpiredPhones() ([]models.SmsPhone, error) {
var phones []models.SmsPhone
@ -707,8 +740,7 @@ func (e *SmsPhone) executeRenewalTransaction(phone models.SmsPhone, price decima
return nil
})
if err.Error() == "余额不足" {
if err != nil && err.Error() == "余额不足" {
return e.handleInsufficientBalance(phone)
}

View File

@ -372,16 +372,19 @@ func (e *SmsTextVerified) GetNumberAndWakeUp(tye int, serviceCode string, price
return resp, code
}
wakeUpResp, code := e.getExtraActivation(resp.Id, apiInfo)
wakeUpResp, code := e.getExtraActivation(resp.Id, apiInfo, 0)
if code != statuscode.Success {
return resp, code
//唤醒失败不退出
now := time.Now()
resp.StartTime = &now
resp.EndTime = &now
} else {
resp.MessageId = wakeUpResp.Id
resp.StartTime = wakeUpResp.UsageWindowStart
resp.EndTime = wakeUpResp.UsageWindowEnd
}
resp.MessageId = wakeUpResp.Id
resp.StartTime = wakeUpResp.UsageWindowStart
resp.EndTime = wakeUpResp.UsageWindowEnd
return resp, code
}
@ -579,7 +582,7 @@ func (e *SmsTextVerified) GetRentalDetail(id string, apiInfo *dto.SmsPlatformKey
// 唤醒号码
// returns activationId,messageId,startTime可用开始时间,endTime可用结束时间, code,
func (e SmsTextVerified) getExtraActivation(id string, apiInfo *dto.SmsPlatformKeyQueueDto) (dto.TextVerifiedWakeUpResp, int) {
func (e SmsTextVerified) getExtraActivation(id string, apiInfo *dto.SmsPlatformKeyQueueDto, retry int) (dto.TextVerifiedWakeUpResp, int) {
client, code := e.GetTextVerifiedAuthClient(apiInfo)
result := dto.TextVerifiedWakeUpResp{}
@ -598,8 +601,13 @@ func (e SmsTextVerified) getExtraActivation(id string, apiInfo *dto.SmsPlatformK
status, err := client.Post(wakeUp, req, headers, &resp)
if err != nil {
e.Log.Errorf("唤醒号码失败 id:%s error: %v", id, err)
return result, statuscode.ServerError
if strings.Contains(err.Error(), "Line is currently busy. Please try this line again later") && retry < 3 {
time.Sleep(300 * time.Millisecond)
return e.getExtraActivation(id, apiInfo, retry+1)
} else {
e.Log.Errorf("唤醒号码失败 id:%s error: %v", id, err)
return result, statuscode.ServerError
}
} else if status == http.StatusCreated || status == http.StatusOK {
if resp.Method != "" && resp.Href != "" {
bytes, err := e.doRequest(&resp, apiInfo)
@ -1030,6 +1038,7 @@ func (e *SmsTextVerified) GetNumbers(apiInfo *dto.SmsPlatformKeyQueueDto) ([]dto
// 添加第一页数据
for _, item := range resp.Data {
item.Number = "1" + item.Number
item.ApiKey = apiInfo.ApiKey
result = append(result, item)
}
@ -1074,6 +1083,7 @@ func (e *SmsTextVerified) fetchPaginatedData(nextLink *dto.TextVerifiedResp, api
for _, item := range nextResp.Data {
item.Number = "1" + item.Number
item.ApiKey = apiInfo.ApiKey
result = append(result, item)
}