Compare commits
9 Commits
f73639a96c
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 837900462a | |||
| 1a5cffa2cc | |||
| 73c6cf9844 | |||
| 996cd27fcb | |||
| 9c826f4966 | |||
| cbefd85f25 | |||
| baa6552994 | |||
| 0f084e1461 | |||
| ab61808bf1 |
314
app/admin/apis/sms_abnormal_number.go
Normal file
314
app/admin/apis/sms_abnormal_number.go
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
package apis
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/go-admin-team/go-admin-core/sdk/api"
|
||||||
|
"github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth/user"
|
||||||
|
_ "github.com/go-admin-team/go-admin-core/sdk/pkg/response"
|
||||||
|
|
||||||
|
"go-admin/app/admin/models"
|
||||||
|
"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 {
|
||||||
|
api.Api
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPage 获取异常号码统计列表
|
||||||
|
// @Summary 获取异常号码统计列表
|
||||||
|
// @Description 获取异常号码统计列表
|
||||||
|
// @Tags 异常号码统计
|
||||||
|
// @Param platformCode query string false "平台code"
|
||||||
|
// @Param phone query string false "电话号码"
|
||||||
|
// @Param pageSize query int false "页条数"
|
||||||
|
// @Param pageIndex query int false "页码"
|
||||||
|
// @Success 200 {object} response.Response{data=response.Page{list=[]models.SmsAbnormalNumber}} "{"code": 200, "data": [...]}"
|
||||||
|
// @Router /api/v1/sms-abnormal-number [get]
|
||||||
|
// @Security Bearer
|
||||||
|
func (e SmsAbnormalNumber) GetPage(c *gin.Context) {
|
||||||
|
req := dto.SmsAbnormalNumberGetPageReq{}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
p := actions.GetPermissionFromContext(c)
|
||||||
|
list := make([]models.SmsAbnormalNumber, 0)
|
||||||
|
var count int64
|
||||||
|
|
||||||
|
err = s.GetPage(&req, p, &list, &count)
|
||||||
|
if err != nil {
|
||||||
|
e.Error(500, err, fmt.Sprintf("获取异常号码统计失败,\r\n失败信息 %s", err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get 获取异常号码统计
|
||||||
|
// @Summary 获取异常号码统计
|
||||||
|
// @Description 获取异常号码统计
|
||||||
|
// @Tags 异常号码统计
|
||||||
|
// @Param id path int false "id"
|
||||||
|
// @Success 200 {object} response.Response{data=models.SmsAbnormalNumber} "{"code": 200, "data": [...]}"
|
||||||
|
// @Router /api/v1/sms-abnormal-number/{id} [get]
|
||||||
|
// @Security Bearer
|
||||||
|
func (e SmsAbnormalNumber) Get(c *gin.Context) {
|
||||||
|
req := dto.SmsAbnormalNumberGetReq{}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
var object models.SmsAbnormalNumber
|
||||||
|
|
||||||
|
p := actions.GetPermissionFromContext(c)
|
||||||
|
err = s.Get(&req, p, &object)
|
||||||
|
if err != nil {
|
||||||
|
e.Error(500, err, fmt.Sprintf("获取异常号码统计失败,\r\n失败信息 %s", err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
e.OK(object, "查询成功")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert 创建异常号码统计
|
||||||
|
// @Summary 创建异常号码统计
|
||||||
|
// @Description 创建异常号码统计
|
||||||
|
// @Tags 异常号码统计
|
||||||
|
// @Accept application/json
|
||||||
|
// @Product application/json
|
||||||
|
// @Param data body dto.SmsAbnormalNumberInsertReq true "data"
|
||||||
|
// @Success 200 {object} response.Response "{"code": 200, "message": "添加成功"}"
|
||||||
|
// @Router /api/v1/sms-abnormal-number [post]
|
||||||
|
// @Security Bearer
|
||||||
|
func (e SmsAbnormalNumber) Insert(c *gin.Context) {
|
||||||
|
req := dto.SmsAbnormalNumberInsertReq{}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
// 设置创建人
|
||||||
|
req.SetCreateBy(user.GetUserId(c))
|
||||||
|
|
||||||
|
err = s.Insert(&req)
|
||||||
|
if err != nil {
|
||||||
|
e.Error(500, err, fmt.Sprintf("创建异常号码统计失败,\r\n失败信息 %s", err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
e.OK(req.GetId(), "创建成功")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update 修改异常号码统计
|
||||||
|
// @Summary 修改异常号码统计
|
||||||
|
// @Description 修改异常号码统计
|
||||||
|
// @Tags 异常号码统计
|
||||||
|
// @Accept application/json
|
||||||
|
// @Product application/json
|
||||||
|
// @Param id path int true "id"
|
||||||
|
// @Param data body dto.SmsAbnormalNumberUpdateReq true "body"
|
||||||
|
// @Success 200 {object} response.Response "{"code": 200, "message": "修改成功"}"
|
||||||
|
// @Router /api/v1/sms-abnormal-number/{id} [put]
|
||||||
|
// @Security Bearer
|
||||||
|
func (e SmsAbnormalNumber) Update(c *gin.Context) {
|
||||||
|
req := dto.SmsAbnormalNumberUpdateReq{}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
req.SetUpdateBy(user.GetUserId(c))
|
||||||
|
p := actions.GetPermissionFromContext(c)
|
||||||
|
|
||||||
|
err = s.Update(&req, p)
|
||||||
|
if err != nil {
|
||||||
|
e.Error(500, err, fmt.Sprintf("修改异常号码统计失败,\r\n失败信息 %s", err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
e.OK(req.GetId(), "修改成功")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete 删除异常号码统计
|
||||||
|
// @Summary 删除异常号码统计
|
||||||
|
// @Description 删除异常号码统计
|
||||||
|
// @Tags 异常号码统计
|
||||||
|
// @Param data body dto.SmsAbnormalNumberDeleteReq true "body"
|
||||||
|
// @Success 200 {object} response.Response "{"code": 200, "message": "删除成功"}"
|
||||||
|
// @Router /api/v1/sms-abnormal-number [delete]
|
||||||
|
// @Security Bearer
|
||||||
|
func (e SmsAbnormalNumber) Delete(c *gin.Context) {
|
||||||
|
s := service.SmsAbnormalNumber{}
|
||||||
|
req := dto.SmsAbnormalNumberDeleteReq{}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// req.SetUpdateBy(user.GetUserId(c))
|
||||||
|
p := actions.GetPermissionFromContext(c)
|
||||||
|
|
||||||
|
err = s.Remove(&req, p)
|
||||||
|
if err != nil {
|
||||||
|
e.Error(500, err, fmt.Sprintf("删除异常号码统计失败,\r\n失败信息 %s", err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
e.OK(req.GetId(), "删除成功")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 同步平台和系统的差异
|
||||||
|
func (e SmsAbnormalNumber) SyncState(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
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.SyncState()
|
||||||
|
if err != nil {
|
||||||
|
e.Error(500, err, fmt.Sprintf("同步异常号码统计失败,\r\n失败信息 %s", err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
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")
|
||||||
|
}
|
||||||
@ -2,6 +2,7 @@ package apis
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-admin-team/go-admin-core/sdk/api"
|
"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")
|
e.OK(resp, "success")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 手动续费
|
||||||
|
func (e SmsPhone) ManualRenewal(c *gin.Context) {
|
||||||
|
req := dto.ManualRenewalReq{}
|
||||||
|
s := service.SmsRenewalLog{}
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
|||||||
192
app/admin/apis/sms_platform_key.go
Normal file
192
app/admin/apis/sms_platform_key.go
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
package apis
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/go-admin-team/go-admin-core/sdk/api"
|
||||||
|
"github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth/user"
|
||||||
|
_ "github.com/go-admin-team/go-admin-core/sdk/pkg/response"
|
||||||
|
|
||||||
|
"go-admin/app/admin/models"
|
||||||
|
"go-admin/app/admin/service"
|
||||||
|
"go-admin/app/admin/service/dto"
|
||||||
|
"go-admin/common/actions"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SmsPlatformKey struct {
|
||||||
|
api.Api
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPage 获取平台密钥管理列表
|
||||||
|
// @Summary 获取平台密钥管理列表
|
||||||
|
// @Description 获取平台密钥管理列表
|
||||||
|
// @Tags 平台密钥管理
|
||||||
|
// @Param platformCode query string false "平台code"
|
||||||
|
// @Param pageSize query int false "页条数"
|
||||||
|
// @Param pageIndex query int false "页码"
|
||||||
|
// @Success 200 {object} response.Response{data=response.Page{list=[]models.SmsPlatformKey}} "{"code": 200, "data": [...]}"
|
||||||
|
// @Router /api/v1/sms-platform-key [get]
|
||||||
|
// @Security Bearer
|
||||||
|
func (e SmsPlatformKey) GetPage(c *gin.Context) {
|
||||||
|
req := dto.SmsPlatformKeyGetPageReq{}
|
||||||
|
s := service.SmsPlatformKey{}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
p := actions.GetPermissionFromContext(c)
|
||||||
|
list := make([]models.SmsPlatformKey, 0)
|
||||||
|
var count int64
|
||||||
|
|
||||||
|
err = s.GetPage(&req, p, &list, &count)
|
||||||
|
if err != nil {
|
||||||
|
e.Error(500, err, fmt.Sprintf("获取平台密钥管理失败,\r\n失败信息 %s", err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get 获取平台密钥管理
|
||||||
|
// @Summary 获取平台密钥管理
|
||||||
|
// @Description 获取平台密钥管理
|
||||||
|
// @Tags 平台密钥管理
|
||||||
|
// @Param id path int false "id"
|
||||||
|
// @Success 200 {object} response.Response{data=models.SmsPlatformKey} "{"code": 200, "data": [...]}"
|
||||||
|
// @Router /api/v1/sms-platform-key/{id} [get]
|
||||||
|
// @Security Bearer
|
||||||
|
func (e SmsPlatformKey) Get(c *gin.Context) {
|
||||||
|
req := dto.SmsPlatformKeyGetReq{}
|
||||||
|
s := service.SmsPlatformKey{}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
var object models.SmsPlatformKey
|
||||||
|
|
||||||
|
p := actions.GetPermissionFromContext(c)
|
||||||
|
err = s.Get(&req, p, &object)
|
||||||
|
if err != nil {
|
||||||
|
e.Error(500, err, fmt.Sprintf("获取平台密钥管理失败,\r\n失败信息 %s", err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
e.OK(object, "查询成功")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert 创建平台密钥管理
|
||||||
|
// @Summary 创建平台密钥管理
|
||||||
|
// @Description 创建平台密钥管理
|
||||||
|
// @Tags 平台密钥管理
|
||||||
|
// @Accept application/json
|
||||||
|
// @Product application/json
|
||||||
|
// @Param data body dto.SmsPlatformKeyInsertReq true "data"
|
||||||
|
// @Success 200 {object} response.Response "{"code": 200, "message": "添加成功"}"
|
||||||
|
// @Router /api/v1/sms-platform-key [post]
|
||||||
|
// @Security Bearer
|
||||||
|
func (e SmsPlatformKey) Insert(c *gin.Context) {
|
||||||
|
req := dto.SmsPlatformKeyInsertReq{}
|
||||||
|
s := service.SmsPlatformKey{}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
// 设置创建人
|
||||||
|
req.SetCreateBy(user.GetUserId(c))
|
||||||
|
|
||||||
|
err = s.Insert(&req)
|
||||||
|
if err != nil {
|
||||||
|
e.Error(500, err, fmt.Sprintf("创建平台密钥管理失败,\r\n失败信息 %s", err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
e.OK(req.GetId(), "创建成功")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update 修改平台密钥管理
|
||||||
|
// @Summary 修改平台密钥管理
|
||||||
|
// @Description 修改平台密钥管理
|
||||||
|
// @Tags 平台密钥管理
|
||||||
|
// @Accept application/json
|
||||||
|
// @Product application/json
|
||||||
|
// @Param id path int true "id"
|
||||||
|
// @Param data body dto.SmsPlatformKeyUpdateReq true "body"
|
||||||
|
// @Success 200 {object} response.Response "{"code": 200, "message": "修改成功"}"
|
||||||
|
// @Router /api/v1/sms-platform-key/{id} [put]
|
||||||
|
// @Security Bearer
|
||||||
|
func (e SmsPlatformKey) Update(c *gin.Context) {
|
||||||
|
req := dto.SmsPlatformKeyUpdateReq{}
|
||||||
|
s := service.SmsPlatformKey{}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
req.SetUpdateBy(user.GetUserId(c))
|
||||||
|
p := actions.GetPermissionFromContext(c)
|
||||||
|
|
||||||
|
err = s.Update(&req, p)
|
||||||
|
if err != nil {
|
||||||
|
e.Error(500, err, fmt.Sprintf("修改平台密钥管理失败,\r\n失败信息 %s", err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
e.OK(req.GetId(), "修改成功")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete 删除平台密钥管理
|
||||||
|
// @Summary 删除平台密钥管理
|
||||||
|
// @Description 删除平台密钥管理
|
||||||
|
// @Tags 平台密钥管理
|
||||||
|
// @Param data body dto.SmsPlatformKeyDeleteReq true "body"
|
||||||
|
// @Success 200 {object} response.Response "{"code": 200, "message": "删除成功"}"
|
||||||
|
// @Router /api/v1/sms-platform-key [delete]
|
||||||
|
// @Security Bearer
|
||||||
|
func (e SmsPlatformKey) Delete(c *gin.Context) {
|
||||||
|
s := service.SmsPlatformKey{}
|
||||||
|
req := dto.SmsPlatformKeyDeleteReq{}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// req.SetUpdateBy(user.GetUserId(c))
|
||||||
|
p := actions.GetPermissionFromContext(c)
|
||||||
|
|
||||||
|
err = s.Remove(&req, p)
|
||||||
|
if err != nil {
|
||||||
|
e.Error(500, err, fmt.Sprintf("删除平台密钥管理失败,\r\n失败信息 %s", err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
e.OK(req.GetId(), "删除成功")
|
||||||
|
}
|
||||||
@ -12,6 +12,8 @@ import (
|
|||||||
"go-admin/app/admin/service"
|
"go-admin/app/admin/service"
|
||||||
"go-admin/app/admin/service/dto"
|
"go-admin/app/admin/service/dto"
|
||||||
"go-admin/common/actions"
|
"go-admin/common/actions"
|
||||||
|
"go-admin/common/middleware"
|
||||||
|
"go-admin/common/statuscode"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SmsRenewalLog struct {
|
type SmsRenewalLog struct {
|
||||||
@ -43,7 +45,7 @@ func (e SmsRenewalLog) GetPage(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
p := actions.GetPermissionFromContext(c)
|
p := actions.GetPermissionFromContext(c)
|
||||||
list := make([]models.SmsRenewalLog, 0)
|
list := make([]dto.SmsRenewalLogResp, 0)
|
||||||
var count int64
|
var count int64
|
||||||
|
|
||||||
err = s.GetPage(&req, p, &list, &count)
|
err = s.GetPage(&req, p, &list, &count)
|
||||||
@ -190,3 +192,70 @@ func (e SmsRenewalLog) Delete(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
e.OK(req.GetId(), "删除成功")
|
e.OK(req.GetId(), "删除成功")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 手动扣费
|
||||||
|
func (e SmsRenewalLog) ManualDeduct(c *gin.Context) {
|
||||||
|
req := dto.ManualDeductReq{}
|
||||||
|
s := service.SmsRenewalLog{}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
userId, err := middleware.GetUserIdByApiKey(c)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.Logger.Error("获取用户id失败", err)
|
||||||
|
e.Error(statuscode.Unauthorized, nil, statuscode.GetMsg(statuscode.Unauthorized, "en"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result, code := s.ManualDeduct(&req, userId)
|
||||||
|
if code != statuscode.Success {
|
||||||
|
e.Logger.Error(statuscode.GetMsg(code, "en"))
|
||||||
|
e.Error(code, nil, statuscode.GetMsg(code, "en"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
e.OK(result, "success")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取详情根据tradeOrderNo
|
||||||
|
func (e *SmsRenewalLog) GetByTradeOrderNo(c *gin.Context) {
|
||||||
|
req := dto.ManualDeductDetailReq{}
|
||||||
|
s := service.SmsRenewalLog{}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
userId, err := middleware.GetUserIdByApiKey(c)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.Logger.Error("获取用户id失败", err)
|
||||||
|
e.Error(statuscode.Unauthorized, nil, statuscode.GetMsg(statuscode.Unauthorized, "en"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result, code := s.GetRenewalDetailByTradeOrderNo(&req, userId)
|
||||||
|
if code != statuscode.Success {
|
||||||
|
e.Logger.Error(statuscode.GetMsg(code, "en"))
|
||||||
|
e.Error(code, nil, statuscode.GetMsg(code, "en"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
e.OK(result, "success")
|
||||||
|
}
|
||||||
|
|||||||
33
app/admin/models/sms_abnormal_number.go
Normal file
33
app/admin/models/sms_abnormal_number.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go-admin/common/models"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SmsAbnormalNumber struct {
|
||||||
|
models.Model
|
||||||
|
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SmsAbnormalNumber) Generate() models.ActiveRecord {
|
||||||
|
o := *e
|
||||||
|
return &o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SmsAbnormalNumber) GetId() interface{} {
|
||||||
|
return e.Id
|
||||||
|
}
|
||||||
@ -12,6 +12,7 @@ type SmsPhone struct {
|
|||||||
|
|
||||||
PlatformCode string `json:"platformCode" gorm:"type:varchar(20);comment:平台code"`
|
PlatformCode string `json:"platformCode" gorm:"type:varchar(20);comment:平台code"`
|
||||||
UserId int `json:"userId" gorm:"type:bigint;comment:用户Id"`
|
UserId int `json:"userId" gorm:"type:bigint;comment:用户Id"`
|
||||||
|
ApiKey string `json:"apiKey" gorm:"type:varchar(255);comment:apiKey"`
|
||||||
Service string `json:"service" gorm:"type:varchar(50);comment:sms 服务"`
|
Service string `json:"service" gorm:"type:varchar(50);comment:sms 服务"`
|
||||||
ServiceCode string `json:"serviceCode" gorm:"type:varchar(30);comment:服务code"`
|
ServiceCode string `json:"serviceCode" gorm:"type:varchar(30);comment:服务code"`
|
||||||
Type int `json:"type" gorm:"type:tinyint;comment:类型 0-短效 1-长效"`
|
Type int `json:"type" gorm:"type:tinyint;comment:类型 0-短效 1-长效"`
|
||||||
@ -23,9 +24,14 @@ type SmsPhone struct {
|
|||||||
Code string `json:"code" gorm:"type:varchar(10);comment:验证码"`
|
Code string `json:"code" gorm:"type:varchar(10);comment:验证码"`
|
||||||
Status int `json:"status" gorm:"type:tinyint;comment:状态 1-等待验证码 2-已获取"`
|
Status int `json:"status" gorm:"type:tinyint;comment:状态 1-等待验证码 2-已获取"`
|
||||||
ExpireTime *time.Time `json:"expireTime" gorm:"type:datetime;comment:过期时间"`
|
ExpireTime *time.Time `json:"expireTime" gorm:"type:datetime;comment:过期时间"`
|
||||||
|
StartTime *time.Time `json:"startTime" gorm:"type:datetime;comment:开始时间"`
|
||||||
|
EndTime *time.Time `json:"endTime" gorm:"type:datetime;comment:结束时间 单次接码结束时间"`
|
||||||
Actived int `json:"actived" gorm:"type:tinyint;comment:是否激活(长期租赁如果第一次没接收到验证码 则不会激活号码) 1-未激活 2-已激活 3-已失效"`
|
Actived int `json:"actived" gorm:"type:tinyint;comment:是否激活(长期租赁如果第一次没接收到验证码 则不会激活号码) 1-未激活 2-已激活 3-已失效"`
|
||||||
Price decimal.Decimal `json:"price" gorm:"type:decimal(10,2);comment:价格"`
|
Price decimal.Decimal `json:"price" gorm:"type:decimal(10,2);comment:价格"`
|
||||||
AutoRenewal int `json:"autoRenewal" gorm:"type:tinyint;comment:是否自动续费 1-自动续费 2-手动续费"`
|
AutoRenewal int `json:"autoRenewal" gorm:"type:tinyint;comment:是否自动续费 1-自动续费 2-手动续费"`
|
||||||
|
Remark string `json:"remark" gorm:"type:varchar(255);comment:备注"`
|
||||||
|
BillingCycleId string `json:"billingCycleId" gorm:"type:varchar(50);comment:手动续费id"`
|
||||||
|
PlatformName string `json:"platformName" gorm:"-"`
|
||||||
models.ModelTime
|
models.ModelTime
|
||||||
models.ControlBy
|
models.ControlBy
|
||||||
}
|
}
|
||||||
|
|||||||
31
app/admin/models/sms_platform_key.go
Normal file
31
app/admin/models/sms_platform_key.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go-admin/common/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SmsPlatformKey struct {
|
||||||
|
models.Model
|
||||||
|
|
||||||
|
PlatformCode string `json:"platformCode" gorm:"type:varchar(20);comment:平台code"`
|
||||||
|
Account string `json:"account" gorm:"type:varchar(50);comment:账号"`
|
||||||
|
ApiKey string `json:"apiKey" gorm:"type:varchar(500);comment:平台key"`
|
||||||
|
ApiSecret string `json:"apiSecret" gorm:"type:varchar(255);comment:平台私钥"`
|
||||||
|
Status int64 `json:"status" gorm:"type:tinyint;comment:状态 1-启用 2-禁用"`
|
||||||
|
Remark string `json:"remark" gorm:"type:varchar(255);comment:备注"`
|
||||||
|
models.ModelTime
|
||||||
|
models.ControlBy
|
||||||
|
}
|
||||||
|
|
||||||
|
func (SmsPlatformKey) TableName() string {
|
||||||
|
return "sms_platform_key"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SmsPlatformKey) Generate() models.ActiveRecord {
|
||||||
|
o := *e
|
||||||
|
return &o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SmsPlatformKey) GetId() interface{} {
|
||||||
|
return e.Id
|
||||||
|
}
|
||||||
@ -10,7 +10,7 @@ type SmsReceiveLog struct {
|
|||||||
UserId int `json:"userId" gorm:"type:bigint;comment:用户id"`
|
UserId int `json:"userId" gorm:"type:bigint;comment:用户id"`
|
||||||
Service string `json:"service" gorm:"type:varchar(30);comment:服务"`
|
Service string `json:"service" gorm:"type:varchar(30);comment:服务"`
|
||||||
ServiceCode string `json:"serviceCode" gorm:"type:varchar(30);comment:服务code"`
|
ServiceCode string `json:"serviceCode" gorm:"type:varchar(30);comment:服务code"`
|
||||||
MessageId int `json:"messageId" gorm:"type:int;comment:短信id"`
|
MessageId string `json:"messageId" gorm:"type:int;comment:短信id"`
|
||||||
Phone string `json:"phone" gorm:"type:varchar(30);comment:号码"`
|
Phone string `json:"phone" gorm:"type:varchar(30);comment:号码"`
|
||||||
Code string `json:"code" gorm:"type:varchar(20);comment:验证码"`
|
Code string `json:"code" gorm:"type:varchar(20);comment:验证码"`
|
||||||
Status int `json:"status" gorm:"type:tinyint;comment:状态 0-等待验证码 1-成功 2-失败"`
|
Status int `json:"status" gorm:"type:tinyint;comment:状态 0-等待验证码 1-成功 2-失败"`
|
||||||
|
|||||||
@ -12,11 +12,23 @@ type SmsRenewalLog struct {
|
|||||||
models.Model
|
models.Model
|
||||||
|
|
||||||
PhoneId int `json:"phoneId" gorm:"type:bigint;comment:号码id"`
|
PhoneId int `json:"phoneId" gorm:"type:bigint;comment:号码id"`
|
||||||
|
Phone string `json:"phone" gorm:"type:varchar(50);comment:号码"`
|
||||||
|
PayOrderNo string `json:"payOrderNo" gorm:"type:varchar(50);comment:本平台支付订单号"`
|
||||||
|
TradeOrderNo string `json:"tradeOrderNo" gorm:"type:varchar(50);comment:交易订单号"`
|
||||||
|
Status int `json:"status" gorm:"type:tinyint;comment:状态 1-处理中(冻结) 2-成功 3-失败(解冻)"`
|
||||||
Type int `json:"type" gorm:"type:tinyint;comment:类型 0-长效 1-短效"`
|
Type int `json:"type" gorm:"type:tinyint;comment:类型 0-长效 1-短效"`
|
||||||
|
Category int `json:"category" gorm:"type:tinyint;comment:类别 1-购买号码 2-续费号码"`
|
||||||
UserId int `json:"userId" gorm:"type:bigint;comment:用户id"`
|
UserId int `json:"userId" gorm:"type:bigint;comment:用户id"`
|
||||||
Amount decimal.Decimal `json:"amount" gorm:"type:decimal(10,2);comment:扣费金额"`
|
Amount decimal.Decimal `json:"amount" gorm:"type:decimal(10,2);comment:扣费金额"`
|
||||||
BeforeTime time.Time `json:"beforeTime" gorm:"type:datetime;comment:续费前过期时间"`
|
BeforeTime time.Time `json:"beforeTime" gorm:"type:datetime;comment:续费前过期时间"`
|
||||||
|
TargetTime *time.Time `json:"targetTime" gorm:"type:datetime;comment:续费目标过期时间"`
|
||||||
Period int `json:"period" gorm:"type:int;comment:时间段"`
|
Period int `json:"period" gorm:"type:int;comment:时间段"`
|
||||||
|
Remark string `json:"remark" gorm:"type:varchar(255);comment:备注"`
|
||||||
|
Username string `json:"username" gorm:"->"`
|
||||||
|
PlatformCode string `json:"platformCode" gorm:"->"`
|
||||||
|
ApiKey string `json:"apiKey" gorm:"->"`
|
||||||
|
BillingCycleId string `json:"billingCycleId" gorm:"->"`
|
||||||
|
ActivationId string `json:"activationId" gorm:"->"`
|
||||||
models.ModelTime
|
models.ModelTime
|
||||||
models.ControlBy
|
models.ControlBy
|
||||||
}
|
}
|
||||||
|
|||||||
37
app/admin/router/sms_abnormal_number.go
Normal file
37
app/admin/router/sms_abnormal_number.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth"
|
||||||
|
|
||||||
|
"go-admin/app/admin/apis"
|
||||||
|
"go-admin/common/actions"
|
||||||
|
"go-admin/common/middleware"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
routerCheckRole = append(routerCheckRole, registerSmsAbnormalNumberRouter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.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)
|
||||||
|
}
|
||||||
|
|
||||||
|
r1 := v1.Group("/sms-abnormal-number")
|
||||||
|
{
|
||||||
|
r1.POST("/sync-state", api.SyncState) //同步差异
|
||||||
|
r1.PUT("/reuse", api.ReUseAbnormalNumber) //重用号码
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -45,6 +45,7 @@ func registerSmsPhoneRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddl
|
|||||||
r.GET("/:id", actions.PermissionAction(), api.Get)
|
r.GET("/:id", actions.PermissionAction(), api.Get)
|
||||||
r.POST("", api.Insert)
|
r.POST("", api.Insert)
|
||||||
r.PUT("/:id", actions.PermissionAction(), api.Update)
|
r.PUT("/:id", actions.PermissionAction(), api.Update)
|
||||||
|
r.PUT("/manual-renewal", api.ManualRenewal)
|
||||||
r.DELETE("", api.Delete)
|
r.DELETE("", api.Delete)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
27
app/admin/router/sms_platform_key.go
Normal file
27
app/admin/router/sms_platform_key.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth"
|
||||||
|
|
||||||
|
"go-admin/app/admin/apis"
|
||||||
|
"go-admin/common/middleware"
|
||||||
|
"go-admin/common/actions"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
routerCheckRole = append(routerCheckRole, registerSmsPlatformKeyRouter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// registerSmsPlatformKeyRouter
|
||||||
|
func registerSmsPlatformKeyRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) {
|
||||||
|
api := apis.SmsPlatformKey{}
|
||||||
|
r := v1.Group("/sms-platform-key").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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,8 +5,8 @@ import (
|
|||||||
jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth"
|
jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth"
|
||||||
|
|
||||||
"go-admin/app/admin/apis"
|
"go-admin/app/admin/apis"
|
||||||
"go-admin/common/middleware"
|
|
||||||
"go-admin/common/actions"
|
"go-admin/common/actions"
|
||||||
|
"go-admin/common/middleware"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -16,6 +16,14 @@ func init() {
|
|||||||
// registerSmsRenewalLogRouter
|
// registerSmsRenewalLogRouter
|
||||||
func registerSmsRenewalLogRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) {
|
func registerSmsRenewalLogRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) {
|
||||||
api := apis.SmsRenewalLog{}
|
api := apis.SmsRenewalLog{}
|
||||||
|
r1 := v1.Group("sms-renewal-log").Use(middleware.FrontedAuth)
|
||||||
|
{
|
||||||
|
// 手动扣费续期
|
||||||
|
r1.POST("manual-deduct", api.ManualDeduct)
|
||||||
|
//根据交易订单号获取续期详情
|
||||||
|
r1.GET("detail-byorderno", api.GetByTradeOrderNo)
|
||||||
|
}
|
||||||
|
|
||||||
r := v1.Group("/sms-renewal-log").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole())
|
r := v1.Group("/sms-renewal-log").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole())
|
||||||
{
|
{
|
||||||
r.GET("", actions.PermissionAction(), api.GetPage)
|
r.GET("", actions.PermissionAction(), api.GetPage)
|
||||||
|
|||||||
93
app/admin/service/dto/sms_abnormal_number.go
Normal file
93
app/admin/service/dto/sms_abnormal_number.go
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
package dto
|
||||||
|
|
||||||
|
import (
|
||||||
|
|
||||||
|
"go-admin/app/admin/models"
|
||||||
|
"go-admin/common/dto"
|
||||||
|
common "go-admin/common/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SmsAbnormalNumberGetPageReq struct {
|
||||||
|
dto.Pagination `search:"-"`
|
||||||
|
PlatformCode string `form:"platformCode" search:"type:exact;column:platform_code;table:sms_abnormal_number" comment:"平台code"`
|
||||||
|
Phone string `form:"phone" search:"type:contains;column:phone;table:sms_abnormal_number" comment:"电话号码"`
|
||||||
|
SmsAbnormalNumberOrder
|
||||||
|
}
|
||||||
|
|
||||||
|
type SmsAbnormalNumberOrder struct {
|
||||||
|
Id string `form:"idOrder" search:"type:order;column:id;table:sms_abnormal_number"`
|
||||||
|
Account string `form:"accountOrder" search:"type:order;column:account;table:sms_abnormal_number"`
|
||||||
|
PlatformCode string `form:"platformCodeOrder" search:"type:order;column:platform_code;table:sms_abnormal_number"`
|
||||||
|
Phone string `form:"phoneOrder" search:"type:order;column:phone;table:sms_abnormal_number"`
|
||||||
|
CreatedAt string `form:"createdAtOrder" search:"type:order;column:created_at;table:sms_abnormal_number"`
|
||||||
|
UpdatedAt string `form:"updatedAtOrder" search:"type:order;column:updated_at;table:sms_abnormal_number"`
|
||||||
|
DeletedAt string `form:"deletedAtOrder" search:"type:order;column:deleted_at;table:sms_abnormal_number"`
|
||||||
|
CreateBy string `form:"createByOrder" search:"type:order;column:create_by;table:sms_abnormal_number"`
|
||||||
|
UpdateBy string `form:"updateByOrder" search:"type:order;column:update_by;table:sms_abnormal_number"`
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *SmsAbnormalNumberGetPageReq) GetNeedSearch() interface{} {
|
||||||
|
return *m
|
||||||
|
}
|
||||||
|
|
||||||
|
type SmsAbnormalNumberInsertReq struct {
|
||||||
|
Id int `json:"-" comment:"主键id"` // 主键id
|
||||||
|
Account string `json:"account" comment:"账号"`
|
||||||
|
PlatformCode string `json:"platformCode" comment:"平台code"`
|
||||||
|
Phone string `json:"phone" comment:"电话号码"`
|
||||||
|
common.ControlBy
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SmsAbnormalNumberInsertReq) Generate(model *models.SmsAbnormalNumber) {
|
||||||
|
if s.Id == 0 {
|
||||||
|
model.Model = common.Model{ Id: s.Id }
|
||||||
|
}
|
||||||
|
model.Account = s.Account
|
||||||
|
model.PlatformCode = s.PlatformCode
|
||||||
|
model.Phone = s.Phone
|
||||||
|
model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SmsAbnormalNumberInsertReq) GetId() interface{} {
|
||||||
|
return s.Id
|
||||||
|
}
|
||||||
|
|
||||||
|
type SmsAbnormalNumberUpdateReq struct {
|
||||||
|
Id int `uri:"id" comment:"主键id"` // 主键id
|
||||||
|
Account string `json:"account" comment:"账号"`
|
||||||
|
PlatformCode string `json:"platformCode" comment:"平台code"`
|
||||||
|
Phone string `json:"phone" comment:"电话号码"`
|
||||||
|
common.ControlBy
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SmsAbnormalNumberUpdateReq) Generate(model *models.SmsAbnormalNumber) {
|
||||||
|
if s.Id == 0 {
|
||||||
|
model.Model = common.Model{ Id: s.Id }
|
||||||
|
}
|
||||||
|
model.Account = s.Account
|
||||||
|
model.PlatformCode = s.PlatformCode
|
||||||
|
model.Phone = s.Phone
|
||||||
|
model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SmsAbnormalNumberUpdateReq) GetId() interface{} {
|
||||||
|
return s.Id
|
||||||
|
}
|
||||||
|
|
||||||
|
// SmsAbnormalNumberGetReq 功能获取请求参数
|
||||||
|
type SmsAbnormalNumberGetReq struct {
|
||||||
|
Id int `uri:"id"`
|
||||||
|
}
|
||||||
|
func (s *SmsAbnormalNumberGetReq) GetId() interface{} {
|
||||||
|
return s.Id
|
||||||
|
}
|
||||||
|
|
||||||
|
// SmsAbnormalNumberDeleteReq 功能删除请求参数
|
||||||
|
type SmsAbnormalNumberDeleteReq struct {
|
||||||
|
Ids []int `json:"ids"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SmsAbnormalNumberDeleteReq) GetId() interface{} {
|
||||||
|
return s.Ids
|
||||||
|
}
|
||||||
@ -14,8 +14,10 @@ type SmsPhoneGetPageReq struct {
|
|||||||
dto.Pagination `search:"-"`
|
dto.Pagination `search:"-"`
|
||||||
Service string `form:"service" search:"type:exact;column:service;table:sms_phone" comment:"sms 服务"`
|
Service string `form:"service" search:"type:exact;column:service;table:sms_phone" comment:"sms 服务"`
|
||||||
PlatformCode string `form:"platformCode" search:"type:exact;column:platform_code;table:sms_phone" comment:"平台code"`
|
PlatformCode string `form:"platformCode" search:"type:exact;column:platform_code;table:sms_phone" comment:"平台code"`
|
||||||
ServiceCode string `form:"serviceCode" search:"type:exact;column:service_code;table:sms_phone" comment:"服务code"`
|
ServiceCode string `form:"serviceCode" search:"type:contains;column:service_code;table:sms_phone" comment:"服务code"`
|
||||||
|
Phone string `form:"phone" search:"type:contains;column:phone;table:sms_phone" comment:"号码"`
|
||||||
Type int `form:"type" search:"-" comment:"类型 0-短效 1-长效"`
|
Type int `form:"type" search:"-" comment:"类型 0-短效 1-长效"`
|
||||||
|
IsActived int `form:"isActived" search:"-" comment:"是否可用"`
|
||||||
SmsPhoneOrder
|
SmsPhoneOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,6 +154,8 @@ type WeakUpReq struct {
|
|||||||
type WeakUpResp struct {
|
type WeakUpResp struct {
|
||||||
ActivationId string `json:"activationId" comment:"激活id"`
|
ActivationId string `json:"activationId" comment:"激活id"`
|
||||||
MessageId string `json:"messageId" commment:"消息id"`
|
MessageId string `json:"messageId" commment:"消息id"`
|
||||||
|
StartTime *time.Time `json:"startTime" comment:"接码开始时间"`
|
||||||
|
EndTime *time.Time `json:"endTime" comment:"接码过期时间"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *WeakUpReq) Validate() error {
|
func (s *WeakUpReq) Validate() error {
|
||||||
@ -192,7 +196,11 @@ type SmsPhoneCleanMyPhoneReq struct {
|
|||||||
type SmsPhoneGetPhoneResp struct {
|
type SmsPhoneGetPhoneResp struct {
|
||||||
Phone string `json:"phone"`
|
Phone string `json:"phone"`
|
||||||
EndAt *time.Time `json:"endTime"`
|
EndAt *time.Time `json:"endTime"`
|
||||||
|
StartTime *time.Time `json:"-" comment:"唤醒后单次接码开始时间"`
|
||||||
|
EndTime *time.Time `json:"-" comment:"唤醒后单次接码过期时间"`
|
||||||
|
MessageId string `json:"-" comment:"消息id"`
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
|
BillingCycleId string `json:"billingCycleId" comment:"续租id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type DaisysmsPriceResp struct {
|
type DaisysmsPriceResp struct {
|
||||||
@ -208,9 +216,45 @@ type OpenGetNumberResp struct {
|
|||||||
Number string `json:"number"`
|
Number string `json:"number"`
|
||||||
ActivationId string `json:"activationId"`
|
ActivationId string `json:"activationId"`
|
||||||
ExpireTime *time.Time `json:"expireTime"`
|
ExpireTime *time.Time `json:"expireTime"`
|
||||||
|
StartTime *time.Time `json:"startTime"`
|
||||||
|
EndTime *time.Time `json:"endTime"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type OpenWakeUpReq struct {
|
type OpenWakeUpReq struct {
|
||||||
PlatformCode string `json:"platformCode" comment:"短信平台"`
|
PlatformCode string `json:"platformCode" comment:"短信平台"`
|
||||||
ActivationId string `json:"activationId" comment:"平台id"`
|
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"`
|
||||||
|
TradeOrderNo string `json:"tradeOrderNo" comment:"交易订单号"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 手动续期回调请求
|
||||||
|
type ManualRenewCallBackReq struct {
|
||||||
|
TradeOrderNo string `json:"tradeOrderNo" comment:"交易订单号"`
|
||||||
|
PayOrderNo string `json:"payOrderNo" comment:"本平台支付订单号"`
|
||||||
|
Status int `json:"status" comment:"状态 1-处理中 2-失败 3-回滚 4-成功"`
|
||||||
|
Remark string `json:"remark" comment:"备注"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取号码响应
|
||||||
|
type SmsPhoneGetNumberResp struct {
|
||||||
|
ActivationId string `json:"activationId" comment:"激活码id"`
|
||||||
|
Phone string `json:"phone" comment:"号码"`
|
||||||
|
ExpireTime *time.Time `json:"expireTime" comment:"过期时间"`
|
||||||
|
StartTime *time.Time `json:"startTime" comment:"唤醒后单次接码开始时间"`
|
||||||
|
EndTime *time.Time `json:"endTime" comment:"唤醒后单次接码过期时间"`
|
||||||
|
BillingCycleId string `json:"billingCycleId" comment:"续租id"`
|
||||||
|
}
|
||||||
|
|||||||
117
app/admin/service/dto/sms_platform_key.go
Normal file
117
app/admin/service/dto/sms_platform_key.go
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
package dto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go-admin/app/admin/models"
|
||||||
|
"go-admin/common/dto"
|
||||||
|
common "go-admin/common/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SmsPlatformKeyGetPageReq struct {
|
||||||
|
dto.Pagination `search:"-"`
|
||||||
|
PlatformCode string `form:"platformCode" search:"type:exact;column:platform_code;table:sms_platform_key" comment:"平台code"`
|
||||||
|
SmsPlatformKeyOrder
|
||||||
|
}
|
||||||
|
|
||||||
|
type SmsPlatformKeyOrder struct {
|
||||||
|
Id string `form:"idOrder" search:"type:order;column:id;table:sms_platform_key"`
|
||||||
|
PlatformCode string `form:"platformCodeOrder" search:"type:order;column:platform_code;table:sms_platform_key"`
|
||||||
|
ApiKey string `form:"apiKeyOrder" search:"type:order;column:api_key;table:sms_platform_key"`
|
||||||
|
ApiSecret string `form:"apiSecretOrder" search:"type:order;column:api_secret;table:sms_platform_key"`
|
||||||
|
Status string `form:"statusOrder" search:"type:order;column:status;table:sms_platform_key"`
|
||||||
|
Remark string `form:"remarkOrder" search:"type:order;column:remark;table:sms_platform_key"`
|
||||||
|
CreatedAt string `form:"createdAtOrder" search:"type:order;column:created_at;table:sms_platform_key"`
|
||||||
|
UpdatedAt string `form:"updatedAtOrder" search:"type:order;column:updated_at;table:sms_platform_key"`
|
||||||
|
DeletedAt string `form:"deletedAtOrder" search:"type:order;column:deleted_at;table:sms_platform_key"`
|
||||||
|
CreateBy string `form:"createByOrder" search:"type:order;column:create_by;table:sms_platform_key"`
|
||||||
|
UpdateBy string `form:"updateByOrder" search:"type:order;column:update_by;table:sms_platform_key"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *SmsPlatformKeyGetPageReq) GetNeedSearch() interface{} {
|
||||||
|
return *m
|
||||||
|
}
|
||||||
|
|
||||||
|
type SmsPlatformKeyInsertReq struct {
|
||||||
|
Id int `json:"-" comment:"主键id"` // 主键id
|
||||||
|
PlatformCode string `json:"platformCode" comment:"平台code"`
|
||||||
|
Account string `json:"account" comment:"account"`
|
||||||
|
ApiKey string `json:"apiKey" comment:"平台key"`
|
||||||
|
ApiSecret string `json:"apiSecret" comment:"平台私钥"`
|
||||||
|
Status int64 `json:"status" comment:"状态 1-启用 2-禁用"`
|
||||||
|
Remark string `json:"remark" comment:"备注"`
|
||||||
|
common.ControlBy
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SmsPlatformKeyInsertReq) Generate(model *models.SmsPlatformKey) {
|
||||||
|
if s.Id == 0 {
|
||||||
|
model.Model = common.Model{Id: s.Id}
|
||||||
|
}
|
||||||
|
model.PlatformCode = s.PlatformCode
|
||||||
|
model.Account = s.Account
|
||||||
|
model.ApiKey = s.ApiKey
|
||||||
|
model.ApiSecret = s.ApiSecret
|
||||||
|
model.Status = s.Status
|
||||||
|
model.Remark = s.Remark
|
||||||
|
model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SmsPlatformKeyInsertReq) GetId() interface{} {
|
||||||
|
return s.Id
|
||||||
|
}
|
||||||
|
|
||||||
|
type SmsPlatformKeyUpdateReq struct {
|
||||||
|
Id int `uri:"id" comment:"主键id"` // 主键id
|
||||||
|
PlatformCode string `json:"platformCode" comment:"平台code"`
|
||||||
|
Account string `json:"account" comment:"平台账号"`
|
||||||
|
ApiKey string `json:"apiKey" comment:"平台key"`
|
||||||
|
ApiSecret string `json:"apiSecret" comment:"平台私钥"`
|
||||||
|
Status int64 `json:"status" comment:"状态 1-启用 2-禁用"`
|
||||||
|
Remark string `json:"remark" comment:"备注"`
|
||||||
|
common.ControlBy
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SmsPlatformKeyUpdateReq) Generate(model *models.SmsPlatformKey) {
|
||||||
|
if s.Id == 0 {
|
||||||
|
model.Model = common.Model{Id: s.Id}
|
||||||
|
}
|
||||||
|
model.PlatformCode = s.PlatformCode
|
||||||
|
model.Account = s.Account
|
||||||
|
model.ApiKey = s.ApiKey
|
||||||
|
model.ApiSecret = s.ApiSecret
|
||||||
|
model.Status = s.Status
|
||||||
|
model.Remark = s.Remark
|
||||||
|
model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SmsPlatformKeyUpdateReq) GetId() interface{} {
|
||||||
|
return s.Id
|
||||||
|
}
|
||||||
|
|
||||||
|
// SmsPlatformKeyGetReq 功能获取请求参数
|
||||||
|
type SmsPlatformKeyGetReq struct {
|
||||||
|
Id int `uri:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SmsPlatformKeyGetReq) GetId() interface{} {
|
||||||
|
return s.Id
|
||||||
|
}
|
||||||
|
|
||||||
|
// SmsPlatformKeyDeleteReq 功能删除请求参数
|
||||||
|
type SmsPlatformKeyDeleteReq struct {
|
||||||
|
Ids []int `json:"ids"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SmsPlatformKeyDeleteReq) GetId() interface{} {
|
||||||
|
return s.Ids
|
||||||
|
}
|
||||||
|
|
||||||
|
type SmsPlatformKeyQueueDto struct {
|
||||||
|
PlatformCode string `json:"platformCode" comment:"平台code"`
|
||||||
|
Account string `json:"account" comment:"平台账号"`
|
||||||
|
ApiKey string `json:"apiKey" comment:"平台key"`
|
||||||
|
ApiSecret string `json:"apiSecret" comment:"平台私钥"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SmsPlatformKeyGroupDto struct {
|
||||||
|
PlatformCode string `json:"platformCode"`
|
||||||
|
Count int `json:"count"`
|
||||||
|
}
|
||||||
@ -13,6 +13,8 @@ import (
|
|||||||
type SmsRenewalLogGetPageReq struct {
|
type SmsRenewalLogGetPageReq struct {
|
||||||
dto.Pagination `search:"-"`
|
dto.Pagination `search:"-"`
|
||||||
Type int64 `form:"type" search:"type:exact;column:type;table:sms_renewal_log" comment:"类型 0-长效 1-短效"`
|
Type int64 `form:"type" search:"type:exact;column:type;table:sms_renewal_log" comment:"类型 0-长效 1-短效"`
|
||||||
|
Category int `form:"category" search:"type:exact;column:category;table:sms_renewal_log" comment:"类别 0-普通续费 1-号码补偿"`
|
||||||
|
UserName string `form:"userName" search:"-"`
|
||||||
SmsRenewalLogOrder
|
SmsRenewalLogOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,3 +110,43 @@ type SmsRenewalLogDeleteReq struct {
|
|||||||
func (s *SmsRenewalLogDeleteReq) GetId() interface{} {
|
func (s *SmsRenewalLogDeleteReq) GetId() interface{} {
|
||||||
return s.Ids
|
return s.Ids
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SmsRenewalLogResp struct {
|
||||||
|
Id int `json:"id" comment:"主键id"` // 主键id
|
||||||
|
PhoneId int `json:"phoneId" comment:"号码id"`
|
||||||
|
Phone string `json:"phone" comment:"电话号码"`
|
||||||
|
TradeOrderNo string `json:"tradeOrderNo" comment:"交易订单号"`
|
||||||
|
PayOrderNo string `json:"payOrderNo" comment:"支付订单号"`
|
||||||
|
Type int `json:"type" comment:"类型 0-长效 1-短效"`
|
||||||
|
Category int `json:"category" comment:"类别 1-购买 2-续期"`
|
||||||
|
UserId int `json:"userId" comment:"用户id"`
|
||||||
|
Amount decimal.Decimal `json:"amount" comment:"扣费金额"`
|
||||||
|
BeforeTime time.Time `json:"beforeTime" comment:"续费前过期时间"`
|
||||||
|
Period int `json:"period" comment:"时间(天)"`
|
||||||
|
CreatedAt time.Time `json:"createdAt" comment:"创建时间"`
|
||||||
|
Status int `json:"status" comment:"状态 1-预扣费 2-成功 3-失败"`
|
||||||
|
// UpdatedAt time.Time `json:"updatedAt" comment:"更新时间"`
|
||||||
|
// DeletedAt time.Time `json:"deletedAt" comment:"删除时间"`
|
||||||
|
UserName string `json:"username" comment:"用户名"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 手动扣费
|
||||||
|
type ManualDeductReq struct {
|
||||||
|
ActivationId string `json:"activationId" comment:"激活id"`
|
||||||
|
TradeOrderNo string `json:"tradeOrderNo" comment:"交易订单号"`
|
||||||
|
BeginTime time.Time `json:"beginTime" comment:"开始时间"`
|
||||||
|
}
|
||||||
|
|
||||||
|
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-失败"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 续费详情
|
||||||
|
type ManualDeductDetailReq struct {
|
||||||
|
TradeOrderNo string `json:"tradeOrderNo" comment:"交易订单号"`
|
||||||
|
}
|
||||||
|
|||||||
@ -118,6 +118,7 @@ type SmsServicesGetListResp struct {
|
|||||||
Status int `json:"status" comment:"状态"`
|
Status int `json:"status" comment:"状态"`
|
||||||
Price decimal.Decimal `json:"price" comment:"价格"`
|
Price decimal.Decimal `json:"price" comment:"价格"`
|
||||||
LongPrice decimal.Decimal `json:"longPrice" comment:"长号码价格"`
|
LongPrice decimal.Decimal `json:"longPrice" comment:"长号码价格"`
|
||||||
|
RenewLongPrice decimal.Decimal `json:"renewLongPrice" comment:"续费长号码价格"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SmsGetPriceReq struct {
|
type SmsGetPriceReq struct {
|
||||||
|
|||||||
@ -131,6 +131,7 @@ type VerificationDTO struct {
|
|||||||
Reuse ReuseInfo `json:"reuse"`
|
Reuse ReuseInfo `json:"reuse"`
|
||||||
Sale TextVerifiedResp `json:"sale"`
|
Sale TextVerifiedResp `json:"sale"`
|
||||||
ServiceName string `json:"serviceName"`
|
ServiceName string `json:"serviceName"`
|
||||||
|
ApiKey string `json:"apiKey"`
|
||||||
// State 表示验证或服务的当前状态。
|
// State 表示验证或服务的当前状态。
|
||||||
// 状态是以下枚举值之一,代表了从创建到完成或取消的生命周期:
|
// 状态是以下枚举值之一,代表了从创建到完成或取消的生命周期:
|
||||||
// * verificationPending: 正在等待验证码。
|
// * verificationPending: 正在等待验证码。
|
||||||
@ -154,6 +155,7 @@ type VerificationDTO struct {
|
|||||||
// * nonrenewableRefunded: 服务已退款。
|
// * nonrenewableRefunded: 服务已退款。
|
||||||
State string `json:"state" comment:"verificationPending┃verificationCompleted┃verificationCanceled┃verificationTimedOut┃verificationReported┃verificationRefunded┃verificationReused┃verificationReactivated┃renewableActive┃renewableOverdue┃renewableExpired┃renewableRefunded┃nonrenewableActive┃nonrenewableExpired┃nonrenewableRefunded"`
|
State string `json:"state" comment:"verificationPending┃verificationCompleted┃verificationCanceled┃verificationTimedOut┃verificationReported┃verificationRefunded┃verificationReused┃verificationReactivated┃renewableActive┃renewableOverdue┃renewableExpired┃renewableRefunded┃nonrenewableActive┃nonrenewableExpired┃nonrenewableRefunded"`
|
||||||
TotalCost float64 `json:"totalCost"`
|
TotalCost float64 `json:"totalCost"`
|
||||||
|
IsIncludedForNextRenewal bool `json:"isIncludedForNextRenewal" comment:"是否自动续期"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 长效详情
|
// 长效详情
|
||||||
@ -165,6 +167,7 @@ type VerificationRentalDetailResp struct {
|
|||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Sale TextVerifiedResp `json:"sale"`
|
Sale TextVerifiedResp `json:"sale"`
|
||||||
SaleId string `json:"saleId"`
|
SaleId string `json:"saleId"`
|
||||||
|
BillingCycleId string `json:"billingCycleId" comment:"续租id"`
|
||||||
ServiceName string `json:"serviceName"`
|
ServiceName string `json:"serviceName"`
|
||||||
State string `json:"state"`
|
State string `json:"state"`
|
||||||
AlwaysOn bool `json:"alwaysOn"`
|
AlwaysOn bool `json:"alwaysOn"`
|
||||||
@ -212,3 +215,45 @@ type TextVerifiedSmsItemResp struct {
|
|||||||
ParsedCode string `json:"parsedCode"`
|
ParsedCode string `json:"parsedCode"`
|
||||||
Encrypted bool `json:"encrypted"`
|
Encrypted bool `json:"encrypted"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TextVerifiedGetNumbersResp struct {
|
||||||
|
HasNext bool `json:"hasNext" comment:"是否有下一页"`
|
||||||
|
Links TextVerifiedGetNumbersLinksResp `json:"links"`
|
||||||
|
Data []VerificationDTO `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TextVerifiedGetRentalListResp struct {
|
||||||
|
HasNext bool `json:"hasNext" comment:"是否有下一页"`
|
||||||
|
Links TextVerifiedGetNumbersLinksResp `json:"links"`
|
||||||
|
Data []VerificationRentalDetailResp `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TextVerifiedGetNumbersLinksResp struct {
|
||||||
|
Next *TextVerifiedResp `json:"next"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TextVerifiedManualRenewalResp struct {
|
||||||
|
CreatedAt time.Time `json:"createdAt" comment:"创建时间"`
|
||||||
|
Id string `json:"id" comment:"id"`
|
||||||
|
ExcludeRentals []TextVerifiedManualRenewalRental `json:"excludeRentals" comment:"排除的长效详情"`
|
||||||
|
IncludeRentals []TextVerifiedManualRenewalRental `json:"includeRentals" comment:"包含的长效详情"`
|
||||||
|
IsPaidFor bool `json:"isPaidFor" comment:"是否已支付"`
|
||||||
|
TotalCost decimal.Decimal `json:"totalCost" comment:"总费用"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TextVerifiedManualRenewalRental struct {
|
||||||
|
Number string `json:"number" comment:"号码"`
|
||||||
|
Rental TextVerifiedResp `json:"rental" comment:"长效详情"`
|
||||||
|
RenewalCost decimal.Decimal `json:"renewalCost" comment:"续费费用"`
|
||||||
|
ServiceName string `json:"serviceName" comment:"服务名称"`
|
||||||
|
AlreadyRenewed bool `json:"alreadyRenewed" comment:"是否已续费"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 续费周期详情
|
||||||
|
type TextVerifiedGetBillingCycleResp struct {
|
||||||
|
Id string `json:"id" comment:"id"`
|
||||||
|
RenewedThrough time.Time `json:"renewedThrough" comment:"已续费到"`
|
||||||
|
BillingCycleEndsAt time.Time `json:"billingCycleEndsAt" comment:"计费周期结束时间"`
|
||||||
|
NextAutoRenewAttempt time.Time `json:"nextAutoRenewAttempt" comment:"下次自动续费时间"`
|
||||||
|
State string `json:"state" comment:"是否激活 active"`
|
||||||
|
}
|
||||||
|
|||||||
@ -115,7 +115,7 @@ func (e MemberRecharge) CustomRecharge(req *dto.MemberRechargeCustomRechargeReq,
|
|||||||
return errors.New("server error")
|
return errors.New("server error")
|
||||||
}
|
}
|
||||||
|
|
||||||
cacheExpireDuration := time.Duration(orderExpireTime+1) * time.Minute
|
cacheExpireDuration := time.Duration(orderExpireTime+10) * time.Minute
|
||||||
key := fmt.Sprintf("pre_order:%s", amount)
|
key := fmt.Sprintf("pre_order:%s", amount)
|
||||||
err = e.Orm.Transaction(func(tx *gorm.DB) error {
|
err = e.Orm.Transaction(func(tx *gorm.DB) error {
|
||||||
if err1 := tx.Create(&data).Error; err1 != nil {
|
if err1 := tx.Create(&data).Error; err1 != nil {
|
||||||
|
|||||||
434
app/admin/service/sms_abnormal_number.go
Normal file
434
app/admin/service/sms_abnormal_number.go
Normal file
@ -0,0 +1,434 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-admin-team/go-admin-core/sdk/service"
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
|
||||||
|
"go-admin/app/admin/models"
|
||||||
|
"go-admin/app/admin/service/dto"
|
||||||
|
"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
|
||||||
|
var data models.SmsAbnormalNumber
|
||||||
|
|
||||||
|
err = e.Orm.Model(&data).
|
||||||
|
Scopes(
|
||||||
|
cDto.MakeCondition(c.GetNeedSearch()),
|
||||||
|
cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
|
||||||
|
actions.Permission(data.TableName(), p),
|
||||||
|
).
|
||||||
|
Find(list).Limit(-1).Offset(-1).
|
||||||
|
Count(count).Error
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("SmsAbnormalNumberService GetPage error:%s \r\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get 获取SmsAbnormalNumber对象
|
||||||
|
func (e *SmsAbnormalNumber) Get(d *dto.SmsAbnormalNumberGetReq, p *actions.DataPermission, model *models.SmsAbnormalNumber) error {
|
||||||
|
var data models.SmsAbnormalNumber
|
||||||
|
|
||||||
|
err := e.Orm.Model(&data).
|
||||||
|
Scopes(
|
||||||
|
actions.Permission(data.TableName(), p),
|
||||||
|
).
|
||||||
|
First(model, d.GetId()).Error
|
||||||
|
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
err = errors.New("查看对象不存在或无权查看")
|
||||||
|
e.Log.Errorf("Service GetSmsAbnormalNumber error:%s \r\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("db error:%s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert 创建SmsAbnormalNumber对象
|
||||||
|
func (e *SmsAbnormalNumber) Insert(c *dto.SmsAbnormalNumberInsertReq) error {
|
||||||
|
var err error
|
||||||
|
var data models.SmsAbnormalNumber
|
||||||
|
c.Generate(&data)
|
||||||
|
err = e.Orm.Create(&data).Error
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("SmsAbnormalNumberService Insert error:%s \r\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update 修改SmsAbnormalNumber对象
|
||||||
|
func (e *SmsAbnormalNumber) Update(c *dto.SmsAbnormalNumberUpdateReq, p *actions.DataPermission) error {
|
||||||
|
var err error
|
||||||
|
var data = models.SmsAbnormalNumber{}
|
||||||
|
e.Orm.Scopes(
|
||||||
|
actions.Permission(data.TableName(), p),
|
||||||
|
).First(&data, c.GetId())
|
||||||
|
c.Generate(&data)
|
||||||
|
|
||||||
|
db := e.Orm.Save(&data)
|
||||||
|
if err = db.Error; err != nil {
|
||||||
|
e.Log.Errorf("SmsAbnormalNumberService Save error:%s \r\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if db.RowsAffected == 0 {
|
||||||
|
return errors.New("无权更新该数据")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove 删除SmsAbnormalNumber
|
||||||
|
func (e *SmsAbnormalNumber) Remove(d *dto.SmsAbnormalNumberDeleteReq, p *actions.DataPermission) error {
|
||||||
|
var data models.SmsAbnormalNumber
|
||||||
|
|
||||||
|
db := e.Orm.Model(&data).
|
||||||
|
Scopes(
|
||||||
|
actions.Permission(data.TableName(), p),
|
||||||
|
).Delete(&data, d.GetId())
|
||||||
|
if err := db.Error; err != nil {
|
||||||
|
e.Log.Errorf("Service RemoveSmsAbnormalNumber error:%s \r\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if db.RowsAffected == 0 {
|
||||||
|
return errors.New("无权删除该数据")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取平台上不再系统内的自动续期号码
|
||||||
|
func (e *SmsAbnormalNumber) SyncState() error {
|
||||||
|
textVerified := SmsTextVerified{Service: e.Service}
|
||||||
|
// daiSysms := SmsDaisysms{Service: e.Service} 没有列表api
|
||||||
|
|
||||||
|
textVerifiedNumbers, err := textVerified.GetAllPlatformNumbers()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("获取长效号码列表失败 %v", err)
|
||||||
|
} else {
|
||||||
|
// 获取系统内textverified平台的自动续期号码
|
||||||
|
// 参数: 平台代码, 号码类型(1=长效), 自动续费(1=开启), 激活状态(2=已激活)
|
||||||
|
systemNumbers, err := e.GetPhoneByPlatformCode(global.SmsPlatformTextVerified, 1, 1, 2)
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("获取系统内textverified自动续期号码失败: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Log.Infof("系统内textverified自动续期号码数量: %d", len(systemNumbers))
|
||||||
|
|
||||||
|
// 比较平台号码和系统号码,找出差异
|
||||||
|
missingNumbers := e.findMissingNumbers(textVerifiedNumbers, systemNumbers)
|
||||||
|
|
||||||
|
if len(missingNumbers) > 0 {
|
||||||
|
e.Log.Warnf("发现%d个平台开启自动续费但系统内缺失的号码", len(missingNumbers))
|
||||||
|
|
||||||
|
err = e.Orm.Transaction(func(tx *gorm.DB) error {
|
||||||
|
if err1 := tx.Model(&models.SmsAbnormalNumber{}).
|
||||||
|
Unscoped().
|
||||||
|
Where(" platform_code =?", global.SmsPlatformTextVerified).
|
||||||
|
Delete(&models.SmsAbnormalNumber{}).
|
||||||
|
Error; err1 != nil {
|
||||||
|
return err1
|
||||||
|
}
|
||||||
|
|
||||||
|
if err1 := tx.CreateInBatches(missingNumbers, 100).Error; err1 != nil {
|
||||||
|
return err1
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("同步后保存差异数据失败 %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
e.Log.Infof("所有平台自动续期号码在系统内都存在")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// findMissingNumbers 比较平台号码和系统号码,找出平台上有但系统内没有的号码
|
||||||
|
// 参数:
|
||||||
|
// - platformNumbers: 平台上的号码列表
|
||||||
|
// - systemNumbers: 系统内的号码列表
|
||||||
|
//
|
||||||
|
// 返回:
|
||||||
|
// - []dto.VerificationDTO: 平台上有但系统内缺失的号码列表
|
||||||
|
func (e *SmsAbnormalNumber) findMissingNumbers(platformNumbers map[string][]dto.VerificationDTO, systemNumbers []models.SmsPhone) []models.SmsAbnormalNumber {
|
||||||
|
// 创建系统号码的映射表,用于快速查找
|
||||||
|
systemNumberMap := make(map[string]bool)
|
||||||
|
for _, sysPhone := range systemNumbers {
|
||||||
|
// 使用号码作为key
|
||||||
|
systemNumberMap[sysPhone.Phone] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找出平台上有但系统内没有的号码
|
||||||
|
missingNumbers := make([]models.SmsAbnormalNumber, 0)
|
||||||
|
for account, platformPhone := range platformNumbers {
|
||||||
|
for _, v := range platformPhone {
|
||||||
|
if v.State == "renewableActive" && v.IsIncludedForNextRenewal && !systemNumberMap[v.Number] {
|
||||||
|
missingNumbers = append(missingNumbers, models.SmsAbnormalNumber{
|
||||||
|
PlatformCode: global.SmsPlatformTextVerified,
|
||||||
|
Account: account,
|
||||||
|
Phone: v.Number,
|
||||||
|
ActivationId: v.ID,
|
||||||
|
ReceivedTime: v.CreatedAt,
|
||||||
|
ApiKey: v.ApiKey,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return missingNumbers
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPhoneByPlatformCode 循环获取对应平台自动续期的号码
|
||||||
|
// 为避免一次性查询过多数据,使用分页查询,每次最多查询100条记录
|
||||||
|
// @param platformCode 平台代码 (如: textverified, daisysms)
|
||||||
|
// @return []models.SmsPhone 号码列表
|
||||||
|
// @return error 错误信息
|
||||||
|
func (e *SmsAbnormalNumber) GetPhoneByPlatformCode(platformCode string, numberType, autoRenewal, status int) ([]models.SmsPhone, error) {
|
||||||
|
var phones []models.SmsPhone
|
||||||
|
var allPhones []models.SmsPhone
|
||||||
|
|
||||||
|
// 分页参数
|
||||||
|
pageSize := 1000 // 每页最多1000条记录
|
||||||
|
offset := 0
|
||||||
|
|
||||||
|
// 循环分页查询,避免一次性查询过多数据
|
||||||
|
for {
|
||||||
|
// 查询条件:
|
||||||
|
// 1. platform_code = platformCode (指定平台)
|
||||||
|
// 2. type = numberType (号码类型)
|
||||||
|
// 3. auto_renewal = autoRenewal (自动续费状态)
|
||||||
|
// 4. actived = status (激活状态)
|
||||||
|
err := e.Orm.Model(&models.SmsPhone{}).
|
||||||
|
Where("platform_code = ? AND type = ? AND auto_renewal = ? AND actived = ? AND expire_time > ?",
|
||||||
|
platformCode, numberType, autoRenewal, status, time.Now()).
|
||||||
|
Order("id ASC").
|
||||||
|
Limit(pageSize).
|
||||||
|
Offset(offset).
|
||||||
|
Find(&phones).Error
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("查询平台[%s]自动续期号码失败: %v", platformCode, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有查询到数据,说明已经查询完毕
|
||||||
|
if len(phones) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将本次查询结果添加到总结果中
|
||||||
|
allPhones = append(allPhones, phones...)
|
||||||
|
|
||||||
|
// 如果本次查询结果少于pageSize,说明已经是最后一页
|
||||||
|
if len(phones) < pageSize {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新偏移量,准备查询下一页
|
||||||
|
offset += pageSize
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Log.Infof("平台[%s]查询到%d个自动续期号码", platformCode, len(allPhones))
|
||||||
|
return allPhones, nil
|
||||||
|
}
|
||||||
20
app/admin/service/sms_abnormal_number_test.go
Normal file
20
app/admin/service/sms_abnormal_number_test.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/go-admin-team/go-admin-core/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 同步差异号码
|
||||||
|
func TestSyncSat(t *testing.T) {
|
||||||
|
db := initSetting()
|
||||||
|
|
||||||
|
smsAbnormalNumber := SmsAbnormalNumber{}
|
||||||
|
smsAbnormalNumber.Orm = db
|
||||||
|
smsAbnormalNumber.Log = logger.NewHelper(logger.DefaultLogger)
|
||||||
|
|
||||||
|
if err := smsAbnormalNumber.SyncState(); err != nil {
|
||||||
|
t.Errorf("同步差异号码失败 %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -25,7 +25,9 @@ type SmsDaisysms struct {
|
|||||||
|
|
||||||
// 同步价格
|
// 同步价格
|
||||||
func (e SmsDaisysms) SyncPrices() {
|
func (e SmsDaisysms) SyncPrices() {
|
||||||
prices, err := e.GetPrices()
|
smsPlatformKeyRedis := NewSmsPlatformKeyRedis(e.Orm, e.Log)
|
||||||
|
apiInfo, err := smsPlatformKeyRedis.GetRoundRobinKey(global.SmsPlatformDaisysms)
|
||||||
|
prices, err := e.GetPrices(apiInfo)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.Log.Errorf("GetPrices error: %v", err)
|
e.Log.Errorf("GetPrices error: %v", err)
|
||||||
@ -44,16 +46,12 @@ func (e SmsDaisysms) SyncPrices() {
|
|||||||
// service 服务code
|
// service 服务code
|
||||||
// maxPrice 最大价格
|
// maxPrice 最大价格
|
||||||
// period 时长(月)
|
// period 时长(月)
|
||||||
func (e *SmsDaisysms) GetNumberForApi(getType int, serviceCode string, maxPrice decimal.Decimal, period int) (int, string, int) {
|
func (e *SmsDaisysms) GetNumberForApi(apiInfo *dto.SmsPlatformKeyQueueDto, getType int, serviceCode string, maxPrice decimal.Decimal, period int) (int, string, int) {
|
||||||
acitvationId := 0
|
acitvationId := 0
|
||||||
result := ""
|
result := ""
|
||||||
resultCode := statuscode.Success
|
resultCode := statuscode.Success
|
||||||
|
|
||||||
configResp, code := GetApiKey(e)
|
url := fmt.Sprintf("?api_key=%s&action=getNumber&service=%s", apiInfo.ApiKey, serviceCode)
|
||||||
if code != statuscode.Success {
|
|
||||||
return acitvationId, result, code
|
|
||||||
}
|
|
||||||
url := fmt.Sprintf("?api_key=%s&action=getNumber&service=%s", configResp.ConfigValue, serviceCode)
|
|
||||||
|
|
||||||
if getType == 1 {
|
if getType == 1 {
|
||||||
url = fmt.Sprintf("%s&duration=%dM", url, period)
|
url = fmt.Sprintf("%s&duration=%dM", url, period)
|
||||||
@ -103,13 +101,8 @@ func (e *SmsDaisysms) GetNumberForApi(getType int, serviceCode string, maxPrice
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 设置租赁结束
|
// 设置租赁结束
|
||||||
func (e *SmsDaisysms) setStatus(id int) int {
|
func (e *SmsDaisysms) setStatus(id int, apiInfo *dto.SmsPlatformKeyQueueDto) int {
|
||||||
configResp, code := GetApiKey(e)
|
url := fmt.Sprintf("?api_key=%s&action=setStatus&id=%d&status=6", apiInfo.ApiKey, id)
|
||||||
if code != statuscode.Success {
|
|
||||||
return code
|
|
||||||
}
|
|
||||||
|
|
||||||
url := fmt.Sprintf("?api_key=%s&action=setStatus&id=%d&status=6", configResp.ConfigValue, id)
|
|
||||||
client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil)
|
client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil)
|
||||||
bytes, _, err := client.GetRaw(url, nil)
|
bytes, _, err := client.GetRaw(url, nil)
|
||||||
|
|
||||||
@ -127,34 +120,14 @@ func (e *SmsDaisysms) setStatus(id int) int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetApiKey(e *SmsDaisysms) (dto.GetSysConfigByKEYForServiceResp, int) {
|
|
||||||
configService := SysConfig{Service: e.Service}
|
|
||||||
configResp := dto.GetSysConfigByKEYForServiceResp{}
|
|
||||||
err := configService.GetWithKey(&dto.SysConfigByKeyReq{ConfigKey: "sms_key"}, &configResp)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
e.Log.Errorf("获取短信api失败, %s", err)
|
|
||||||
return dto.GetSysConfigByKEYForServiceResp{}, statuscode.ServerError
|
|
||||||
}
|
|
||||||
|
|
||||||
if configResp.ConfigValue == "" {
|
|
||||||
e.Log.Error("短信api不能为空")
|
|
||||||
return dto.GetSysConfigByKEYForServiceResp{}, statuscode.ServerError
|
|
||||||
}
|
|
||||||
return configResp, statuscode.Success
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCodeForApi 获取验证码
|
// GetCodeForApi 获取验证码
|
||||||
// messageId 短信id
|
// messageId 短信id
|
||||||
// return 验证码, 状态码
|
// return 验证码, 状态码
|
||||||
func (e *SmsDaisysms) GetCodeForApi(messageId string) (string, int) {
|
func (e *SmsDaisysms) GetCodeForApi(messageId string, apiInfo *dto.SmsPlatformKeyQueueDto) (string, int) {
|
||||||
result := ""
|
result := ""
|
||||||
key, code := GetApiKey(e)
|
code := statuscode.Success
|
||||||
|
|
||||||
if code != statuscode.Success {
|
url := fmt.Sprintf("?api_key=%s&action=getStatus&id=%s", apiInfo.ApiKey, messageId)
|
||||||
return result, code
|
|
||||||
}
|
|
||||||
url := fmt.Sprintf("?api_key=%s&action=getStatus&id=%s", key.ConfigValue, messageId)
|
|
||||||
client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil)
|
client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil)
|
||||||
bytes, _, err := client.GetRaw(url, nil)
|
bytes, _, err := client.GetRaw(url, nil)
|
||||||
|
|
||||||
@ -183,13 +156,10 @@ func (e *SmsDaisysms) GetCodeForApi(messageId string) (string, int) {
|
|||||||
// getExtraActivation 获取额外的激活
|
// getExtraActivation 获取额外的激活
|
||||||
// messageId 短信id
|
// messageId 短信id
|
||||||
// return 验证码, 状态码
|
// return 验证码, 状态码
|
||||||
func (e *SmsDaisysms) getExtraActivation(activationId string) (int, int) {
|
func (e *SmsDaisysms) getExtraActivation(activationId string, apiInfo *dto.SmsPlatformKeyQueueDto) (int, int) {
|
||||||
result := 0
|
result := 0
|
||||||
key, err := GetApiKey(e)
|
|
||||||
if err != statuscode.Success {
|
url := fmt.Sprintf("?api_key=%s&action=getExtraActivation&activationId=%s", apiInfo.ApiKey, activationId)
|
||||||
return 0, statuscode.ServerError
|
|
||||||
}
|
|
||||||
url := fmt.Sprintf("?api_key=%s&action=getExtraActivation&activationId=%s", key.ConfigValue, activationId)
|
|
||||||
client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil)
|
client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil)
|
||||||
bytes, _, err1 := client.GetRaw(url, nil)
|
bytes, _, err1 := client.GetRaw(url, nil)
|
||||||
|
|
||||||
@ -224,14 +194,9 @@ func (e *SmsDaisysms) getExtraActivation(activationId string) (int, int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// KeepLongTerm 长期租赁
|
// KeepLongTerm 长期租赁
|
||||||
func (e *SmsDaisysms) KeepLongTerm(activationId string) int {
|
func (e *SmsDaisysms) KeepLongTerm(activationId string, apiInfo *dto.SmsPlatformKeyQueueDto) int {
|
||||||
key, code := GetApiKey(e)
|
|
||||||
|
|
||||||
if code != statuscode.Success {
|
url := fmt.Sprintf("?api_key=%s&action=keep&id=%s", apiInfo.ApiKey, activationId)
|
||||||
return statuscode.ServerError
|
|
||||||
}
|
|
||||||
|
|
||||||
url := fmt.Sprintf("?api_key=%s&action=keep&id=%s", key.ConfigValue, activationId)
|
|
||||||
client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil)
|
client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil)
|
||||||
bytes, _, err := client.GetRaw(url, nil)
|
bytes, _, err := client.GetRaw(url, nil)
|
||||||
|
|
||||||
@ -250,15 +215,8 @@ func (e *SmsDaisysms) KeepLongTerm(activationId string) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 取消租赁
|
// 取消租赁
|
||||||
func (e *SmsDaisysms) CancelRental(activationId string) int {
|
func (e *SmsDaisysms) CancelRental(activationId string, apiInfo *dto.SmsPlatformKeyQueueDto) int {
|
||||||
key, code := GetApiKey(e)
|
url := fmt.Sprintf("?api_key=%s&action=setStatus&id=%s&status=8", apiInfo.ApiKey, activationId)
|
||||||
|
|
||||||
if code != statuscode.Success {
|
|
||||||
e.Log.Errorf("租赁api请求失败 %s")
|
|
||||||
return statuscode.ServerError
|
|
||||||
}
|
|
||||||
|
|
||||||
url := fmt.Sprintf("?api_key=%s&action=setStatus&id=%s&status=8", key.ConfigValue, activationId)
|
|
||||||
client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil)
|
client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil)
|
||||||
bytes, _, err := client.GetRaw(url, nil)
|
bytes, _, err := client.GetRaw(url, nil)
|
||||||
|
|
||||||
@ -277,16 +235,10 @@ func (e *SmsDaisysms) CancelRental(activationId string) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取价格
|
// 获取价格
|
||||||
func (e *SmsDaisysms) GetPrices() ([]dto.DaisysmsPriceResp, error) {
|
func (e *SmsDaisysms) GetPrices(apiInfo *dto.SmsPlatformKeyQueueDto) ([]dto.DaisysmsPriceResp, error) {
|
||||||
result := make([]dto.DaisysmsPriceResp, 0)
|
result := make([]dto.DaisysmsPriceResp, 0)
|
||||||
key, code := GetApiKey(e)
|
|
||||||
|
|
||||||
if code != statuscode.Success {
|
url := fmt.Sprintf("?api_key=%s&action=getPrices", apiInfo.ApiKey)
|
||||||
e.Log.Errorf("租赁api请求失败 %s")
|
|
||||||
return result, errors.New("获取租赁ApiKey失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
url := fmt.Sprintf("?api_key=%s&action=getPrices", key.ConfigValue)
|
|
||||||
client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil)
|
client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil)
|
||||||
bytes, status, err := client.GetRaw(url, nil)
|
bytes, status, err := client.GetRaw(url, nil)
|
||||||
|
|
||||||
@ -327,15 +279,9 @@ func (e *SmsDaisysms) GetPrices() ([]dto.DaisysmsPriceResp, error) {
|
|||||||
// ChangeAutoRenew 修改自动续期
|
// ChangeAutoRenew 修改自动续期
|
||||||
// activationId 短信id
|
// activationId 短信id
|
||||||
// status 状态
|
// status 状态
|
||||||
func (e *SmsDaisysms) ChangeAutoRenewForApi(activationId string, status bool) int {
|
func (e *SmsDaisysms) ChangeAutoRenewForApi(activationId string, status bool, apiInfo *dto.SmsPlatformKeyQueueDto) int {
|
||||||
key, err := GetApiKey(e)
|
|
||||||
|
|
||||||
if err != statuscode.Success {
|
url := fmt.Sprintf("?api_key=%s&action=setAutoRenew&id=%s&value=%t", apiInfo.ApiKey, activationId, status)
|
||||||
e.Log.Errorf("查询sms api请求失败 %d", activationId)
|
|
||||||
return statuscode.ServerError
|
|
||||||
}
|
|
||||||
|
|
||||||
url := fmt.Sprintf("?api_key=%s&action=setAutoRenew&id=%s&value=%t", key.ConfigValue, activationId, status)
|
|
||||||
client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil)
|
client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil)
|
||||||
bytes, _, err1 := client.GetRaw(url, nil)
|
bytes, _, err1 := client.GetRaw(url, nil)
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
@ -16,6 +17,7 @@ import (
|
|||||||
cDto "go-admin/common/dto"
|
cDto "go-admin/common/dto"
|
||||||
"go-admin/common/global"
|
"go-admin/common/global"
|
||||||
"go-admin/common/statuscode"
|
"go-admin/common/statuscode"
|
||||||
|
"go-admin/utils/utility"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SmsPhone struct {
|
type SmsPhone struct {
|
||||||
@ -23,7 +25,7 @@ type SmsPhone struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// open-API 获取电话号码
|
// open-API 获取电话号码
|
||||||
func (e SmsPhone) OpenGetNumber(req *dto.GetNumberReq, userId int) (dto.OpenGetNumberResp, int) {
|
func (e *SmsPhone) OpenGetNumber(req *dto.GetNumberReq, userId int) (dto.OpenGetNumberResp, int) {
|
||||||
resp := dto.OpenGetNumberResp{}
|
resp := dto.OpenGetNumberResp{}
|
||||||
balanceService := MemberBalance{Service: e.Service}
|
balanceService := MemberBalance{Service: e.Service}
|
||||||
balance, smsPhone, i := e.DoGetNumber(&balanceService, req, userId)
|
balance, smsPhone, i := e.DoGetNumber(&balanceService, req, userId)
|
||||||
@ -36,6 +38,8 @@ func (e SmsPhone) OpenGetNumber(req *dto.GetNumberReq, userId int) (dto.OpenGetN
|
|||||||
resp.ActivationId = smsPhone.NewActivationId
|
resp.ActivationId = smsPhone.NewActivationId
|
||||||
resp.ExpireTime = smsPhone.ExpireTime
|
resp.ExpireTime = smsPhone.ExpireTime
|
||||||
resp.Number = smsPhone.Phone
|
resp.Number = smsPhone.Phone
|
||||||
|
resp.StartTime = smsPhone.StartTime
|
||||||
|
resp.EndTime = smsPhone.EndTime
|
||||||
|
|
||||||
return resp, statuscode.Success
|
return resp, statuscode.Success
|
||||||
}
|
}
|
||||||
@ -64,7 +68,7 @@ func (e *SmsPhone) CancelNumber(req *dto.SmsPhoneCancelNumberReq, userId int) in
|
|||||||
|
|
||||||
if code == statuscode.Success {
|
if code == statuscode.Success {
|
||||||
err := e.Orm.Transaction(func(tx *gorm.DB) error {
|
err := e.Orm.Transaction(func(tx *gorm.DB) error {
|
||||||
if err1 := e.Orm.Model(data).Updates(map[string]interface{}{"status": 3, "code": "", "actived": "3", "auto_renewal": 2}).Error; err1 != nil {
|
if err1 := e.Orm.Model(data).Updates(map[string]interface{}{"status": 3, "code": "", "actived": "3", "auto_renewal": 2, "deleted_at": time.Now()}).Error; err1 != nil {
|
||||||
e.Log.Errorf("更新短信号码失败, %s", err1)
|
e.Log.Errorf("更新短信号码失败, %s", err1)
|
||||||
return err1
|
return err1
|
||||||
}
|
}
|
||||||
@ -80,6 +84,11 @@ func (e *SmsPhone) CancelNumber(req *dto.SmsPhoneCancelNumberReq, userId int) in
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
e.Log.Errorf("取消租赁后修改失败, %s", err)
|
e.Log.Errorf("取消租赁后修改失败, %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err1 := e.Orm.
|
||||||
|
Where("phone_id = ?", data.Id).Delete(&models.SmsRenewalLog{}).Error; err1 != nil {
|
||||||
|
e.Log.Errorf("更新短信号码续费日志失败, %s", err1)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
e.Log.Errorf("取消租赁失败, %s", code)
|
e.Log.Errorf("取消租赁失败, %s", code)
|
||||||
return code
|
return code
|
||||||
@ -91,15 +100,22 @@ func (e *SmsPhone) CancelNumber(req *dto.SmsPhoneCancelNumberReq, userId int) in
|
|||||||
|
|
||||||
// 聚合取消
|
// 聚合取消
|
||||||
func (e *SmsPhone) CancelNumberManage(data *models.SmsPhone) int {
|
func (e *SmsPhone) CancelNumberManage(data *models.SmsPhone) int {
|
||||||
|
smsPlatformKeyRedis := NewSmsPlatformKeyRedis(e.Orm, e.Log)
|
||||||
|
apiInfo, err := smsPlatformKeyRedis.GetApiInfo(data.PlatformCode, data.ApiKey)
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("获取平台密钥失败, %s", err)
|
||||||
|
return statuscode.ServerError
|
||||||
|
}
|
||||||
|
|
||||||
switch data.PlatformCode {
|
switch data.PlatformCode {
|
||||||
case global.SmsPlatformDaisysms:
|
case global.SmsPlatformDaisysms:
|
||||||
service := SmsDaisysms{Service: e.Service}
|
service := SmsDaisysms{Service: e.Service}
|
||||||
|
|
||||||
return service.CancelRental(data.NewActivationId)
|
return service.CancelRental(data.NewActivationId, &apiInfo)
|
||||||
case global.SmsPlatformTextVerified:
|
case global.SmsPlatformTextVerified:
|
||||||
service := SmsTextVerified{Service: e.Service}
|
service := SmsTextVerified{Service: e.Service}
|
||||||
|
|
||||||
return service.CancelRental(data.NewActivationId, data.Type)
|
return service.CancelRental(data.NewActivationId, data.Type, &apiInfo)
|
||||||
default:
|
default:
|
||||||
return statuscode.SmsPlatformUnavailable
|
return statuscode.SmsPlatformUnavailable
|
||||||
}
|
}
|
||||||
@ -137,27 +153,41 @@ func (e *SmsPhone) DeleteMyNumber(req *dto.DeleteMyNumberReq, userId int) int {
|
|||||||
func (e *SmsPhone) WeakUp(req *dto.WeakUpReq, userId int, defult bool) (dto.WeakUpResp, int) {
|
func (e *SmsPhone) WeakUp(req *dto.WeakUpReq, userId int, defult bool) (dto.WeakUpResp, int) {
|
||||||
smsPhone := models.SmsPhone{}
|
smsPhone := models.SmsPhone{}
|
||||||
result := dto.WeakUpResp{}
|
result := dto.WeakUpResp{}
|
||||||
if err := e.Orm.Model(smsPhone).Where("activation_id =? and user_id= ?", req.ActivationId, userId).First(&smsPhone).Error; err != nil {
|
if err := e.Orm.Model(smsPhone).
|
||||||
|
Where("activation_id =? and user_id= ?", req.ActivationId, userId).
|
||||||
|
First(&smsPhone).Error; err != nil {
|
||||||
e.Log.Errorf("获取短信号码失败, %s", err)
|
e.Log.Errorf("获取短信号码失败, %s", err)
|
||||||
return result, statuscode.ServerError
|
return result, statuscode.ServerError
|
||||||
}
|
}
|
||||||
|
|
||||||
if smsPhone.Status != 1 || defult {
|
if smsPhone.Status != 1 || defult {
|
||||||
newActivationId, messageId, code := e.WeakUpManage(&smsPhone)
|
smsPlatformKeyRedis := NewSmsPlatformKeyRedis(e.Orm, e.Log)
|
||||||
|
apiInfo, err := smsPlatformKeyRedis.GetApiInfo(smsPhone.PlatformCode, smsPhone.ApiKey)
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("获取平台密钥失败, %s", err)
|
||||||
|
return result, statuscode.ServerError
|
||||||
|
}
|
||||||
|
newActivationId, messageId, startTime, endTime, code := e.WeakUpManage(&smsPhone, &apiInfo)
|
||||||
|
|
||||||
if code == statuscode.ServerError {
|
if code == statuscode.ServerError {
|
||||||
return result, code
|
return result, code
|
||||||
} else if code == statuscode.Success {
|
} else if code == statuscode.Success {
|
||||||
if err := e.Orm.Model(smsPhone).Updates(map[string]interface{}{"new_activation_id": newActivationId, "status": 1, "code": "", "message_id": messageId}).Error; err != nil {
|
if err := e.Orm.Model(smsPhone).
|
||||||
|
Updates(map[string]interface{}{
|
||||||
|
"new_activation_id": newActivationId, "status": 1, "code": "",
|
||||||
|
"message_id": messageId, "start_time": startTime, "end_time": endTime}).Error; err != nil {
|
||||||
e.Log.Errorf("更新短信号码失败, %s", err)
|
e.Log.Errorf("更新短信号码失败, %s", err)
|
||||||
return result, statuscode.ServerError
|
return result, statuscode.ServerError
|
||||||
}
|
}
|
||||||
|
|
||||||
result.ActivationId = newActivationId
|
result.ActivationId = newActivationId
|
||||||
result.MessageId = messageId
|
result.MessageId = messageId
|
||||||
|
result.StartTime = startTime
|
||||||
|
result.EndTime = endTime
|
||||||
} else if code == statuscode.SmsLongNumWaitCode {
|
} else if code == statuscode.SmsLongNumWaitCode {
|
||||||
e.Log.Info("无须唤醒")
|
if err := e.Orm.Model(smsPhone).
|
||||||
if err := e.Orm.Model(smsPhone).Updates(map[string]interface{}{"status": 1, "code": ""}).Error; err != nil {
|
Updates(map[string]interface{}{"status": 1, "code": "", "start_time": startTime, "end_time": endTime}).
|
||||||
|
Error; err != nil {
|
||||||
e.Log.Errorf("更新短信号码失败, %s", err)
|
e.Log.Errorf("更新短信号码失败, %s", err)
|
||||||
return result, statuscode.ServerError
|
return result, statuscode.ServerError
|
||||||
}
|
}
|
||||||
@ -171,18 +201,18 @@ func (e *SmsPhone) WeakUp(req *dto.WeakUpReq, userId int, defult bool) (dto.Weak
|
|||||||
|
|
||||||
// 唤醒号码
|
// 唤醒号码
|
||||||
// returns newActivationId,messageId, code
|
// returns newActivationId,messageId, code
|
||||||
func (e *SmsPhone) WeakUpManage(req *models.SmsPhone) (string, string, int) {
|
func (e *SmsPhone) WeakUpManage(req *models.SmsPhone, apiInfo *dto.SmsPlatformKeyQueueDto) (string, string, *time.Time, *time.Time, int) {
|
||||||
switch req.PlatformCode {
|
switch req.PlatformCode {
|
||||||
case global.SmsPlatformDaisysms:
|
case global.SmsPlatformDaisysms:
|
||||||
service := SmsDaisysms{Service: e.Service}
|
service := SmsDaisysms{Service: e.Service}
|
||||||
id, code := service.getExtraActivation(req.ActivationId)
|
id, code := service.getExtraActivation(req.ActivationId, apiInfo)
|
||||||
return strconv.Itoa(id), "", code
|
return strconv.Itoa(id), "", nil, nil, code
|
||||||
case global.SmsPlatformTextVerified:
|
case global.SmsPlatformTextVerified:
|
||||||
service := SmsTextVerified{Service: e.Service}
|
service := SmsTextVerified{Service: e.Service}
|
||||||
|
resp, code := service.getExtraActivation(req.NewActivationId, apiInfo, 0)
|
||||||
return service.getExtraActivation(req.NewActivationId)
|
return resp.ReservationId, resp.Id, resp.UsageWindowStart, resp.UsageWindowEnd, code
|
||||||
default:
|
default:
|
||||||
return "", "", statuscode.SmsPlatformUnavailable
|
return "", "", nil, nil, statuscode.SmsPlatformUnavailable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,6 +231,17 @@ func (e *SmsPhone) GetMyPage(req *dto.SmsPhoneGetPageReq, userId int, phone *[]m
|
|||||||
return statuscode.ServerError
|
return statuscode.ServerError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dictService := SysDictData{}
|
||||||
|
dictService.Orm = e.Orm
|
||||||
|
dictService.Log = e.Log
|
||||||
|
dictDatas, _ := dictService.GetMapByType("sms_platform")
|
||||||
|
|
||||||
|
for index := range *phone {
|
||||||
|
if dict, ok := dictDatas[(*phone)[index].PlatformCode]; ok {
|
||||||
|
(*phone)[index].PlatformName = dict.DictLabel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return statuscode.Success
|
return statuscode.Success
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,7 +322,7 @@ func (e *SmsPhone) DoGetNumber(balanceService *MemberBalance, req *dto.GetNumber
|
|||||||
}
|
}
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
activationId, phone, code, expireTime := e.GetNumberManage(req.PlatformCode, req.Type, req.ServiceCode, price, req.Period)
|
numberResp, code, apiInfo := e.GetNumberManage(req.PlatformCode, req.Type, req.ServiceCode, price, req.Period)
|
||||||
|
|
||||||
if code != statuscode.Success {
|
if code != statuscode.Success {
|
||||||
return decimal.Decimal{}, models.SmsPhone{}, code
|
return decimal.Decimal{}, models.SmsPhone{}, code
|
||||||
@ -289,30 +330,38 @@ func (e *SmsPhone) DoGetNumber(balanceService *MemberBalance, req *dto.GetNumber
|
|||||||
|
|
||||||
smsPhone := models.SmsPhone{}
|
smsPhone := models.SmsPhone{}
|
||||||
smsPhone.PlatformCode = req.PlatformCode
|
smsPhone.PlatformCode = req.PlatformCode
|
||||||
smsPhone.Phone = phone
|
smsPhone.Phone = numberResp.Phone
|
||||||
smsPhone.UserId = userId
|
smsPhone.UserId = userId
|
||||||
smsPhone.Service = serviceItem.Name
|
smsPhone.Service = serviceItem.Name
|
||||||
smsPhone.ServiceCode = req.ServiceCode
|
smsPhone.ServiceCode = req.ServiceCode
|
||||||
smsPhone.Type = req.Type
|
smsPhone.Type = req.Type
|
||||||
smsPhone.Period = req.Period
|
smsPhone.Period = req.Period
|
||||||
smsPhone.ActivationId = activationId
|
smsPhone.ActivationId = numberResp.ActivationId
|
||||||
smsPhone.MessageId = activationId
|
smsPhone.MessageId = numberResp.ActivationId
|
||||||
smsPhone.NewActivationId = activationId
|
smsPhone.NewActivationId = numberResp.ActivationId
|
||||||
smsPhone.Actived = 1
|
smsPhone.Actived = 1
|
||||||
|
smsPhone.StartTime = numberResp.StartTime
|
||||||
|
smsPhone.EndTime = numberResp.EndTime
|
||||||
|
smsPhone.ApiKey = apiInfo.ApiKey
|
||||||
|
smsPhone.BillingCycleId = numberResp.BillingCycleId
|
||||||
smsPhone.Price = price
|
smsPhone.Price = price
|
||||||
|
|
||||||
if req.Type == 1 {
|
if req.Type == 1 {
|
||||||
if expireTime != nil {
|
if numberResp.ExpireTime != nil {
|
||||||
smsPhone.ExpireTime = expireTime
|
smsPhone.ExpireTime = numberResp.ExpireTime
|
||||||
} else {
|
} else {
|
||||||
days := req.Period * 30
|
days := req.Period * 30
|
||||||
now = now.AddDate(0, 0, days)
|
now = now.AddDate(0, 0, days)
|
||||||
smsPhone.ExpireTime = &now
|
smsPhone.ExpireTime = &now
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//平台不允许取消
|
||||||
|
if req.PlatformCode == global.SmsPlatformTextVerified {
|
||||||
|
smsPhone.Actived = 2
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if expireTime != nil {
|
if numberResp.ExpireTime != nil {
|
||||||
smsPhone.ExpireTime = expireTime
|
smsPhone.ExpireTime = numberResp.ExpireTime
|
||||||
} else {
|
} else {
|
||||||
now = now.Add(time.Minute * time.Duration(serviceItem.ExpirationMinutes))
|
now = now.Add(time.Minute * time.Duration(serviceItem.ExpirationMinutes))
|
||||||
smsPhone.ExpireTime = &now
|
smsPhone.ExpireTime = &now
|
||||||
@ -336,7 +385,7 @@ func (e *SmsPhone) DoGetNumber(balanceService *MemberBalance, req *dto.GetNumber
|
|||||||
if req.Type == 1 {
|
if req.Type == 1 {
|
||||||
var code int
|
var code int
|
||||||
if req.PlatformCode == global.SmsPlatformDaisysms {
|
if req.PlatformCode == global.SmsPlatformDaisysms {
|
||||||
code = e.ChangeAutoRenewManage(smsPhone.PlatformCode, smsPhone.NewActivationId, true)
|
code = e.ChangeAutoRenewManage(smsPhone.PlatformCode, smsPhone.ApiKey, smsPhone.NewActivationId, true)
|
||||||
} else {
|
} else {
|
||||||
code = http.StatusOK
|
code = http.StatusOK
|
||||||
e.WeakUp(&dto.WeakUpReq{ActivationId: smsPhone.NewActivationId}, userId, true)
|
e.WeakUp(&dto.WeakUpReq{ActivationId: smsPhone.NewActivationId}, userId, true)
|
||||||
@ -351,28 +400,72 @@ func (e *SmsPhone) DoGetNumber(balanceService *MemberBalance, req *dto.GetNumber
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log := models.SmsRenewalLog{}
|
||||||
|
log.PhoneId = smsPhone.Id
|
||||||
|
log.PayOrderNo = utility.GenerateTraceID()
|
||||||
|
log.Phone = smsPhone.Phone
|
||||||
|
log.Type = smsPhone.Type
|
||||||
|
log.Category = 1
|
||||||
|
log.UserId = userId
|
||||||
|
log.Amount = price
|
||||||
|
log.BeforeTime = time.Now()
|
||||||
|
log.Period = req.Period * 30
|
||||||
|
log.Status = 2
|
||||||
|
log.TargetTime = smsPhone.ExpireTime
|
||||||
|
|
||||||
|
if err := e.Orm.Save(&log).Error; err != nil {
|
||||||
|
e.Log.Errorf("保存短信续费日志失败,phone_id:%d, %s", smsPhone.Id, err)
|
||||||
|
}
|
||||||
|
|
||||||
return balance, smsPhone, statuscode.Success
|
return balance, smsPhone, statuscode.Success
|
||||||
}
|
}
|
||||||
|
|
||||||
// 租赁号码
|
// 租赁号码
|
||||||
func (e *SmsPhone) GetNumberManage(platformCode string, typ int, serviceCode string, price decimal.Decimal, period int) (string, string, int, *time.Time) {
|
// return activationId 激活id
|
||||||
|
// phone 号码
|
||||||
|
// code 状态码
|
||||||
|
// *expirateTime 号码过期时间
|
||||||
|
// *startTime 长效号码单次接码开始使用(textverified有用)
|
||||||
|
// *endTime 长效号码单次接码过期时间(textverified有用)
|
||||||
|
func (e *SmsPhone) GetNumberManage(platformCode string, typ int, serviceCode string,
|
||||||
|
price decimal.Decimal, period int) (dto.SmsPhoneGetNumberResp, int, *dto.SmsPlatformKeyQueueDto) {
|
||||||
|
smsPlatformKeyRedis := NewSmsPlatformKeyRedis(e.Orm, e.Log)
|
||||||
|
queue, err := smsPlatformKeyRedis.GetRoundRobinKey(platformCode)
|
||||||
|
result := dto.SmsPhoneGetNumberResp{}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("获取短信平台队列失败, %s", err)
|
||||||
|
return result, statuscode.ServerError, queue
|
||||||
|
}
|
||||||
|
|
||||||
switch platformCode {
|
switch platformCode {
|
||||||
case global.SmsPlatformDaisysms:
|
case global.SmsPlatformDaisysms:
|
||||||
service := SmsDaisysms{Service: e.Service}
|
service := SmsDaisysms{Service: e.Service}
|
||||||
activationId, phone, code := service.GetNumberForApi(typ, serviceCode, price, period)
|
activationId, phone, code := service.GetNumberForApi(queue, typ, serviceCode, price, period)
|
||||||
|
result.ActivationId = strconv.Itoa(activationId)
|
||||||
|
result.Phone = phone
|
||||||
|
result.ExpireTime = nil
|
||||||
|
result.StartTime = nil
|
||||||
|
result.EndTime = nil
|
||||||
|
|
||||||
return strconv.Itoa(activationId), phone, code, nil
|
return result, code, queue
|
||||||
case global.SmsPlatformTextVerified:
|
case global.SmsPlatformTextVerified:
|
||||||
service := SmsTextVerified{Service: e.Service}
|
service := SmsTextVerified{Service: e.Service}
|
||||||
resp, code := service.GetNumberForApi(typ, serviceCode, price, period)
|
resp, code := service.GetNumberAndWakeUp(typ, serviceCode, price, period, queue)
|
||||||
|
result.ActivationId = resp.Id
|
||||||
|
result.Phone = resp.Phone
|
||||||
|
result.ExpireTime = resp.EndAt
|
||||||
|
result.StartTime = resp.StartTime
|
||||||
|
result.EndTime = resp.EndTime
|
||||||
|
result.BillingCycleId = resp.BillingCycleId
|
||||||
|
|
||||||
if code != statuscode.Success {
|
if code != statuscode.Success {
|
||||||
return "", "", code, nil
|
return result, code, queue
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp.Id, resp.Phone, code, resp.EndAt
|
return result, code, queue
|
||||||
default:
|
default:
|
||||||
return "", "", statuscode.SmsPlatformUnavailable, nil
|
return result, statuscode.SmsPlatformUnavailable, queue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -414,13 +507,38 @@ func (e *SmsPhone) SyncCodes() error {
|
|||||||
if err := e.Orm.Model(models.SmsPhone{}).Where("status =1").Find(&phones).Error; err != nil {
|
if err := e.Orm.Model(models.SmsPhone{}).Where("status =1").Find(&phones).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
mapData := make(map[string]*dto.SmsPlatformKeyQueueDto)
|
||||||
|
smsPlatformKeyRedis := NewSmsPlatformKeyRedis(e.Orm, e.Log)
|
||||||
|
|
||||||
for _, item := range phones {
|
for _, item := range phones {
|
||||||
code, codeStatus := e.GetCodeManage(item.PlatformCode, item.NewActivationId, item.Type, item.UpdatedAt.Unix())
|
key := fmt.Sprintf("%s:%s", item.PlatformCode, item.ApiKey)
|
||||||
|
|
||||||
|
if _, ok := mapData[key]; !ok {
|
||||||
|
apiInfo, _ := smsPlatformKeyRedis.GetApiInfo(item.PlatformCode, item.ApiKey)
|
||||||
|
|
||||||
|
if apiInfo.ApiKey != "" {
|
||||||
|
mapData[key] = &apiInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range phones {
|
||||||
|
apiInfo, ok := mapData[fmt.Sprintf("%s:%s", item.PlatformCode, item.ApiKey)]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
code, codeStatus := e.GetCodeManage(item.PlatformCode, item.NewActivationId,
|
||||||
|
item.Type, item.UserId, item.Service, item.ServiceCode, apiInfo)
|
||||||
|
|
||||||
if code == "" {
|
if code == "" {
|
||||||
expireTime := item.UpdatedAt.Add(time.Second * 150)
|
var expireTime time.Time
|
||||||
if item.PlatformCode == global.SmsPlatformTextVerified && expireTime.Before(time.Now()) {
|
|
||||||
|
if item.EndTime != nil {
|
||||||
|
expireTime = *item.EndTime
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.Status == 3 && item.Actived == 2) || (item.PlatformCode == global.SmsPlatformTextVerified && expireTime.Before(time.Now())) {
|
||||||
if err := e.Orm.Model(&item).Updates(map[string]interface{}{"status": 3, "updated_at": time.Now()}).Error; err != nil {
|
if err := e.Orm.Model(&item).Updates(map[string]interface{}{"status": 3, "updated_at": time.Now()}).Error; err != nil {
|
||||||
e.Log.Errorf("手机号一睡眠 %s", item.Phone)
|
e.Log.Errorf("手机号一睡眠 %s", item.Phone)
|
||||||
}
|
}
|
||||||
@ -473,106 +591,56 @@ func (e *SmsPhone) SyncCodes() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取验证码
|
// 获取验证码
|
||||||
func (e *SmsPhone) GetCodeManage(platformCode string, messageId string, typ int, unixTime int64) (string, int) {
|
func (e *SmsPhone) GetCodeManage(platformCode string, messageId string, typ int, userId int, smsService, serviceCode string, apiInfo *dto.SmsPlatformKeyQueueDto) (string, int) {
|
||||||
switch platformCode {
|
switch platformCode {
|
||||||
case global.SmsPlatformDaisysms:
|
case global.SmsPlatformDaisysms:
|
||||||
service := SmsDaisysms{Service: e.Service}
|
service := SmsDaisysms{Service: e.Service}
|
||||||
|
|
||||||
return service.GetCodeForApi(messageId)
|
return service.GetCodeForApi(messageId, apiInfo)
|
||||||
case global.SmsPlatformTextVerified:
|
case global.SmsPlatformTextVerified:
|
||||||
service := SmsTextVerified{Service: e.Service}
|
service := SmsTextVerified{Service: e.Service}
|
||||||
return service.GetCode(messageId, typ, unixTime)
|
return service.GetCode(messageId, typ, userId, smsService, serviceCode, apiInfo)
|
||||||
default:
|
default:
|
||||||
return "", statuscode.SmsPlatformUnavailable
|
return "", statuscode.SmsPlatformUnavailable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 自动续期
|
|
||||||
func (e SmsServices) AutoRenewal() error {
|
|
||||||
var datas []models.SmsPhone
|
|
||||||
var data models.SmsPhone
|
|
||||||
startTime := time.Now().Add(-24 * time.Hour)
|
|
||||||
endTime := time.Now().Add(24 * time.Hour)
|
|
||||||
|
|
||||||
smsServices := SmsServices{Service: e.Service}
|
|
||||||
serviceMap := smsServices.GetMapAll()
|
|
||||||
|
|
||||||
configService := SysConfig{Service: e.Service}
|
|
||||||
mapDatas, _ := configService.GetMapByKeys([]string{"renew_number_premium_daisysms", "renew_number_premium_textverified"})
|
|
||||||
|
|
||||||
if err := e.Orm.Model(&models.SmsPhone{}).Where("auto_renewal =1 and type =1 and actived =2 and expire_time >=? and expire_time <?", startTime, endTime).Find(&datas).Error; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, item := range datas {
|
|
||||||
percent := decimal.NewFromInt(1)
|
|
||||||
price := decimal.Zero
|
|
||||||
|
|
||||||
if service, ok := serviceMap[item.PlatformCode+"_"+item.ServiceCode]; !ok {
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
price = service.LongPrice
|
|
||||||
switch {
|
|
||||||
case item.PlatformCode == global.SmsPlatformDaisysms:
|
|
||||||
p, ok := mapDatas["renew_number_premium_daisysms"]
|
|
||||||
val, _ := decimal.NewFromString(p.ConfigValue)
|
|
||||||
|
|
||||||
if ok && val.Cmp(decimal.Zero) > 0 {
|
|
||||||
per := decimal.NewFromInt(100).Add(val)
|
|
||||||
percent = per.Div(decimal.NewFromInt(100)).Truncate(2)
|
|
||||||
}
|
|
||||||
case item.PlatformCode == global.SmsPlatformTextVerified:
|
|
||||||
p, ok := mapDatas["renew_number_premium_textverified"]
|
|
||||||
val, _ := decimal.NewFromString(p.ConfigValue)
|
|
||||||
|
|
||||||
if ok && val.Cmp(decimal.Zero) > 0 {
|
|
||||||
per := decimal.NewFromInt(100).Add(val)
|
|
||||||
percent = per.Div(decimal.NewFromInt(100)).Truncate(2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
renewLog := models.SmsRenewalLog{}
|
|
||||||
renewLog.UserId = item.UserId
|
|
||||||
renewLog.PhoneId = item.Id
|
|
||||||
renewLog.Type = item.Type
|
|
||||||
renewLog.Amount = price.Mul(percent).Truncate(2)
|
|
||||||
renewLog.BeforeTime = *item.ExpireTime
|
|
||||||
renewLog.Period = 30
|
|
||||||
|
|
||||||
if err := e.Orm.Transaction(func(tx *gorm.DB) error {
|
|
||||||
db := tx.Exec("UPDATE member_balance set balance = balance - ? where user_id = ? and balance >= ?", price, item.UserId, price)
|
|
||||||
|
|
||||||
if db.Error != nil {
|
|
||||||
return db.Error
|
|
||||||
}
|
|
||||||
|
|
||||||
if db.RowsAffected == 0 {
|
|
||||||
return errors.New("余额不足")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err1 := tx.Create(&renewLog).Error; err1 != nil {
|
|
||||||
|
|
||||||
return errors.New("创建续费日志失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
newExpireTime := item.ExpireTime.AddDate(0, 0, 30)
|
|
||||||
if err1 := tx.Model(data).Where("id =?", item.Id).Update("expire_time", newExpireTime).Error; err1 != nil {
|
|
||||||
return err1
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}); err != nil {
|
|
||||||
e.Log.Errorf("自动续期失败:%s", err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChangeAutoRenew 修改自动续期
|
// ChangeAutoRenew 修改自动续期
|
||||||
func (e *SmsPhone) ChangeAutoRenew(req *dto.SmsPhoneChangeAutoRenewReq, userId int) int {
|
func (e *SmsPhone) ChangeAutoRenew(req *dto.SmsPhoneChangeAutoRenewReq, userId int) int {
|
||||||
var data models.SmsPhone
|
var data models.SmsPhone
|
||||||
|
|
||||||
|
if req.AutoRenew == 1 {
|
||||||
|
smsServices := SmsServices{Service: e.Service}
|
||||||
|
balanceService := MemberBalance{Service: e.Service}
|
||||||
|
balance := balanceService.GetBalance(userId)
|
||||||
|
|
||||||
|
if err := e.Orm.Model(data).Where("id =? or activation_id =?", req.Id, req.ActivationId).First(&data).Error; err != nil {
|
||||||
|
e.Log.Errorf("修改自动续期 数据不存在 id:%d err:%s", req.Id, err.Error())
|
||||||
|
return statuscode.SmsNotExisted
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.ExpireTime.Before(time.Now()) {
|
||||||
|
return statuscode.SmsRentalCantRenew
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceItem, err := smsServices.GetByCode(data.PlatformCode, data.ServiceCode)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("短信服务报错:%v", err)
|
||||||
|
return statuscode.ServerError
|
||||||
|
}
|
||||||
|
configService := SysConfig{Service: e.Service}
|
||||||
|
configKeys := []string{"renew_number_premium_daisysms", "renew_number_premium_textverified"}
|
||||||
|
premiumMap, _ := configService.GetMapByKeys(configKeys)
|
||||||
|
renewalService := SmsRenewalLog{Service: e.Service}
|
||||||
|
|
||||||
|
price := renewalService.calculateRenewalPrice(serviceItem.LongPrice, serviceItem.PlatformCode, premiumMap)
|
||||||
|
|
||||||
|
if balance.Cmp(price) < 0 {
|
||||||
|
return statuscode.BalanceNotEnough
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if req.Id > 0 {
|
if req.Id > 0 {
|
||||||
if err := e.Orm.Model(data).Where("id =? and user_id =?", req.Id, userId).First(&data).Error; err != nil {
|
if err := e.Orm.Model(data).Where("id =? and user_id =?", req.Id, userId).First(&data).Error; err != nil {
|
||||||
e.Log.Errorf("修改自动续期 数据不存在 id:%d err:%s", req.Id, err.Error())
|
e.Log.Errorf("修改自动续期 数据不存在 id:%d err:%s", req.Id, err.Error())
|
||||||
@ -615,7 +683,7 @@ func (e *SmsPhone) DoChangeAutoRenewal(data models.SmsPhone, autoRenewal int) (b
|
|||||||
if data.Actived == 3 {
|
if data.Actived == 3 {
|
||||||
code = http.StatusOK
|
code = http.StatusOK
|
||||||
} else {
|
} else {
|
||||||
code = e.ChangeAutoRenewManage(data.PlatformCode, data.ActivationId, status)
|
code = e.ChangeAutoRenewManage(data.PlatformCode, data.ApiKey, data.ActivationId, status)
|
||||||
|
|
||||||
if code != statuscode.Success {
|
if code != statuscode.Success {
|
||||||
return true, statuscode.ServerError
|
return true, statuscode.ServerError
|
||||||
@ -629,16 +697,24 @@ func (e *SmsPhone) DoChangeAutoRenewal(data models.SmsPhone, autoRenewal int) (b
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ChangeAutoRenewManage 修改自动续期管理
|
// ChangeAutoRenewManage 修改自动续期管理
|
||||||
func (e *SmsPhone) ChangeAutoRenewManage(platform string, activationId string, status bool) int {
|
func (e *SmsPhone) ChangeAutoRenewManage(platform, apiKey string, activationId string, status bool) int {
|
||||||
|
smsPlatformKeyRedis := NewSmsPlatformKeyRedis(e.Orm, e.Log)
|
||||||
|
apiInfo, err := smsPlatformKeyRedis.GetApiInfo(platform, apiKey)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("获取平台密钥失败: %v", err)
|
||||||
|
return statuscode.ServerError
|
||||||
|
}
|
||||||
|
|
||||||
switch platform {
|
switch platform {
|
||||||
case global.SmsPlatformDaisysms:
|
case global.SmsPlatformDaisysms:
|
||||||
service := SmsDaisysms{Service: e.Service}
|
service := SmsDaisysms{Service: e.Service}
|
||||||
|
|
||||||
return service.ChangeAutoRenewForApi(activationId, status)
|
return service.ChangeAutoRenewForApi(activationId, status, &apiInfo)
|
||||||
case global.SmsPlatformTextVerified:
|
case global.SmsPlatformTextVerified:
|
||||||
service := SmsTextVerified{Service: e.Service}
|
service := SmsTextVerified{Service: e.Service}
|
||||||
|
|
||||||
return service.Renew(activationId, status)
|
return service.Renew(activationId, status, &apiInfo)
|
||||||
default:
|
default:
|
||||||
return statuscode.SmsPlatformUnavailable
|
return statuscode.SmsPlatformUnavailable
|
||||||
}
|
}
|
||||||
@ -649,12 +725,20 @@ func (e *SmsPhone) GetPage(c *dto.SmsPhoneGetPageReq, p *actions.DataPermission,
|
|||||||
var err error
|
var err error
|
||||||
var data models.SmsPhone
|
var data models.SmsPhone
|
||||||
|
|
||||||
err = e.Orm.Model(&data).
|
query := e.Orm.Model(&data).
|
||||||
Scopes(
|
Scopes(
|
||||||
cDto.MakeCondition(c.GetNeedSearch()),
|
cDto.MakeCondition(c.GetNeedSearch()),
|
||||||
cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
|
cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
|
||||||
actions.Permission(data.TableName(), p),
|
actions.Permission(data.TableName(), p),
|
||||||
).
|
)
|
||||||
|
|
||||||
|
switch c.IsActived {
|
||||||
|
case 1:
|
||||||
|
query = query.Where("expire_time > ?", time.Now())
|
||||||
|
case 2:
|
||||||
|
query = query.Where("expire_time < ?", time.Now())
|
||||||
|
}
|
||||||
|
err = query.
|
||||||
Find(list).Limit(-1).Offset(-1).
|
Find(list).Limit(-1).Offset(-1).
|
||||||
Count(count).Error
|
Count(count).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -767,3 +851,71 @@ func (e *SmsPhone) DeleteExpired() error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 同步手动续费id
|
||||||
|
func (e *SmsPhone) SyncBilingCycleId() error {
|
||||||
|
var logs []models.SmsPhone
|
||||||
|
logMap := map[string]models.SmsPhone{}
|
||||||
|
if err := e.Orm.Model(&models.SmsPhone{}).Where("billing_cycle_id = '' or billing_cycle_id is null").Find(&logs).Error; err != nil {
|
||||||
|
e.Log.Errorf("查询手动续费id为空的记录失败 err:%v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range logs {
|
||||||
|
logMap[item.ActivationId] = item
|
||||||
|
}
|
||||||
|
|
||||||
|
textVerifiedService := SmsTextVerified{Service: e.Service}
|
||||||
|
smsPlatformKeyRedis := NewSmsPlatformKeyRedis(e.Orm, e.Log)
|
||||||
|
apiInfo, err := smsPlatformKeyRedis.GetApiInfo(global.SmsPlatformTextVerified, "ZQ0swXnsaPpeGdwa3c7gT9U9I1Oh9WoDHx0amuYovvaHuqd5u6B4NBBUSUBjR")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("获取平台密钥失败: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = e.syncLoop("", textVerifiedService, apiInfo, logMap)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SmsPhone) syncLoop(nextUrl string, textVerifiedService SmsTextVerified, apiInfo dto.SmsPlatformKeyQueueDto, logMap map[string]models.SmsPhone) (dto.TextVerifiedGetRentalListResp, error) {
|
||||||
|
if len(logMap) == 0 {
|
||||||
|
return dto.TextVerifiedGetRentalListResp{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
rentalList, err := textVerifiedService.GetRentalList(nextUrl, &apiInfo)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("获取平台密钥失败: %v", err)
|
||||||
|
return dto.TextVerifiedGetRentalListResp{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, log := range rentalList.Data {
|
||||||
|
if log.BillingCycleId == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if item, ok := logMap[log.ID]; ok {
|
||||||
|
if err1 := e.Orm.Model(&models.SmsPhone{}).Where("activation_id = ?", item.ActivationId).Update("billing_cycle_id", log.BillingCycleId).Error; err1 != nil {
|
||||||
|
e.Log.Errorf("更新手动续费id失败 err:%v", err1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(logMap, item.ActivationId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rentalList.HasNext {
|
||||||
|
time.Sleep(150 * time.Millisecond)
|
||||||
|
rentalList, err = e.syncLoop(rentalList.Links.Next.Href, textVerifiedService, apiInfo, logMap)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("同步续费id失败 err:%v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rentalList, nil
|
||||||
|
}
|
||||||
|
|||||||
1
app/admin/service/sms_phone_extend.go
Normal file
1
app/admin/service/sms_phone_extend.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package service
|
||||||
@ -4,6 +4,7 @@ import (
|
|||||||
"go-admin/app/admin/service/dto"
|
"go-admin/app/admin/service/dto"
|
||||||
"go-admin/common/global"
|
"go-admin/common/global"
|
||||||
"go-admin/common/statuscode"
|
"go-admin/common/statuscode"
|
||||||
|
"go-admin/utils/utility"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/go-admin-team/go-admin-core/logger"
|
"github.com/go-admin-team/go-admin-core/logger"
|
||||||
@ -63,3 +64,31 @@ func TestGetPrices(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSyncRenewalLogs(t *testing.T) {
|
||||||
|
db := initSetting()
|
||||||
|
|
||||||
|
service := SmsPhone{}
|
||||||
|
service.Orm = db
|
||||||
|
service.Log = logger.NewHelper(logger.DefaultLogger)
|
||||||
|
|
||||||
|
if err := service.SyncBilingCycleId(); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestManualRenewal(t *testing.T) {
|
||||||
|
db := initSetting()
|
||||||
|
|
||||||
|
service := SmsRenewalLog{}
|
||||||
|
service.Orm = db
|
||||||
|
service.Log = logger.NewHelper(logger.DefaultLogger)
|
||||||
|
activationId := "lr_01K5DP71G06SFX84S7W99D61F9"
|
||||||
|
|
||||||
|
if _, err := service.ManualDeduct(&dto.ManualDeductReq{
|
||||||
|
ActivationId: activationId,
|
||||||
|
TradeOrderNo: utility.GenerateTraceID(),
|
||||||
|
}, 1); err != statuscode.Success {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
314
app/admin/service/sms_platform_key.go
Normal file
314
app/admin/service/sms_platform_key.go
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/go-admin-team/go-admin-core/sdk/service"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
|
||||||
|
"go-admin/app/admin/models"
|
||||||
|
"go-admin/app/admin/service/dto"
|
||||||
|
"go-admin/common/actions"
|
||||||
|
cDto "go-admin/common/dto"
|
||||||
|
"go-admin/utils/utility"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SmsPlatformKey struct {
|
||||||
|
service.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPage 获取SmsPlatformKey列表
|
||||||
|
func (e *SmsPlatformKey) GetPage(c *dto.SmsPlatformKeyGetPageReq, p *actions.DataPermission, list *[]models.SmsPlatformKey, count *int64) error {
|
||||||
|
var err error
|
||||||
|
var data models.SmsPlatformKey
|
||||||
|
|
||||||
|
err = e.Orm.Model(&data).
|
||||||
|
Scopes(
|
||||||
|
cDto.MakeCondition(c.GetNeedSearch()),
|
||||||
|
cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
|
||||||
|
actions.Permission(data.TableName(), p),
|
||||||
|
).
|
||||||
|
Find(list).Limit(-1).Offset(-1).
|
||||||
|
Count(count).Error
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("SmsPlatformKeyService GetPage error:%s \r\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for index := range *list {
|
||||||
|
(*list)[index].ApiKey = utility.DesensitizeGeneric((*list)[index].ApiKey, 3, 3, '*')
|
||||||
|
(*list)[index].ApiSecret = utility.DesensitizeGeneric((*list)[index].ApiSecret, 3, 3, '*')
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRandomKey 随机获取一个密钥
|
||||||
|
func (e *SmsPlatformKey) GetRandomKey(platformCode string) (*dto.SmsPlatformKeyQueueDto, error) {
|
||||||
|
redisService := NewSmsPlatformKeyRedis(e.Orm, e.Log)
|
||||||
|
return redisService.GetRandomKey(platformCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRoundRobinKey 轮询获取密钥
|
||||||
|
func (e *SmsPlatformKey) GetRoundRobinKey(platformCode string) (*dto.SmsPlatformKeyQueueDto, error) {
|
||||||
|
redisService := NewSmsPlatformKeyRedis(e.Orm, e.Log)
|
||||||
|
return redisService.GetRoundRobinKey(platformCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetQueueLength 获取队列长度
|
||||||
|
func (e *SmsPlatformKey) GetQueueLength(platformCode string) (int64, error) {
|
||||||
|
redisService := NewSmsPlatformKeyRedis(e.Orm, e.Log)
|
||||||
|
return redisService.GetQueueLength(platformCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearPlatformQueue 清空指定平台的队列
|
||||||
|
func (e *SmsPlatformKey) ClearPlatformQueue(platformCode string) error {
|
||||||
|
redisService := NewSmsPlatformKeyRedis(e.Orm, e.Log)
|
||||||
|
return redisService.ClearPlatformQueue(platformCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get 获取SmsPlatformKey对象
|
||||||
|
func (e *SmsPlatformKey) Get(d *dto.SmsPlatformKeyGetReq, p *actions.DataPermission, model *models.SmsPlatformKey) error {
|
||||||
|
var data models.SmsPlatformKey
|
||||||
|
|
||||||
|
err := e.Orm.Model(&data).
|
||||||
|
Scopes(
|
||||||
|
actions.Permission(data.TableName(), p),
|
||||||
|
).
|
||||||
|
First(model, d.GetId()).Error
|
||||||
|
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
err = errors.New("查看对象不存在或无权查看")
|
||||||
|
e.Log.Errorf("Service GetSmsPlatformKey error:%s \r\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("db error:%s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert 创建SmsPlatformKey对象
|
||||||
|
func (e *SmsPlatformKey) Insert(c *dto.SmsPlatformKeyInsertReq) error {
|
||||||
|
var err error
|
||||||
|
var data models.SmsPlatformKey
|
||||||
|
var count int64
|
||||||
|
c.Generate(&data)
|
||||||
|
|
||||||
|
if err := e.Orm.Model(&models.SmsPlatformKey{}).
|
||||||
|
Where("platform_code = ? and api_key =? and account=?", data.PlatformCode, data.ApiKey, data.Account).
|
||||||
|
Count(&count).Error; err != nil {
|
||||||
|
e.Log.Errorf("SmsPlatformKeyService Insert error:%s \r\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if count > 0 {
|
||||||
|
return errors.New("ApiKey或账号 已存在")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = e.Orm.Create(&data).Error
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("SmsPlatformKeyService Insert error:%s \r\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := e.AddQueque(dto.SmsPlatformKeyQueueDto{
|
||||||
|
PlatformCode: data.PlatformCode,
|
||||||
|
Account: data.Account,
|
||||||
|
ApiKey: data.ApiKey,
|
||||||
|
ApiSecret: data.ApiSecret,
|
||||||
|
}); err != nil {
|
||||||
|
e.Log.Errorf("添加队列失败,%v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update 修改SmsPlatformKey对象
|
||||||
|
func (e *SmsPlatformKey) Update(c *dto.SmsPlatformKeyUpdateReq, p *actions.DataPermission) error {
|
||||||
|
var err error
|
||||||
|
var data = models.SmsPlatformKey{}
|
||||||
|
var count int64
|
||||||
|
e.Orm.Scopes(
|
||||||
|
actions.Permission(data.TableName(), p),
|
||||||
|
).First(&data, c.GetId())
|
||||||
|
oldKey := data.ApiKey
|
||||||
|
oldSecret := data.ApiSecret
|
||||||
|
oldStatus := data.Status
|
||||||
|
oldPlatformCode := data.PlatformCode
|
||||||
|
|
||||||
|
if err1 := e.Orm.Model(&models.SmsPlatformKey{}).
|
||||||
|
Where("platform_code = ? and api_key =? and account=? and id != ?",
|
||||||
|
data.PlatformCode, data.ApiKey, data.Account, data.Id).
|
||||||
|
Count(&count).Error; err1 != nil {
|
||||||
|
e.Log.Errorf("SmsPlatformKeyService Insert error:%s \r\n", err1)
|
||||||
|
return err1
|
||||||
|
}
|
||||||
|
if count > 0 {
|
||||||
|
return errors.New("ApiKey或账号 已存在")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Generate(&data)
|
||||||
|
|
||||||
|
// 如果要将启用状态改为禁用状态,需要检查该平台是否还有其他启用的密钥
|
||||||
|
if data.Status == 2 {
|
||||||
|
var remainingCount int64
|
||||||
|
err1 := e.Orm.Model(&models.SmsPlatformKey{}).
|
||||||
|
Where("platform_code = ? AND status = ? AND id != ?", oldPlatformCode, 1, c.GetId()).
|
||||||
|
Count(&remainingCount).Error
|
||||||
|
if err1 != nil {
|
||||||
|
e.Log.Errorf("检查平台剩余启用密钥失败: %v", err1)
|
||||||
|
return errors.New("检查平台剩余启用密钥失败")
|
||||||
|
}
|
||||||
|
if remainingCount == 0 {
|
||||||
|
return fmt.Errorf("平台 %s 至少需要保留一个启用状态的密钥,无法禁用", oldPlatformCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
db := e.Orm.Save(&data)
|
||||||
|
if err = db.Error; err != nil {
|
||||||
|
e.Log.Errorf("SmsPlatformKeyService Save error:%s \r\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if db.RowsAffected == 0 {
|
||||||
|
return errors.New("无权更新该数据")
|
||||||
|
}
|
||||||
|
|
||||||
|
if oldStatus == 1 && data.Status == 2 {
|
||||||
|
if err := e.RemoveQueque(dto.SmsPlatformKeyQueueDto{
|
||||||
|
PlatformCode: data.PlatformCode,
|
||||||
|
Account: data.Account,
|
||||||
|
ApiKey: data.ApiKey,
|
||||||
|
ApiSecret: data.ApiSecret,
|
||||||
|
}, false); err != nil {
|
||||||
|
e.Log.Errorf("删除失败,%v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else if oldStatus == 2 && data.Status == 1 {
|
||||||
|
if err := e.AddQueque(dto.SmsPlatformKeyQueueDto{
|
||||||
|
PlatformCode: data.PlatformCode,
|
||||||
|
Account: data.Account,
|
||||||
|
ApiKey: data.ApiKey,
|
||||||
|
ApiSecret: data.ApiSecret,
|
||||||
|
}); err != nil {
|
||||||
|
e.Log.Errorf("添加队列失败,%v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
oldQueueData := dto.SmsPlatformKeyQueueDto{
|
||||||
|
PlatformCode: data.PlatformCode,
|
||||||
|
Account: data.Account,
|
||||||
|
ApiKey: oldKey,
|
||||||
|
ApiSecret: oldSecret,
|
||||||
|
}
|
||||||
|
queueData := dto.SmsPlatformKeyQueueDto{
|
||||||
|
PlatformCode: data.PlatformCode,
|
||||||
|
Account: data.Account,
|
||||||
|
ApiKey: data.ApiKey,
|
||||||
|
ApiSecret: data.ApiSecret,
|
||||||
|
}
|
||||||
|
if err := e.Replace(oldQueueData, queueData); err != nil {
|
||||||
|
e.Log.Errorf("替换队列失败,%v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove 删除SmsPlatformKey
|
||||||
|
func (e *SmsPlatformKey) Remove(d *dto.SmsPlatformKeyDeleteReq, p *actions.DataPermission) error {
|
||||||
|
var data models.SmsPlatformKey
|
||||||
|
var datas []models.SmsPlatformKey
|
||||||
|
|
||||||
|
if err1 := e.Orm.Where("id in (?)", d.GetId()).Find(&datas).Error; err1 != nil {
|
||||||
|
e.Log.Errorf("Service RemoveSmsPlatformKey error:%s \r\n", err1)
|
||||||
|
return err1
|
||||||
|
}
|
||||||
|
|
||||||
|
var platformCodes []string
|
||||||
|
|
||||||
|
for _, item := range datas {
|
||||||
|
if utility.ContainsString(platformCodes, item.PlatformCode) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
platformCodes = append(platformCodes, item.PlatformCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := e.Orm.Transaction(func(tx *gorm.DB) error {
|
||||||
|
db := tx.Model(&data).
|
||||||
|
Scopes(
|
||||||
|
actions.Permission(data.TableName(), p),
|
||||||
|
).Delete(&data, d.GetId())
|
||||||
|
|
||||||
|
var count []string
|
||||||
|
|
||||||
|
if err1 := tx.Model(&data).Where("status =1").
|
||||||
|
Group("platform_code").
|
||||||
|
Select("platform_code").
|
||||||
|
Scan(&count).
|
||||||
|
Error; err1 != nil {
|
||||||
|
e.Log.Errorf("Service RemoveSmsPlatformKey error:%s \r\n", err1)
|
||||||
|
return err1
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range platformCodes {
|
||||||
|
if !utility.ContainsString(count, item) {
|
||||||
|
return errors.New("删除失败,通道最少需要保留一个可用ApiKey")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.Error; err != nil {
|
||||||
|
e.Log.Errorf("Service RemoveSmsPlatformKey error:%s \r\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if db.RowsAffected == 0 {
|
||||||
|
return errors.New("无权删除该数据")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range datas {
|
||||||
|
queueDta := dto.SmsPlatformKeyQueueDto{
|
||||||
|
PlatformCode: item.PlatformCode,
|
||||||
|
Account: data.Account,
|
||||||
|
ApiKey: item.ApiKey,
|
||||||
|
ApiSecret: item.ApiSecret,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := e.RemoveQueque(queueDta, true); err != nil {
|
||||||
|
e.Log.Errorf("移出队列失败,%v", err)
|
||||||
|
return errors.New("删除队列失败")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitQueque 初始化Redis缓存队列
|
||||||
|
func (e *SmsPlatformKey) InitQueque() error {
|
||||||
|
redisService := NewSmsPlatformKeyRedis(e.Orm, e.Log)
|
||||||
|
return redisService.InitRedisQueque()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace 替换Redis缓存中的密钥
|
||||||
|
func (e *SmsPlatformKey) Replace(oldEntity dto.SmsPlatformKeyQueueDto, entity dto.SmsPlatformKeyQueueDto) error {
|
||||||
|
redisService := NewSmsPlatformKeyRedis(e.Orm, e.Log)
|
||||||
|
return redisService.ReplaceRedisKey(oldEntity, entity)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveQueque 从Redis缓存中移出队列
|
||||||
|
func (e *SmsPlatformKey) RemoveQueque(entity dto.SmsPlatformKeyQueueDto, shouldDel bool) error {
|
||||||
|
redisService := NewSmsPlatformKeyRedis(e.Orm, e.Log)
|
||||||
|
return redisService.RemoveRedisQueque(entity, shouldDel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddQueque 添加到Redis缓存队列
|
||||||
|
func (e *SmsPlatformKey) AddQueque(entity dto.SmsPlatformKeyQueueDto) error {
|
||||||
|
redisService := NewSmsPlatformKeyRedis(e.Orm, e.Log)
|
||||||
|
return redisService.AddRedisQueque(entity)
|
||||||
|
}
|
||||||
327
app/admin/service/sms_platform_key_redis.go
Normal file
327
app/admin/service/sms_platform_key_redis.go
Normal file
@ -0,0 +1,327 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go-admin/app/admin/models"
|
||||||
|
"go-admin/app/admin/service/dto"
|
||||||
|
"go-admin/utils/redishelper"
|
||||||
|
|
||||||
|
"github.com/bytedance/sonic"
|
||||||
|
"github.com/go-admin-team/go-admin-core/logger"
|
||||||
|
"github.com/go-admin-team/go-admin-core/sdk/service"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SmsPlatformKeyRedis Redis版本的SMS平台密钥管理服务
|
||||||
|
type SmsPlatformKeyRedis struct {
|
||||||
|
SmsPlatformKey
|
||||||
|
redisHelper *redishelper.RedisHelper
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSmsPlatformKeyRedis 创建Redis版本的SMS平台密钥服务
|
||||||
|
func NewSmsPlatformKeyRedis(orm *gorm.DB, log logger.Logger) *SmsPlatformKeyRedis {
|
||||||
|
return &SmsPlatformKeyRedis{
|
||||||
|
SmsPlatformKey: SmsPlatformKey{
|
||||||
|
Service: service.Service{
|
||||||
|
Orm: orm,
|
||||||
|
Log: logger.NewHelper(log),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
redisHelper: redishelper.DefaultRedis,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getRedisKey 获取Redis键名
|
||||||
|
func (e *SmsPlatformKeyRedis) getRedisKey(platformCode string) string {
|
||||||
|
return fmt.Sprintf("sms:platform:keys:%s", platformCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getRedisIndexKey 获取Redis索引键名(用于快速查找)
|
||||||
|
func (e *SmsPlatformKeyRedis) getRedisIndexKey(platformCode, apiKey string) string {
|
||||||
|
return fmt.Sprintf("sms:platform:index:%s:%s", platformCode, apiKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SmsPlatformKeyRedis) GetApiInfo(platformCode, apiKey string) (dto.SmsPlatformKeyQueueDto, error) {
|
||||||
|
indexKey := e.getRedisIndexKey(platformCode, apiKey)
|
||||||
|
data, err := e.redisHelper.GetString(indexKey)
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("获取Redis索引失败 [%s:%s]: %v", platformCode, apiKey, err)
|
||||||
|
return dto.SmsPlatformKeyQueueDto{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var apiInfo dto.SmsPlatformKeyQueueDto
|
||||||
|
err = sonic.Unmarshal([]byte(data), &apiInfo)
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("解析Redis数据失败 [%s:%s]: %v", platformCode, apiKey, err)
|
||||||
|
return dto.SmsPlatformKeyQueueDto{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitRedisQueque 初始化Redis缓存队列
|
||||||
|
// 从数据库加载所有SMS平台密钥并存储到Redis中
|
||||||
|
func (e *SmsPlatformKeyRedis) InitRedisQueque() error {
|
||||||
|
// 查询所有启用的SMS平台密钥
|
||||||
|
var list []models.SmsPlatformKey
|
||||||
|
err := e.Orm.Where("status = ?", 1).Find(&list).Error
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("查询SMS平台密钥失败: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(list) == 0 {
|
||||||
|
e.Log.Warn("未找到启用的SMS平台密钥")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按平台分组
|
||||||
|
platformKeyMap := make(map[string][]dto.SmsPlatformKeyQueueDto)
|
||||||
|
for _, item := range list {
|
||||||
|
data := dto.SmsPlatformKeyQueueDto{
|
||||||
|
PlatformCode: item.PlatformCode,
|
||||||
|
Account: item.Account,
|
||||||
|
ApiKey: item.ApiKey,
|
||||||
|
ApiSecret: item.ApiSecret,
|
||||||
|
}
|
||||||
|
platformKeyMap[item.PlatformCode] = append(platformKeyMap[item.PlatformCode], data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将每个平台的密钥存储到Redis
|
||||||
|
for platformCode, keys := range platformKeyMap {
|
||||||
|
redisKey := e.getRedisKey(platformCode)
|
||||||
|
|
||||||
|
// 清空现有数据
|
||||||
|
err := e.redisHelper.DeleteString(redisKey)
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("清空Redis队列失败 [%s]: %v", platformCode, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 存储密钥列表
|
||||||
|
for _, key := range keys {
|
||||||
|
keyJson, _ := json.Marshal(key)
|
||||||
|
err := e.redisHelper.RPushList(redisKey, string(keyJson))
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("添加密钥到Redis队列失败 [%s]: %v", platformCode, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建索引,便于快速查找和删除
|
||||||
|
indexKey := e.getRedisIndexKey(platformCode, key.ApiKey)
|
||||||
|
err = e.redisHelper.SetString(indexKey, string(keyJson))
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("创建密钥索引失败 [%s:%s]: %v", platformCode, key.ApiKey, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置过期时间(24小时)
|
||||||
|
err = e.redisHelper.SetKeyExpiration(redisKey, 24*time.Hour)
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("设置Redis队列过期时间失败 [%s]: %v", platformCode, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Log.Infof("平台 [%s] 初始化 %d 个密钥到Redis缓存", platformCode, len(keys))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRedisQueque 添加密钥到Redis缓存
|
||||||
|
func (e *SmsPlatformKeyRedis) AddRedisQueque(entity dto.SmsPlatformKeyQueueDto) error {
|
||||||
|
redisKey := e.getRedisKey(entity.PlatformCode)
|
||||||
|
indexKey := e.getRedisIndexKey(entity.PlatformCode, entity.ApiKey)
|
||||||
|
|
||||||
|
// 检查是否已存在
|
||||||
|
// exists, err := e.redisHelper.GetString(indexKey)
|
||||||
|
// if err == nil && exists != "" {
|
||||||
|
// return errors.New("密钥已存在")
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 序列化密钥数据
|
||||||
|
keyJson, err := json.Marshal(entity)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("序列化密钥数据失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加到队列
|
||||||
|
err = e.redisHelper.RPushList(redisKey, string(keyJson))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("添加密钥到Redis队列失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建索引
|
||||||
|
err = e.redisHelper.SetString(indexKey, string(keyJson))
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("创建密钥索引失败: %v", err)
|
||||||
|
// 索引创建失败不影响主要功能
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置过期时间
|
||||||
|
err = e.redisHelper.SetKeyExpiration(redisKey, 24*time.Hour)
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("设置Redis队列过期时间失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Log.Infof("成功添加密钥到Redis缓存 [%s:%s]", entity.PlatformCode, entity.ApiKey)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveRedisQueque 从Redis缓存中删除密钥
|
||||||
|
// shouldDel 是否删除索引
|
||||||
|
func (e *SmsPlatformKeyRedis) RemoveRedisQueque(entity dto.SmsPlatformKeyQueueDto, shouldDel bool) error {
|
||||||
|
redisKey := e.getRedisKey(entity.PlatformCode)
|
||||||
|
indexKey := e.getRedisIndexKey(entity.PlatformCode, entity.ApiKey)
|
||||||
|
|
||||||
|
// 获取要删除的密钥数据
|
||||||
|
keyData, err := e.redisHelper.GetString(indexKey)
|
||||||
|
if err != nil || keyData == "" {
|
||||||
|
return errors.New("密钥不存在")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从队列中删除
|
||||||
|
_, err = e.redisHelper.LRem(redisKey, keyData)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("从Redis队列删除密钥失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除索引
|
||||||
|
if shouldDel {
|
||||||
|
err = e.redisHelper.DeleteString(indexKey)
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("删除密钥索引失败: %v", err)
|
||||||
|
// 索引删除失败不影响主要功能
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// e.Log.Infof("成功从Redis缓存删除密钥 [%s:%s]", entity.PlatformCode, entity.ApiKey)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRandomKey 随机获取一个密钥
|
||||||
|
func (e *SmsPlatformKeyRedis) GetRandomKey(platformCode string) (*dto.SmsPlatformKeyQueueDto, error) {
|
||||||
|
redisKey := e.getRedisKey(platformCode)
|
||||||
|
|
||||||
|
// 获取所有密钥
|
||||||
|
keys, err := e.redisHelper.GetAllList(redisKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("获取Redis队列失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(keys) == 0 {
|
||||||
|
return nil, errors.New("队列为空")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 随机选择一个
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
randomIndex := rand.Intn(len(keys))
|
||||||
|
keyJson := keys[randomIndex]
|
||||||
|
|
||||||
|
var keyDto dto.SmsPlatformKeyQueueDto
|
||||||
|
err = json.Unmarshal([]byte(keyJson), &keyDto)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("反序列化密钥数据失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &keyDto, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRoundRobinKey 轮询获取密钥
|
||||||
|
func (e *SmsPlatformKeyRedis) GetRoundRobinKey(platformCode string) (*dto.SmsPlatformKeyQueueDto, error) {
|
||||||
|
redisKey := e.getRedisKey(platformCode)
|
||||||
|
|
||||||
|
// 从队列头部取出一个密钥
|
||||||
|
keyJson, err := e.redisHelper.LPopList(redisKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("从Redis队列获取密钥失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if keyJson == "" {
|
||||||
|
return nil, errors.New("队列为空")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将密钥重新放到队列尾部(实现轮询)
|
||||||
|
err = e.redisHelper.RPushList(redisKey, keyJson)
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("轮询密钥回放失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var keyDto dto.SmsPlatformKeyQueueDto
|
||||||
|
err = json.Unmarshal([]byte(keyJson), &keyDto)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("反序列化密钥数据失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &keyDto, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetQueueLength 获取队列长度
|
||||||
|
func (e *SmsPlatformKeyRedis) GetQueueLength(platformCode string) (int64, error) {
|
||||||
|
redisKey := e.getRedisKey(platformCode)
|
||||||
|
keys, err := e.redisHelper.GetAllList(redisKey)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("获取Redis队列长度失败: %v", err)
|
||||||
|
}
|
||||||
|
return int64(len(keys)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplaceRedisKey 替换Redis缓存中的密钥
|
||||||
|
func (e *SmsPlatformKeyRedis) ReplaceRedisKey(oldEntity, newEntity dto.SmsPlatformKeyQueueDto) error {
|
||||||
|
// 先删除旧密钥
|
||||||
|
err := e.RemoveRedisQueque(oldEntity, true)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("删除旧密钥失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 再添加新密钥
|
||||||
|
err = e.AddRedisQueque(newEntity)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("添加新密钥失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// e.Log.Infof("成功替换Redis缓存密钥 [%s] %s -> %s", newEntity.PlatformCode, oldEntity.ApiKey, newEntity.ApiKey)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearPlatformQueue 清空指定平台的队列
|
||||||
|
func (e *SmsPlatformKeyRedis) ClearPlatformQueue(platformCode string) error {
|
||||||
|
redisKey := e.getRedisKey(platformCode)
|
||||||
|
err := e.redisHelper.DeleteString(redisKey)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("清空平台队列失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空相关索引
|
||||||
|
pattern := fmt.Sprintf("sms:platform:index:%s:*", platformCode)
|
||||||
|
err = e.redisHelper.DeleteKeysByPrefix(pattern)
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("清空平台索引失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Log.Infof("成功清空平台 [%s] 的Redis队列", platformCode)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取平台所有密钥
|
||||||
|
func (e *SmsPlatformKeyRedis) GetPlatformKeys(platformCode string) ([]dto.SmsPlatformKeyQueueDto, error) {
|
||||||
|
redisKey := e.getRedisKey(platformCode)
|
||||||
|
keys, err := e.redisHelper.GetAllList(redisKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("获取Redis队列失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var keyDtos []dto.SmsPlatformKeyQueueDto
|
||||||
|
for _, keyJson := range keys {
|
||||||
|
var keyDto dto.SmsPlatformKeyQueueDto
|
||||||
|
err = json.Unmarshal([]byte(keyJson), &keyDto)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("反序列化密钥数据失败: %v", err)
|
||||||
|
}
|
||||||
|
keyDtos = append(keyDtos, keyDto)
|
||||||
|
}
|
||||||
|
return keyDtos, nil
|
||||||
|
}
|
||||||
23
app/admin/service/sms_platform_key_redis_test.go
Normal file
23
app/admin/service/sms_platform_key_redis_test.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go-admin/common/global"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/go-admin-team/go-admin-core/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNextKey(t *testing.T) {
|
||||||
|
orm := initSetting()
|
||||||
|
|
||||||
|
redisService := NewSmsPlatformKeyRedis(orm, logger.DefaultLogger)
|
||||||
|
|
||||||
|
val, err := redisService.GetRoundRobinKey(global.SmsPlatformTextVerified)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("GetRoundRobinKey error:%s \r\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("val:%s \r\n", val)
|
||||||
|
}
|
||||||
@ -2,6 +2,7 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/go-admin-team/go-admin-core/sdk/service"
|
"github.com/go-admin-team/go-admin-core/sdk/service"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
@ -38,7 +39,7 @@ func (e SmsReceiveLog) WebHook(req *dto.SmsReceiveWebHookReq) error {
|
|||||||
UserId: phoneLog.UserId,
|
UserId: phoneLog.UserId,
|
||||||
Service: phoneLog.Service,
|
Service: phoneLog.Service,
|
||||||
ServiceCode: phoneLog.ServiceCode,
|
ServiceCode: phoneLog.ServiceCode,
|
||||||
MessageId: req.MessageID,
|
MessageId: strconv.Itoa(req.MessageID),
|
||||||
Phone: phoneLog.Phone,
|
Phone: phoneLog.Phone,
|
||||||
Code: req.Code,
|
Code: req.Code,
|
||||||
Status: 2,
|
Status: 2,
|
||||||
@ -48,8 +49,16 @@ func (e SmsReceiveLog) WebHook(req *dto.SmsReceiveWebHookReq) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
smsPlatformKeyRedis := NewSmsPlatformKeyRedis(e.Orm, e.Log)
|
||||||
|
apiInfo, err := smsPlatformKeyRedis.GetApiInfo(phoneLog.PlatformCode, phoneLog.ApiKey)
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("获取平台密钥失败, %s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
phoneService := SmsDaisysms{Service: e.Service}
|
phoneService := SmsDaisysms{Service: e.Service}
|
||||||
if code := phoneService.setStatus(req.ActivationID); code != statuscode.Success {
|
|
||||||
|
if code := phoneService.setStatus(req.ActivationID, &apiInfo); code != statuscode.Success {
|
||||||
e.Log.Errorf("接受验证码回调后修改状态失败 %d", code)
|
e.Log.Errorf("接受验证码回调后修改状态失败 %d", code)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import (
|
|||||||
"go-admin/app/admin/service/dto"
|
"go-admin/app/admin/service/dto"
|
||||||
"go-admin/config"
|
"go-admin/config"
|
||||||
"go-admin/utils/redishelper"
|
"go-admin/utils/redishelper"
|
||||||
|
"go-admin/utils/utility"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/go-admin-team/go-admin-core/logger"
|
"github.com/go-admin-team/go-admin-core/logger"
|
||||||
@ -13,9 +14,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func initSetting() *gorm.DB {
|
func initSetting() *gorm.DB {
|
||||||
dsn := "root:123456@tcp(127.0.0.1:3306)/proxy_server?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms"
|
dsn := "root:123456@tcp(127.0.0.1:3306)/proxy_server_prod?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms"
|
||||||
db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})
|
db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})
|
||||||
sdk.Runtime.SetDb("default", db)
|
sdk.Runtime.SetDb("default", db)
|
||||||
|
// config.ExtConfig.
|
||||||
config.ExtConfig.TrxGridUrl = "https://api.trongrid.io"
|
config.ExtConfig.TrxGridUrl = "https://api.trongrid.io"
|
||||||
config.ExtConfig.DaisysmsUrl = "https://daisysms.com/stubs/handler_api.php"
|
config.ExtConfig.DaisysmsUrl = "https://daisysms.com/stubs/handler_api.php"
|
||||||
config.ExtConfig.SmsTextVerified.ApiKey = "ZQ0swXnsaPpeGdwa3c7gT9U9I1Oh9WoDHx0amuYovvaHuqd5u6B4NBBUSUBjR"
|
config.ExtConfig.SmsTextVerified.ApiKey = "ZQ0swXnsaPpeGdwa3c7gT9U9I1Oh9WoDHx0amuYovvaHuqd5u6B4NBBUSUBjR"
|
||||||
@ -24,6 +26,7 @@ func initSetting() *gorm.DB {
|
|||||||
redishelper.InitDefaultRedis("127.0.0.1:6379", "", 4)
|
redishelper.InitDefaultRedis("127.0.0.1:6379", "", 4)
|
||||||
redishelper.InitLockRedisConn("127.0.0.1:6379", "", "4")
|
redishelper.InitLockRedisConn("127.0.0.1:6379", "", "4")
|
||||||
|
|
||||||
|
utility.InitSnowflake()
|
||||||
return db
|
return db
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,14 +2,21 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-admin-team/go-admin-core/logger"
|
||||||
"github.com/go-admin-team/go-admin-core/sdk/service"
|
"github.com/go-admin-team/go-admin-core/sdk/service"
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"go-admin/app/admin/models"
|
"go-admin/app/admin/models"
|
||||||
"go-admin/app/admin/service/dto"
|
"go-admin/app/admin/service/dto"
|
||||||
"go-admin/common/actions"
|
"go-admin/common/actions"
|
||||||
cDto "go-admin/common/dto"
|
cDto "go-admin/common/dto"
|
||||||
|
"go-admin/common/global"
|
||||||
|
"go-admin/common/statuscode"
|
||||||
|
"go-admin/utils/utility"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SmsRenewalLog struct {
|
type SmsRenewalLog struct {
|
||||||
@ -17,22 +24,45 @@ type SmsRenewalLog struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetPage 获取SmsRenewalLog列表
|
// GetPage 获取SmsRenewalLog列表
|
||||||
func (e *SmsRenewalLog) GetPage(c *dto.SmsRenewalLogGetPageReq, p *actions.DataPermission, list *[]models.SmsRenewalLog, count *int64) error {
|
func (e *SmsRenewalLog) GetPage(c *dto.SmsRenewalLogGetPageReq, p *actions.DataPermission, list *[]dto.SmsRenewalLogResp, count *int64) error {
|
||||||
var err error
|
var err error
|
||||||
var data models.SmsRenewalLog
|
var data models.SmsRenewalLog
|
||||||
|
var datas []models.SmsRenewalLog
|
||||||
err = e.Orm.Model(&data).
|
query := e.Orm.Model(&data).
|
||||||
|
Joins("left join sys_user on sys_user.user_id = sms_renewal_log.user_id").
|
||||||
Scopes(
|
Scopes(
|
||||||
cDto.MakeCondition(c.GetNeedSearch()),
|
cDto.MakeCondition(c.GetNeedSearch()),
|
||||||
cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
|
cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
|
||||||
actions.Permission(data.TableName(), p),
|
actions.Permission(data.TableName(), p),
|
||||||
).
|
)
|
||||||
Find(list).Limit(-1).Offset(-1).
|
|
||||||
|
if c.UserName != "" {
|
||||||
|
query = query.Where("sys_user.username LIKE ?", "%"+c.UserName+"%")
|
||||||
|
}
|
||||||
|
err = query.Select("sms_renewal_log.*, sys_user.username").Find(&datas).Limit(-1).Offset(-1).
|
||||||
Count(count).Error
|
Count(count).Error
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.Log.Errorf("SmsRenewalLogService GetPage error:%s \r\n", err)
|
e.Log.Errorf("SmsRenewalLogService GetPage error:%s \r\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, v := range datas {
|
||||||
|
*list = append(*list, dto.SmsRenewalLogResp{
|
||||||
|
Id: v.Id,
|
||||||
|
UserId: v.UserId,
|
||||||
|
Category: v.Category,
|
||||||
|
Phone: v.Phone,
|
||||||
|
Amount: v.Amount,
|
||||||
|
BeforeTime: v.BeforeTime,
|
||||||
|
Period: v.Period,
|
||||||
|
CreatedAt: v.CreatedAt,
|
||||||
|
UserName: v.Username,
|
||||||
|
TradeOrderNo: v.TradeOrderNo,
|
||||||
|
PayOrderNo: v.PayOrderNo,
|
||||||
|
Status: v.Status,
|
||||||
|
})
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,3 +137,551 @@ func (e *SmsRenewalLog) Remove(d *dto.SmsRenewalLogDeleteReq, p *actions.DataPer
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 手动扣费
|
||||||
|
func (e *SmsRenewalLog) ManualDeduct(req *dto.ManualDeductReq, userId int) (dto.ManualDeductResp, int) {
|
||||||
|
var entity models.SmsPhone
|
||||||
|
var result dto.ManualDeductResp
|
||||||
|
phoneService := SmsPhone{}
|
||||||
|
phoneService.Orm = e.Orm
|
||||||
|
phoneService.Log = e.Log
|
||||||
|
|
||||||
|
if err := e.Orm.Model(&entity).
|
||||||
|
Where("activation_id =?", req.ActivationId).
|
||||||
|
First(&entity).Error; err != nil {
|
||||||
|
e.Log.Errorf("获取短信号码失败, %s", err)
|
||||||
|
return result, statuscode.SmsNotExisted
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取服务配置映射
|
||||||
|
serviceMap, premiumMap, err := phoneService.getRenewalConfigs()
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("获取短信服务配置失败, %s", err)
|
||||||
|
return result, statuscode.ServerError
|
||||||
|
}
|
||||||
|
|
||||||
|
var count int64
|
||||||
|
|
||||||
|
if err := e.Orm.Model(&models.SmsRenewalLog{}).
|
||||||
|
Where("phone_id = ? AND status = 1", entity.Id).
|
||||||
|
Count(&count).Error; err != nil {
|
||||||
|
e.Log.Errorf("查询号码续费记录失败 [PhoneID: %d]: %v", entity.Id, err)
|
||||||
|
return result, statuscode.ServerError
|
||||||
|
}
|
||||||
|
|
||||||
|
if count > 0 {
|
||||||
|
return result, statuscode.SmsRenewalLogExisted
|
||||||
|
}
|
||||||
|
|
||||||
|
if renewLog, err := e.processPhoneRenewal(entity, serviceMap, premiumMap, req.TradeOrderNo, false); err != nil {
|
||||||
|
e.Log.Errorf("处理号码续费失败 [PhoneID: %d 续费订单号:%s]: %v", entity.Id, renewLog.PayOrderNo, err)
|
||||||
|
return result, statuscode.ServerError
|
||||||
|
} else {
|
||||||
|
result.ActivationId = req.ActivationId
|
||||||
|
result.TradeOrderNo = renewLog.TradeOrderNo
|
||||||
|
result.PayOrderNo = renewLog.PayOrderNo
|
||||||
|
result.BeginTime = req.BeginTime
|
||||||
|
result.EndTime = renewLog.TargetTime
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, statuscode.Success
|
||||||
|
}
|
||||||
|
|
||||||
|
// 续期详情
|
||||||
|
func (e *SmsRenewalLog) GetRenewalDetailByTradeOrderNo(req *dto.ManualDeductDetailReq, userId int) (dto.ManualDeductResp, int) {
|
||||||
|
var entity models.SmsRenewalLog
|
||||||
|
result := dto.ManualDeductResp{}
|
||||||
|
|
||||||
|
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).
|
||||||
|
Select("sms_renewal_log.*, s.activation_id").
|
||||||
|
First(&entity).Error; err != nil {
|
||||||
|
e.Log.Errorf("获取续费记录失败, %s", err)
|
||||||
|
return result, statuscode.SmsNotExisted
|
||||||
|
}
|
||||||
|
|
||||||
|
result.ActivationId = entity.ActivationId
|
||||||
|
result.TradeOrderNo = entity.TradeOrderNo
|
||||||
|
result.PayOrderNo = entity.PayOrderNo
|
||||||
|
result.BeginTime = entity.BeforeTime
|
||||||
|
result.EndTime = entity.TargetTime
|
||||||
|
|
||||||
|
return result, statuscode.Success
|
||||||
|
}
|
||||||
|
|
||||||
|
// AutoRenewal 自动续期处理
|
||||||
|
// 处理即将到期的长期号码自动续费逻辑
|
||||||
|
func (e *SmsRenewalLog) AutoRenewal() error {
|
||||||
|
if err := e.JudgeRenewalLogStatus(); err != nil {
|
||||||
|
e.Log.Errorf("处理预扣费记录失败 %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取24小时内到期的自动续费号码
|
||||||
|
phoneService := SmsPhone{}
|
||||||
|
phoneService.Orm = e.Orm
|
||||||
|
phoneService.Log = e.Log
|
||||||
|
expiredPhones, err := e.getExpiredPhones()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取服务配置映射
|
||||||
|
serviceMap, premiumMap, err := phoneService.getRenewalConfigs()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理每个到期号码
|
||||||
|
for _, phone := range expiredPhones {
|
||||||
|
if renewLog, err := e.processPhoneRenewal(phone, serviceMap, premiumMap, "", false); err != nil {
|
||||||
|
e.Log.Errorf("处理号码续费失败 [PhoneID: %d 续费订单号:%s]: %v", phone.Id, renewLog.PayOrderNo, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 手动续费(处理到期号码)
|
||||||
|
func (e *SmsRenewalLog) ManualRenewal(activationIds []string) ([]string, error) {
|
||||||
|
if len(activationIds) == 0 {
|
||||||
|
return nil, errors.New("activationIds 不能为空")
|
||||||
|
}
|
||||||
|
|
||||||
|
var errorIds []string
|
||||||
|
var expiredPhones []models.SmsPhone
|
||||||
|
phoneService := SmsPhone{}
|
||||||
|
phoneService.Orm = e.Orm
|
||||||
|
phoneService.Log = e.Log
|
||||||
|
// 查询所有到期号码
|
||||||
|
if err := e.Orm.Model(&models.SmsPhone{}).
|
||||||
|
Where("activation_id IN ? ", activationIds). //expire_time < ? time.Now()
|
||||||
|
Find(&expiredPhones).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取服务配置映射
|
||||||
|
serviceMap, premiumMap, err := phoneService.getRenewalConfigs()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理每个到期号码
|
||||||
|
for _, phone := range expiredPhones {
|
||||||
|
if renewLog, err := e.processPhoneRenewal(phone, serviceMap, premiumMap, "", true); err != nil {
|
||||||
|
e.Log.Errorf("处理号码续费失败 [PhoneID: %d 续费订单号:%s]: %v", phone.Id, renewLog.PayOrderNo, err)
|
||||||
|
errorIds = append(errorIds, fmt.Sprintf("处理号码续费失败 [PhoneID: %d]: %v", phone.Id, err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errorIds, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getExpiredPhones 获取即将到期的自动续费号码
|
||||||
|
func (e *SmsRenewalLog) getExpiredPhones() ([]models.SmsPhone, error) {
|
||||||
|
var phones []models.SmsPhone
|
||||||
|
startTime := time.Now().Add(-24 * time.Hour)
|
||||||
|
endTime := time.Now().Add(24 * time.Hour)
|
||||||
|
|
||||||
|
notExistsQuery := e.Orm.Model(&models.SmsRenewalLog{}).
|
||||||
|
Select("1").
|
||||||
|
Where("sms_renewal_log.phone_id = sms_phone.id AND sms_renewal_log.status in (1,2) AND status = 1")
|
||||||
|
|
||||||
|
err := e.Orm.Model(&models.SmsPhone{}).
|
||||||
|
Where("auto_renewal = 1 AND type = 1 AND actived = 2 AND expire_time >= ? AND expire_time < ?", startTime, endTime).
|
||||||
|
Where("NOT EXISTS (?)", notExistsQuery).
|
||||||
|
Find(&phones).Error
|
||||||
|
|
||||||
|
return phones, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取预扣除的记录
|
||||||
|
func (e *SmsRenewalLog) GetLockRenewalLogs() ([]models.SmsRenewalLog, error) {
|
||||||
|
var logs []models.SmsRenewalLog
|
||||||
|
err := e.Orm.Model(&models.SmsRenewalLog{}).
|
||||||
|
Joins("left join sms_phone s on s.id = sms_renewal_log.phone_id").
|
||||||
|
Where("sms_renewal_log.status = 1").
|
||||||
|
Select("sms_renewal_log.*, s.platform_code,s.api_key,s.billing_cycle_id").
|
||||||
|
Find(&logs).Error
|
||||||
|
return logs, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断续费是否成功
|
||||||
|
func (e *SmsRenewalLog) JudgeRenewalLogStatus() error {
|
||||||
|
logs, err := e.GetLockRenewalLogs()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
smsPlatformKeyRedis := NewSmsPlatformKeyRedis(e.Orm, e.Log)
|
||||||
|
textVerifiedService := SmsTextVerified{Service: e.Service}
|
||||||
|
|
||||||
|
for _, item := range logs {
|
||||||
|
switch item.PlatformCode {
|
||||||
|
case global.SmsPlatformTextVerified:
|
||||||
|
apiInfo, err := smsPlatformKeyRedis.GetApiInfo(item.PlatformCode, item.ApiKey)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("获取短信平台密钥失败 [PlatformCode: %s]: %v", item.PlatformCode, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cycleInfo, err := textVerifiedService.GetBillingCycle(item.BillingCycleId, &apiInfo)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("获取短信平台密钥失败 [PlatformCode: %s]: %v", item.PlatformCode, err)
|
||||||
|
|
||||||
|
if errors.Is(err, LogNotFund) {
|
||||||
|
if err := e.rollbackRenewalLog(item.Id); err != nil {
|
||||||
|
e.Log.Errorf("回滚续费记录失败 [RenewalLogID: %d]: %v", item.Id, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
startTime := item.BeforeTime.UTC().Truncate(24 * time.Hour)
|
||||||
|
endTime := cycleInfo.RenewedThrough.Truncate(24 * time.Hour)
|
||||||
|
// 续费成功
|
||||||
|
if startTime.Before(endTime) {
|
||||||
|
if err := e.confirmPayment(item.PhoneId, item.Id); err != nil {
|
||||||
|
e.Log.Errorf("更新续费记录状态失败 [RenewalLogID: %d]: %v", item.Id, err)
|
||||||
|
}
|
||||||
|
} else if item.BeforeTime.Before(time.Now()) {
|
||||||
|
if err := e.rollbackRenewalLog(item.Id); err != nil {
|
||||||
|
e.Log.Errorf("回滚续费记录失败 [RenewalLogID: %d]: %v", item.Id, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case global.SmsPlatformDaisysms:
|
||||||
|
//没有手动续费只能 在到期前直接扣费
|
||||||
|
timeDuration := time.Since(item.BeforeTime)
|
||||||
|
|
||||||
|
if timeDuration < 24*time.Hour {
|
||||||
|
if err := e.confirmPayment(item.PhoneId, item.Id); err != nil {
|
||||||
|
e.Log.Errorf("更新续费记录状态失败 [RenewalLogID: %d]: %v", item.Id, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
e.Log.Errorf("不支持的短信平台 %s 续费id:%d", item.PlatformCode, item.Id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getRenewalConfigs 获取续费相关配置
|
||||||
|
func (e *SmsPhone) getRenewalConfigs() (map[string]models.SmsServices, map[string]dto.GetSysConfigByKEYForServiceResp, error) {
|
||||||
|
smsServices := SmsServices{Service: e.Service}
|
||||||
|
serviceMap := smsServices.GetMapAll()
|
||||||
|
|
||||||
|
configService := SysConfig{Service: e.Service}
|
||||||
|
configKeys := []string{"renew_number_premium_daisysms", "renew_number_premium_textverified"}
|
||||||
|
premiumMap, err := configService.GetMapByKeys(configKeys)
|
||||||
|
|
||||||
|
return serviceMap, premiumMap, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// processPhoneRenewal 处理单个号码的续费
|
||||||
|
// tradeOrderNo 交易订单号
|
||||||
|
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
|
||||||
|
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:
|
||||||
|
//可以手动续费
|
||||||
|
textVerifiedService := SmsTextVerified{Service: e.Service}
|
||||||
|
detail, code := textVerifiedService.GetRentalDetail(phone.ActivationId, &apiInfo)
|
||||||
|
|
||||||
|
if code != statuscode.Success {
|
||||||
|
logger.Errorf("获取短信验证码续费详情失败 [ActivationID: %s]: %v", phone.ActivationId, code)
|
||||||
|
return renewLog, fmt.Errorf("获取短信验证码续费详情失败 [ActivationID: %s]: %v", phone.ActivationId, code)
|
||||||
|
}
|
||||||
|
|
||||||
|
phone.BillingCycleId = detail.BillingCycleId
|
||||||
|
case global.SmsPlatformDaisysms:
|
||||||
|
//只有自动续费的 用定时服务查询状态
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取服务价格
|
||||||
|
service, exists := serviceMap[phone.PlatformCode+"_"+phone.ServiceCode]
|
||||||
|
if !exists {
|
||||||
|
return renewLog, errors.New("服务不存在")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算续费价格
|
||||||
|
renewalPrice := e.calculateRenewalPrice(service.LongPrice, phone.PlatformCode, premiumMap)
|
||||||
|
if renewalPrice.IsZero() {
|
||||||
|
return renewLog, errors.New("续费价格计算失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建续费日志
|
||||||
|
renewLog = e.createRenewalLog(phone, renewalPrice, tradeOrderNo)
|
||||||
|
|
||||||
|
// 执行续费事务
|
||||||
|
if err := e.executeRenewalTransaction(phone, renewalPrice, &renewLog, &apiInfo, admin); err != nil {
|
||||||
|
return renewLog, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return renewLog, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculateRenewalPrice 计算续费价格
|
||||||
|
func (e *SmsRenewalLog) calculateRenewalPrice(basePrice decimal.Decimal, platformCode string, premiumMap map[string]dto.GetSysConfigByKEYForServiceResp) decimal.Decimal {
|
||||||
|
percent := decimal.NewFromInt(1)
|
||||||
|
|
||||||
|
var configKey string
|
||||||
|
switch platformCode {
|
||||||
|
case global.SmsPlatformDaisysms:
|
||||||
|
configKey = "renew_number_premium_daisysms"
|
||||||
|
case global.SmsPlatformTextVerified:
|
||||||
|
configKey = "renew_number_premium_textverified"
|
||||||
|
default:
|
||||||
|
return basePrice
|
||||||
|
}
|
||||||
|
|
||||||
|
if config, exists := premiumMap[configKey]; exists {
|
||||||
|
if val, err := decimal.NewFromString(config.ConfigValue); err == nil && val.Cmp(decimal.Zero) > 0 {
|
||||||
|
percent = decimal.NewFromInt(100).Add(val).Div(decimal.NewFromInt(100))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return basePrice.Mul(percent).Truncate(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// createRenewalLog 创建续费日志
|
||||||
|
// tradeOrderNo 交易订单号
|
||||||
|
func (e *SmsRenewalLog) createRenewalLog(phone models.SmsPhone, amount decimal.Decimal, tradeOrderNo string) models.SmsRenewalLog {
|
||||||
|
targetTime := phone.ExpireTime.AddDate(0, 0, 30)
|
||||||
|
|
||||||
|
return models.SmsRenewalLog{
|
||||||
|
UserId: phone.UserId,
|
||||||
|
TradeOrderNo: tradeOrderNo,
|
||||||
|
PayOrderNo: utility.GenerateTraceID(),
|
||||||
|
Phone: phone.Phone,
|
||||||
|
Category: 2,
|
||||||
|
PhoneId: phone.Id,
|
||||||
|
Type: phone.Type,
|
||||||
|
Amount: amount,
|
||||||
|
BeforeTime: *phone.ExpireTime,
|
||||||
|
TargetTime: &targetTime,
|
||||||
|
Period: 30,
|
||||||
|
Status: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// executeRenewalTransaction 执行续费事务
|
||||||
|
func (e *SmsRenewalLog) executeRenewalTransaction(phone models.SmsPhone, price decimal.Decimal,
|
||||||
|
renewLog *models.SmsRenewalLog, apiInfo *dto.SmsPlatformKeyQueueDto, isAdmin bool) error {
|
||||||
|
err := e.Orm.Transaction(func(tx *gorm.DB) error {
|
||||||
|
// 扣除余额
|
||||||
|
result := tx.Exec("UPDATE member_balance SET balance = balance - ? WHERE user_id = ? AND balance >= ?", price, phone.UserId, price)
|
||||||
|
if result.Error != nil {
|
||||||
|
return result.Error
|
||||||
|
}
|
||||||
|
if result.RowsAffected == 0 {
|
||||||
|
return errors.New("余额不足")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建续费日志
|
||||||
|
if err := tx.Create(renewLog).Error; err != nil {
|
||||||
|
return errors.New("创建续费日志失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil && err.Error() == "余额不足" && phone.ExpireTime.Before(time.Now()) {
|
||||||
|
return e.handleInsufficientBalance(phone)
|
||||||
|
}
|
||||||
|
|
||||||
|
//是否是管理员处理
|
||||||
|
if isAdmin {
|
||||||
|
if err1 := e.confirmPayment(phone.Id, renewLog.Id); err1 != nil {
|
||||||
|
logger.Errorf("确认扣款失败 [PhoneID: %d, RenewalLogID: %d]: %v", phone.Id, renewLog.Id, err1)
|
||||||
|
return err1
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch phone.PlatformCode {
|
||||||
|
case global.SmsPlatformTextVerified:
|
||||||
|
//可以手动续费
|
||||||
|
textVerifiedService := SmsTextVerified{Service: e.Service}
|
||||||
|
|
||||||
|
code := textVerifiedService.ManualRenewal(phone.BillingCycleId, apiInfo)
|
||||||
|
|
||||||
|
// 没有需要续费的号码 重新设置可自动续费之后再试一次
|
||||||
|
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 err1 := e.confirmPayment(phone.Id, renewLog.Id); err1 != nil {
|
||||||
|
logger.Errorf("确认扣款失败 [PhoneID: %d, RenewalLogID: %d]: %v", phone.Id, renewLog.Id, err1)
|
||||||
|
return err1
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err1 := e.rollbackRenewalLog(renewLog.Id); err1 != nil {
|
||||||
|
logger.Errorf("回滚续费日志状态失败,续费日志ID: %d, 错误: %v", renewLog.Id, err1)
|
||||||
|
return err1
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("手动续费失败 [BillingCycleID: %s]: %v", phone.BillingCycleId, code)
|
||||||
|
}
|
||||||
|
case global.SmsPlatformDaisysms:
|
||||||
|
//只有自动续费的 用定时服务查询状态
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确认扣款成功
|
||||||
|
func (e *SmsRenewalLog) confirmPayment(phoneId int, renewalLogId int) error {
|
||||||
|
var renewLog models.SmsRenewalLog
|
||||||
|
if err := e.Orm.Where("id = ?", renewalLogId).First(&renewLog).Error; err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
// 事务ID不存在:这是致命错误,平台A必须有此记录才能Confirm
|
||||||
|
e.Log.Errorf("Confirm失败: 找不到续费记录ID %s 的续费日志", renewalLogId)
|
||||||
|
return errors.New("事务记录不存在,无法确认支付")
|
||||||
|
}
|
||||||
|
return fmt.Errorf("查询续费日志失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查幂等性
|
||||||
|
if renewLog.Status == 2 { // 状态 2: 扣费成功
|
||||||
|
// 幂等性成功:已经被 Confirm 过了,直接返回成功,不需重复操作
|
||||||
|
e.Log.Warnf("Confirm重复调用: 续费ID %s 已是成功状态", renewalLogId)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 流程错误检查:不能对已取消或失败的事务执行 Confirm
|
||||||
|
if renewLog.Status != 1 { // 状态 1: 预扣中/冻结
|
||||||
|
// 理论上不该发生。说明平台 B 的调用时序错误。
|
||||||
|
e.Log.Errorf("Confirm失败: 续费ID %s 状态为 %d,无法执行确认支付", renewalLogId, renewLog.Status)
|
||||||
|
return errors.New("事务状态不正确,无法确认支付")
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.Orm.Transaction(func(tx *gorm.DB) error {
|
||||||
|
|
||||||
|
// 1. 查找并更新续费日志状态 (状态 2: 扣费成功)
|
||||||
|
result := tx.Model(&models.SmsRenewalLog{}).
|
||||||
|
Where("id = ? AND status = ?", renewalLogId, 1). // 必须是预扣中状态
|
||||||
|
Update("status", 2)
|
||||||
|
|
||||||
|
if result.RowsAffected == 0 {
|
||||||
|
return errors.New("续费日志状态不正确或已处理")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. **更新到期时间** (只有在 Confirm 阶段才能更新)
|
||||||
|
var phone models.SmsPhone
|
||||||
|
if err := tx.Model(&models.SmsPhone{}).Where("id = ?", phoneId).First(&phone).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var targetTime time.Time
|
||||||
|
if renewLog.TargetTime != nil {
|
||||||
|
targetTime = *renewLog.TargetTime
|
||||||
|
} else {
|
||||||
|
targetTime = phone.ExpireTime.AddDate(0, 0, 30)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Model(&phone).Update("expire_time", targetTime).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil // TCC Confirm 成功
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// rollbackRenewalLog 回滚续费日志状态
|
||||||
|
func (e *SmsRenewalLog) rollbackRenewalLog(renewLogId int) error {
|
||||||
|
var renewLog models.SmsRenewalLog
|
||||||
|
if err := e.Orm.Where("id = ?", renewLogId).First(&renewLog).Error; err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
e.Log.Errorf("回滚失败: 找不到续费记录ID %d", renewLogId)
|
||||||
|
return errors.New("事务记录不存在,无法回滚")
|
||||||
|
}
|
||||||
|
return fmt.Errorf("查询续费日志失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 幂等性检查:如果已经处于回滚状态,直接返回成功
|
||||||
|
if renewLog.Status == 3 {
|
||||||
|
e.Log.Warnf("回滚重复调用: 续费ID %d 已是回滚状态 (Status=3)", renewLogId)
|
||||||
|
return nil // 幂等性成功
|
||||||
|
}
|
||||||
|
|
||||||
|
// 流程检查:如果已经是成功扣费状态,不能执行普通回滚
|
||||||
|
if renewLog.Status == 2 {
|
||||||
|
e.Log.Errorf("回滚失败: 续费ID %d 已是成功扣费状态 (Status=2)", renewLogId)
|
||||||
|
return errors.New("事务已确认支付,无法执行取消回滚")
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.Orm.Transaction(func(tx *gorm.DB) error {
|
||||||
|
// 1. 更新续费日志状态 (状态 1: 预扣中/冻结)
|
||||||
|
result := tx.Model(&models.SmsRenewalLog{}).
|
||||||
|
Where("id = ? AND status = ?", renewLogId, 1). // 必须是扣费成功状态
|
||||||
|
Update("status", 3)
|
||||||
|
if result.Error != nil {
|
||||||
|
logger.Errorf("回滚续费日志状态失败,续费日志ID: %d, 错误: %v", renewLogId, result.Error)
|
||||||
|
return result.Error
|
||||||
|
}
|
||||||
|
if result.RowsAffected == 0 {
|
||||||
|
logger.Errorf("回滚续费日志状态失败,续费日志ID: %d", renewLogId)
|
||||||
|
return errors.New("续费日志状态不正确或已处理")
|
||||||
|
}
|
||||||
|
|
||||||
|
result = tx.Exec("UPDATE member_balance SET balance = balance + ? WHERE user_id = ? ", renewLog.Amount, renewLog.UserId)
|
||||||
|
if result.Error != nil {
|
||||||
|
logger.Errorf("回滚续费日志状态失败,续费日志ID: %d, 错误: %v", renewLogId, result.Error)
|
||||||
|
return result.Error
|
||||||
|
}
|
||||||
|
if result.RowsAffected == 0 {
|
||||||
|
return errors.New("余额不足")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleInsufficientBalance 处理余额不足情况
|
||||||
|
func (e *SmsRenewalLog) handleInsufficientBalance(phone models.SmsPhone) error {
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"auto_renewal": 2,
|
||||||
|
"remark": "余额不足,取消自动续费",
|
||||||
|
}
|
||||||
|
// 取消自动续费
|
||||||
|
if err := e.Orm.Model(&models.SmsPhone{}).
|
||||||
|
Where("id = ?", phone.Id).
|
||||||
|
Updates(params).Error; err != nil {
|
||||||
|
e.Log.Errorf("余额不足,取消自动续费失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
phoneService := SmsPhone{}
|
||||||
|
phoneService.Orm = e.Orm
|
||||||
|
phoneService.Log = e.Log
|
||||||
|
// 调用平台取消续费接口
|
||||||
|
code := phoneService.ChangeAutoRenewManage(phone.PlatformCode, phone.ApiKey, phone.ActivationId, false)
|
||||||
|
if code != statuscode.Success {
|
||||||
|
params["auto_renewal"] = 1
|
||||||
|
params["remark"] = ""
|
||||||
|
// 如果平台取消失败,恢复自动续费状态
|
||||||
|
if err := e.Orm.Model(&models.SmsPhone{}).
|
||||||
|
Where("id = ?", phone.Id).
|
||||||
|
Updates(params).Error; err != nil {
|
||||||
|
e.Log.Errorf("恢复自动续费状态失败: %v", err)
|
||||||
|
}
|
||||||
|
e.Log.Errorf("平台取消自动续费失败,状态码: %d", code)
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.New("余额不足")
|
||||||
|
}
|
||||||
|
|||||||
@ -115,7 +115,9 @@ func (e SmsServices) GetList(resp *[]dto.SmsServicesGetListResp) error {
|
|||||||
dictService := SysDictData{Service: e.Service}
|
dictService := SysDictData{Service: e.Service}
|
||||||
dictDatas, _ := dictService.GetMapByType("sms_platform")
|
dictDatas, _ := dictService.GetMapByType("sms_platform")
|
||||||
|
|
||||||
keys := []string{"number_premium_daisysms", "number_premium_textverified", "long_number_premium_daisysms", "long_number_premium_textverified"}
|
keys := []string{"number_premium_daisysms", "number_premium_textverified",
|
||||||
|
"long_number_premium_daisysms", "long_number_premium_textverified",
|
||||||
|
"renew_number_premium_daisysms", "renew_number_premium_textverified"}
|
||||||
configService := SysConfig{Service: e.Service}
|
configService := SysConfig{Service: e.Service}
|
||||||
mapConfigs, err := configService.GetWithKeyForMap(keys)
|
mapConfigs, err := configService.GetWithKeyForMap(keys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -131,6 +133,7 @@ func (e SmsServices) GetList(resp *[]dto.SmsServicesGetListResp) error {
|
|||||||
PlatformCode: item.PlatformCode,
|
PlatformCode: item.PlatformCode,
|
||||||
Price: item.Price,
|
Price: item.Price,
|
||||||
LongPrice: item.LongPrice,
|
LongPrice: item.LongPrice,
|
||||||
|
RenewLongPrice: item.LongPrice,
|
||||||
}
|
}
|
||||||
|
|
||||||
if dict, ok := dictDatas[respItem.PlatformCode]; ok {
|
if dict, ok := dictDatas[respItem.PlatformCode]; ok {
|
||||||
@ -157,6 +160,15 @@ func (e SmsServices) GetList(resp *[]dto.SmsServicesGetListResp) error {
|
|||||||
respItem.LongPrice = respItem.LongPrice.Mul(decimal.NewFromInt(100).Add(premium).Div(decimal.NewFromInt(100))).Truncate(2)
|
respItem.LongPrice = respItem.LongPrice.Mul(decimal.NewFromInt(100).Add(premium).Div(decimal.NewFromInt(100))).Truncate(2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config, ok := mapConfigs["renew_number_premium_daisysms"]; ok {
|
||||||
|
premium, err := decimal.NewFromString(config.ConfigValue)
|
||||||
|
if err != nil || premium.IsZero() {
|
||||||
|
e.Log.Errorf("浮动百分比为0,或者是转换错误 %s", config.ConfigValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
respItem.RenewLongPrice = respItem.RenewLongPrice.Mul(decimal.NewFromInt(100).Add(premium).Div(decimal.NewFromInt(100))).Truncate(2)
|
||||||
|
}
|
||||||
case global.SmsPlatformTextVerified:
|
case global.SmsPlatformTextVerified:
|
||||||
if config, ok := mapConfigs["number_premium_textverified"]; ok {
|
if config, ok := mapConfigs["number_premium_textverified"]; ok {
|
||||||
premium, err := decimal.NewFromString(config.ConfigValue)
|
premium, err := decimal.NewFromString(config.ConfigValue)
|
||||||
@ -176,6 +188,17 @@ func (e SmsServices) GetList(resp *[]dto.SmsServicesGetListResp) error {
|
|||||||
respItem.LongPrice = respItem.LongPrice.Mul(decimal.NewFromInt(100).Add(premium).Div(decimal.NewFromInt(100))).Truncate(2)
|
respItem.LongPrice = respItem.LongPrice.Mul(decimal.NewFromInt(100).Add(premium).Div(decimal.NewFromInt(100))).Truncate(2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//续期价格浮动
|
||||||
|
if config, ok := mapConfigs["renew_number_premium_textverified"]; ok {
|
||||||
|
premium, err := decimal.NewFromString(config.ConfigValue)
|
||||||
|
|
||||||
|
if err != nil || premium.IsZero() {
|
||||||
|
e.Log.Errorf("浮动百分比为0,或者是转换错误 %s", config.ConfigValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
respItem.RenewLongPrice = respItem.RenewLongPrice.Mul(decimal.NewFromInt(100).Add(premium).Div(decimal.NewFromInt(100))).Truncate(2)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*resp = append(*resp, respItem)
|
*resp = append(*resp, respItem)
|
||||||
|
|||||||
@ -29,58 +29,10 @@ type SmsTextVerified struct {
|
|||||||
service.Service
|
service.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取收到的验证码
|
// 错误
|
||||||
// messageId 短效或长效号码的ID
|
var (
|
||||||
// typ 0-短效 1-长效
|
LogNotFund = errors.New("记录不存在")
|
||||||
func (e SmsTextVerified) GetCode(messageId string, typ int, unixTime int64) (string, int) {
|
)
|
||||||
reservationType := ""
|
|
||||||
|
|
||||||
if typ == 0 {
|
|
||||||
reservationType = "verification"
|
|
||||||
} else {
|
|
||||||
reservationType = "renewable"
|
|
||||||
}
|
|
||||||
url := fmt.Sprintf(getSmsCode, messageId, reservationType)
|
|
||||||
client, code := e.GetTextVerifiedAuthClient()
|
|
||||||
|
|
||||||
if code != http.StatusOK {
|
|
||||||
e.Log.Errorf("获取授权请求失败,status %d", code)
|
|
||||||
return "", code
|
|
||||||
}
|
|
||||||
|
|
||||||
var parsedCode string
|
|
||||||
resp := dto.TextVerifiedSmsResp{}
|
|
||||||
|
|
||||||
_, err := client.Get(url, nil, &resp)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
e.Log.Errorf("获取验证码失败, error: %v", err)
|
|
||||||
return "", statuscode.ServerError
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(resp.Data) > 0 {
|
|
||||||
for _, v := range resp.Data {
|
|
||||||
// 2. 解析时间字符串
|
|
||||||
// time.RFC3339Nano 是一个预定义的格式常量,
|
|
||||||
// 它能够解析包含纳秒和时区信息的 ISO 8601 字符串。
|
|
||||||
t, err := time.Parse(time.RFC3339Nano, v.CreatedAt)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("时间解析失败:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. 将解析后的 time.Time 对象转换为 Unix 时间戳
|
|
||||||
// t.Unix() 返回以秒为单位的整数时间戳
|
|
||||||
unixTimestamp := t.Unix()
|
|
||||||
|
|
||||||
if unixTimestamp >= unixTime {
|
|
||||||
parsedCode = v.ParsedCode
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return parsedCode, http.StatusOK
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
loginUrl = "/api/pub/v2/auth"
|
loginUrl = "/api/pub/v2/auth"
|
||||||
@ -97,8 +49,126 @@ const (
|
|||||||
renewRental = "/api/pub/v2/reservations/rental/renewable/%s/renew" //长效续期
|
renewRental = "/api/pub/v2/reservations/rental/renewable/%s/renew" //长效续期
|
||||||
updateRentalRenewStatus = "/api/pub/v2/reservations/rental/renewable/%s" // 更改续租状态
|
updateRentalRenewStatus = "/api/pub/v2/reservations/rental/renewable/%s" // 更改续租状态
|
||||||
getSmsCode = "/api/pub/v2/sms?reservationId=%s&reservationType=%s" //获取短信验证码
|
getSmsCode = "/api/pub/v2/sms?reservationId=%s&reservationType=%s" //获取短信验证码
|
||||||
|
getNumbers = "/api/pub/v2/reservations/rental/renewable" //获取长效号码列表 Get
|
||||||
|
manualRenewal = "/api/pub/v2/billing-cycles/%s/renew" //手动续期 POST
|
||||||
|
getBillingCycles = "/api/pub/v2/billing-cycles/%s" //获取计费周期详情 GET
|
||||||
|
rentalList = "/api/pub/v2/reservations/rental/renewable" //长效号码列表
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 获取收到的验证码
|
||||||
|
// messageId 短效或长效号码的ID
|
||||||
|
// typ 0-短效 1-长效
|
||||||
|
func (e SmsTextVerified) GetCode(messageId string, typ int, userId int, service, serviceCode string, apiInfo *dto.SmsPlatformKeyQueueDto) (string, int) {
|
||||||
|
reservationType := ""
|
||||||
|
|
||||||
|
if typ == 0 {
|
||||||
|
reservationType = "verification"
|
||||||
|
} else {
|
||||||
|
reservationType = "renewable"
|
||||||
|
}
|
||||||
|
url := fmt.Sprintf(getSmsCode, messageId, reservationType)
|
||||||
|
client, code := e.GetTextVerifiedAuthClient(apiInfo)
|
||||||
|
|
||||||
|
if code != http.StatusOK {
|
||||||
|
e.Log.Errorf("获取授权请求失败,status %d", code)
|
||||||
|
return "", code
|
||||||
|
}
|
||||||
|
|
||||||
|
var parsedCode string
|
||||||
|
resp := dto.TextVerifiedSmsResp{}
|
||||||
|
|
||||||
|
_, err := client.Get(url, nil, &resp)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("获取验证码失败, error: %v", err)
|
||||||
|
return "", statuscode.ServerError
|
||||||
|
}
|
||||||
|
|
||||||
|
messageIds := []string{}
|
||||||
|
|
||||||
|
if len(resp.Data) > 0 {
|
||||||
|
for _, v := range resp.Data {
|
||||||
|
messageIds = append(messageIds, v.Id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(messageIds) > 0 {
|
||||||
|
receiveLogs := make([]models.SmsReceiveLog, 0)
|
||||||
|
receiveMaps := make(map[string]string, 0)
|
||||||
|
|
||||||
|
if err := e.Orm.Model(&models.SmsReceiveLog{}).
|
||||||
|
Where("message_id in ?", messageIds).
|
||||||
|
Select("message_id").
|
||||||
|
Find(&receiveLogs).Error; err != nil {
|
||||||
|
e.Log.Errorf("获取短信接收记录失败, error: %v", err)
|
||||||
|
return "", statuscode.ServerError
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range receiveLogs {
|
||||||
|
receiveMaps[v.MessageId] = v.MessageId
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找最新验证码并存储所有新验证码
|
||||||
|
var latestSms *dto.TextVerifiedSmsItemResp
|
||||||
|
var latestTime time.Time
|
||||||
|
var newSmsData []dto.TextVerifiedSmsItemResp
|
||||||
|
|
||||||
|
// 遍历所有验证码数据
|
||||||
|
for _, v := range resp.Data {
|
||||||
|
// 解析时间字符串
|
||||||
|
t, err := time.Parse(time.RFC3339Nano, v.CreatedAt)
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("时间解析失败: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否为新验证码(数据库中不存在)
|
||||||
|
if _, ok := receiveMaps[v.Id]; !ok {
|
||||||
|
newSmsData = append(newSmsData, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择最新的验证码(包括已存在的)
|
||||||
|
if latestSms == nil || t.After(latestTime) {
|
||||||
|
latestSms = &v
|
||||||
|
latestTime = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量保存所有新验证码到数据库
|
||||||
|
if len(newSmsData) > 0 {
|
||||||
|
var receiveLogs []models.SmsReceiveLog
|
||||||
|
for _, sms := range newSmsData {
|
||||||
|
receiveLog := models.SmsReceiveLog{
|
||||||
|
UserId: userId,
|
||||||
|
Service: service,
|
||||||
|
ServiceCode: serviceCode,
|
||||||
|
MessageId: sms.Id,
|
||||||
|
Phone: "1" + sms.To,
|
||||||
|
Code: sms.ParsedCode,
|
||||||
|
Status: 2,
|
||||||
|
}
|
||||||
|
receiveLogs = append(receiveLogs, receiveLog)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量插入数据库
|
||||||
|
if err := e.Orm.Create(&receiveLogs).Error; err != nil {
|
||||||
|
e.Log.Errorf("批量创建短信接收记录失败, error: %v", err)
|
||||||
|
return "", statuscode.ServerError
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Log.Infof("成功存储 %d 条新验证码记录", len(newSmsData))
|
||||||
|
|
||||||
|
// 返回最新的验证码
|
||||||
|
if latestSms != nil {
|
||||||
|
parsedCode = latestSms.ParsedCode
|
||||||
|
e.Log.Infof("返回最新验证码: %s, messageId: %s, 时间: %s", parsedCode, latestSms.Id, latestSms.CreatedAt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsedCode, http.StatusOK
|
||||||
|
}
|
||||||
|
|
||||||
var idPattern = regexp.MustCompile(`^[a-zA-Z0-9_]{28}$`)
|
var idPattern = regexp.MustCompile(`^[a-zA-Z0-9_]{28}$`)
|
||||||
|
|
||||||
// 获取授权失败
|
// 获取授权失败
|
||||||
@ -106,13 +176,13 @@ var ErrUnAuth = errors.New("未授权,请检查配置的账号和密码")
|
|||||||
var ErrOutOfStockOrUnavailable = errors.New("缺货或服务不可用")
|
var ErrOutOfStockOrUnavailable = errors.New("缺货或服务不可用")
|
||||||
|
|
||||||
// Login 登录
|
// Login 登录
|
||||||
func (e *SmsTextVerified) Login() (string, error) {
|
func (e *SmsTextVerified) Login(apiInfo *dto.SmsPlatformKeyQueueDto) (string, error) {
|
||||||
resp := dto.TextVerifiedLoginResp{}
|
resp := dto.TextVerifiedLoginResp{}
|
||||||
var token string
|
var token string
|
||||||
client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.SmsTextVerified.Url, nil)
|
client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.SmsTextVerified.Url, nil)
|
||||||
headers := map[string]string{
|
headers := map[string]string{
|
||||||
"X-API-USERNAME": config.ExtConfig.SmsTextVerified.UserName,
|
"X-API-USERNAME": apiInfo.Account,
|
||||||
"X-API-KEY": config.ExtConfig.SmsTextVerified.ApiKey,
|
"X-API-KEY": apiInfo.ApiKey,
|
||||||
}
|
}
|
||||||
_, err1 := client.Post(loginUrl, nil, headers, &resp)
|
_, err1 := client.Post(loginUrl, nil, headers, &resp)
|
||||||
|
|
||||||
@ -130,7 +200,8 @@ func (e *SmsTextVerified) Login() (string, error) {
|
|||||||
|
|
||||||
if resp.ExpiresIn >= 10 {
|
if resp.ExpiresIn >= 10 {
|
||||||
resp.ExpiresIn = resp.ExpiresIn - 10 // 提前10秒过期
|
resp.ExpiresIn = resp.ExpiresIn - 10 // 提前10秒过期
|
||||||
if err := redishelper.DefaultRedis.SetStringExpire(global.TextVerifiedToken, token, time.Duration(resp.ExpiresIn)*time.Second); err != nil {
|
key := fmt.Sprintf(global.TextVerifiedToken, apiInfo.ApiKey)
|
||||||
|
if err := redishelper.DefaultRedis.SetStringExpire(key, token, time.Duration(resp.ExpiresIn)*time.Second); err != nil {
|
||||||
e.Log.Errorf("TextVerified登录失败,缓存Token失败 error: %v", err)
|
e.Log.Errorf("TextVerified登录失败,缓存Token失败 error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -139,15 +210,16 @@ func (e *SmsTextVerified) Login() (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取token
|
// 获取token
|
||||||
func (e *SmsTextVerified) GetToken() (string, error) {
|
func (e *SmsTextVerified) GetToken(apiInfo *dto.SmsPlatformKeyQueueDto) (string, error) {
|
||||||
token, err := redishelper.DefaultRedis.GetString(global.TextVerifiedToken)
|
key := fmt.Sprintf(global.TextVerifiedToken, apiInfo.ApiKey)
|
||||||
|
token, err := redishelper.DefaultRedis.GetString(key)
|
||||||
if err != nil && errors.Is(err, redis.Nil) {
|
if err != nil && errors.Is(err, redis.Nil) {
|
||||||
// token不存在,重新登录获取
|
// token不存在,重新登录获取
|
||||||
return e.Login()
|
return e.Login(apiInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
if token == "" {
|
if token == "" {
|
||||||
return e.Login()
|
return e.Login(apiInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
return token, nil
|
return token, nil
|
||||||
@ -163,9 +235,9 @@ func (e *SmsTextVerified) GetAreas() ([]string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取服务列表
|
// 获取服务列表
|
||||||
func (e *SmsTextVerified) GetServices() ([]dto.TextVerifiedServeResp, error) {
|
func (e *SmsTextVerified) GetServices(apiInfo *dto.SmsPlatformKeyQueueDto) ([]dto.TextVerifiedServeResp, error) {
|
||||||
client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.SmsTextVerified.Url, nil)
|
client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.SmsTextVerified.Url, nil)
|
||||||
headers, err := e.GetAuthHeader()
|
headers, err := e.GetAuthHeader(apiInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -182,7 +254,15 @@ func (e *SmsTextVerified) GetServices() ([]dto.TextVerifiedServeResp, error) {
|
|||||||
|
|
||||||
// 同步服务列表
|
// 同步服务列表
|
||||||
func (e *SmsTextVerified) SyncServices() error {
|
func (e *SmsTextVerified) SyncServices() error {
|
||||||
services, err := e.GetServices()
|
// 获取API信息
|
||||||
|
smsPlatformKeyRedis := NewSmsPlatformKeyRedis(e.Orm, e.Log)
|
||||||
|
apiInfo, err := smsPlatformKeyRedis.GetRoundRobinKey(global.SmsPlatformTextVerified)
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("获取API信息失败: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
services, err := e.GetServices(apiInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.Log.Errorf("获取服务列表失败: %v", err)
|
e.Log.Errorf("获取服务列表失败: %v", err)
|
||||||
return err
|
return err
|
||||||
@ -259,17 +339,25 @@ func (e *SmsTextVerified) SyncPrices() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
smsPlatformKeyRedis := NewSmsPlatformKeyRedis(e.Orm, e.Log)
|
||||||
|
apiInfo, err := smsPlatformKeyRedis.GetRoundRobinKey(global.SmsPlatformTextVerified)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("获取API信息失败: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
for _, v := range services {
|
for _, v := range services {
|
||||||
params := map[string]interface{}{}
|
params := map[string]interface{}{}
|
||||||
|
|
||||||
price, code := e.GetPrice(0, v.Code)
|
price, code := e.GetPrice(0, v.Code, apiInfo)
|
||||||
|
|
||||||
if code == http.StatusOK {
|
if code == http.StatusOK {
|
||||||
params["price"] = price
|
params["price"] = price
|
||||||
}
|
}
|
||||||
|
|
||||||
time.Sleep(time.Microsecond * 500) // 避免请求过快被限制
|
time.Sleep(time.Microsecond * 500) // 避免请求过快被限制
|
||||||
longPrice, code := e.GetPrice(1, v.Code)
|
longPrice, code := e.GetPrice(1, v.Code, apiInfo)
|
||||||
if code == http.StatusOK {
|
if code == http.StatusOK {
|
||||||
params["long_price"] = longPrice
|
params["long_price"] = longPrice
|
||||||
}
|
}
|
||||||
@ -284,12 +372,36 @@ func (e *SmsTextVerified) SyncPrices() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取号码并唤醒
|
||||||
|
func (e *SmsTextVerified) GetNumberAndWakeUp(tye int, serviceCode string, price decimal.Decimal, period int, apiInfo *dto.SmsPlatformKeyQueueDto) (dto.SmsPhoneGetPhoneResp, int) {
|
||||||
|
resp, code := e.GetNumberForApi(tye, serviceCode, price, period, apiInfo)
|
||||||
|
|
||||||
|
if code != statuscode.Success {
|
||||||
|
return resp, code
|
||||||
|
}
|
||||||
|
|
||||||
|
wakeUpResp, code := e.getExtraActivation(resp.Id, apiInfo, 0)
|
||||||
|
|
||||||
|
if code != statuscode.Success {
|
||||||
|
//唤醒失败不退出
|
||||||
|
now := time.Now()
|
||||||
|
resp.StartTime = &now
|
||||||
|
resp.EndTime = &now
|
||||||
|
} else {
|
||||||
|
resp.MessageId = wakeUpResp.Id
|
||||||
|
resp.StartTime = wakeUpResp.UsageWindowStart
|
||||||
|
resp.EndTime = wakeUpResp.UsageWindowEnd
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, code
|
||||||
|
}
|
||||||
|
|
||||||
// 号码租赁
|
// 号码租赁
|
||||||
// getType 0-短效 1-长效
|
// getType 0-短效 1-长效
|
||||||
// service 服务code
|
// service 服务code
|
||||||
// maxPrice 最大价格
|
// maxPrice 最大价格
|
||||||
// period 时长(月=30天)
|
// period 时长(月=30天)
|
||||||
func (e SmsTextVerified) GetNumberForApi(typ int, serviceCode string, price decimal.Decimal, period int) (dto.SmsPhoneGetPhoneResp, int) {
|
func (e *SmsTextVerified) GetNumberForApi(typ int, serviceCode string, price decimal.Decimal, period int, apiInfo *dto.SmsPlatformKeyQueueDto) (dto.SmsPhoneGetPhoneResp, int) {
|
||||||
//这个平台的所有号码都是美国号码 默认国家代码都是 +1
|
//这个平台的所有号码都是美国号码 默认国家代码都是 +1
|
||||||
var err error
|
var err error
|
||||||
var createResp dto.TextVerifiedResp
|
var createResp dto.TextVerifiedResp
|
||||||
@ -297,10 +409,10 @@ func (e SmsTextVerified) GetNumberForApi(typ int, serviceCode string, price deci
|
|||||||
switch typ {
|
switch typ {
|
||||||
case 0:
|
case 0:
|
||||||
var code int
|
var code int
|
||||||
createResp, code = e.CreateVerification(serviceCode)
|
createResp, code = e.CreateVerification(serviceCode, apiInfo)
|
||||||
|
|
||||||
if code == statuscode.Success && createResp.Href != "" {
|
if code == statuscode.Success && createResp.Href != "" {
|
||||||
bytes, err := e.doRequest(&createResp)
|
bytes, err := e.doRequest(&createResp, apiInfo)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.Log.Errorf("短效号码 获取号码失败:%v", e)
|
e.Log.Errorf("短效号码 获取号码失败:%v", e)
|
||||||
@ -327,11 +439,11 @@ func (e SmsTextVerified) GetNumberForApi(typ int, serviceCode string, price deci
|
|||||||
return result, statuscode.ServerError
|
return result, statuscode.ServerError
|
||||||
}
|
}
|
||||||
case 1:
|
case 1:
|
||||||
createResp, err = e.CreateRental(serviceCode)
|
createResp, err = e.CreateRental(serviceCode, apiInfo)
|
||||||
|
|
||||||
if err == nil && createResp.Href != "" {
|
if err == nil && createResp.Href != "" {
|
||||||
saleId := getIdByUrl(createResp.Href)
|
saleId := getIdByUrl(createResp.Href)
|
||||||
bytes, err := e.doRequest(&createResp)
|
bytes, err := e.doRequest(&createResp, apiInfo)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.Log.Errorf("通过销售id [%s] 获取号码失败:%v", saleId, err)
|
e.Log.Errorf("通过销售id [%s] 获取号码失败:%v", saleId, err)
|
||||||
@ -358,13 +470,14 @@ func (e SmsTextVerified) GetNumberForApi(typ int, serviceCode string, price deci
|
|||||||
return result, statuscode.ServerError
|
return result, statuscode.ServerError
|
||||||
}
|
}
|
||||||
|
|
||||||
detail, code := e.GetRentalDetail(result.Id)
|
detail, code := e.GetRentalDetail(result.Id, apiInfo)
|
||||||
|
|
||||||
if code != statuscode.Success {
|
if code != statuscode.Success {
|
||||||
return result, code
|
return result, code
|
||||||
}
|
}
|
||||||
|
|
||||||
result.Phone = "1" + detail.Number
|
result.Phone = "1" + detail.Number
|
||||||
|
result.BillingCycleId = detail.BillingCycleId
|
||||||
endTime := resp.UpdatedAt.AddDate(0, 0, 30)
|
endTime := resp.UpdatedAt.AddDate(0, 0, 30)
|
||||||
result.EndAt = &(endTime) // 30天后过期
|
result.EndAt = &(endTime) // 30天后过期
|
||||||
|
|
||||||
@ -394,13 +507,13 @@ func (e SmsTextVerified) GetNumberForApi(typ int, serviceCode string, price deci
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 执行查询请求
|
// 执行查询请求
|
||||||
func (e *SmsTextVerified) doRequest(resp *dto.TextVerifiedResp) ([]byte, error) {
|
func (e *SmsTextVerified) doRequest(resp *dto.TextVerifiedResp, apiInfo *dto.SmsPlatformKeyQueueDto) ([]byte, error) {
|
||||||
bytes := []byte{}
|
bytes := []byte{}
|
||||||
if resp.Href == "" {
|
if resp.Href == "" {
|
||||||
return bytes, errors.New("成功请求没有返回url")
|
return bytes, errors.New("成功请求没有返回url")
|
||||||
}
|
}
|
||||||
|
|
||||||
header, err := e.GetAuthHeader()
|
header, err := e.GetAuthHeader(apiInfo)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return bytes, fmt.Errorf("获取授权失败:%v", err)
|
return bytes, fmt.Errorf("获取授权失败:%v", err)
|
||||||
@ -418,7 +531,7 @@ func (e *SmsTextVerified) doRequest(resp *dto.TextVerifiedResp) ([]byte, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 长号码租赁
|
// 长号码租赁
|
||||||
func (e *SmsTextVerified) CreateRental(serviceCode string) (dto.TextVerifiedResp, error) {
|
func (e *SmsTextVerified) CreateRental(serviceCode string, apiInfo *dto.SmsPlatformKeyQueueDto) (dto.TextVerifiedResp, error) {
|
||||||
req := dto.TextVerifiedCreateRewalReq{
|
req := dto.TextVerifiedCreateRewalReq{
|
||||||
ServiceName: serviceCode,
|
ServiceName: serviceCode,
|
||||||
Capability: "sms",
|
Capability: "sms",
|
||||||
@ -430,7 +543,7 @@ func (e *SmsTextVerified) CreateRental(serviceCode string) (dto.TextVerifiedResp
|
|||||||
|
|
||||||
resp := dto.TextVerifiedResp{}
|
resp := dto.TextVerifiedResp{}
|
||||||
|
|
||||||
client, code := e.GetTextVerifiedAuthClient()
|
client, code := e.GetTextVerifiedAuthClient(apiInfo)
|
||||||
|
|
||||||
if code != statuscode.Success {
|
if code != statuscode.Success {
|
||||||
e.Log.Errorf("获取头信息失败 error: %d", code)
|
e.Log.Errorf("获取头信息失败 error: %d", code)
|
||||||
@ -458,10 +571,10 @@ func (e *SmsTextVerified) CreateRental(serviceCode string) (dto.TextVerifiedResp
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取长效号码详情
|
// 获取长效号码详情
|
||||||
func (e *SmsTextVerified) GetRentalDetail(id string) (dto.VerificationRentalDetailResp, int) {
|
func (e *SmsTextVerified) GetRentalDetail(id string, apiInfo *dto.SmsPlatformKeyQueueDto) (dto.VerificationRentalDetailResp, int) {
|
||||||
result := dto.VerificationRentalDetailResp{}
|
result := dto.VerificationRentalDetailResp{}
|
||||||
|
|
||||||
client, code := e.GetTextVerifiedAuthClient()
|
client, code := e.GetTextVerifiedAuthClient(apiInfo)
|
||||||
if code != statuscode.Success {
|
if code != statuscode.Success {
|
||||||
return result, code
|
return result, code
|
||||||
}
|
}
|
||||||
@ -477,12 +590,13 @@ func (e *SmsTextVerified) GetRentalDetail(id string) (dto.VerificationRentalDeta
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 唤醒号码
|
// 唤醒号码
|
||||||
// returns activationId,messageId, code
|
// returns activationId,messageId,startTime(可用开始时间),endTime(可用结束时间), code,
|
||||||
func (e SmsTextVerified) getExtraActivation(id string) (string, string, int) {
|
func (e SmsTextVerified) getExtraActivation(id string, apiInfo *dto.SmsPlatformKeyQueueDto, retry int) (dto.TextVerifiedWakeUpResp, int) {
|
||||||
client, code := e.GetTextVerifiedAuthClient()
|
client, code := e.GetTextVerifiedAuthClient(apiInfo)
|
||||||
|
result := dto.TextVerifiedWakeUpResp{}
|
||||||
|
|
||||||
if code != statuscode.Success {
|
if code != statuscode.Success {
|
||||||
return "", "", code
|
return result, code
|
||||||
}
|
}
|
||||||
|
|
||||||
req := dto.TextVerifiedWakeUpReq{
|
req := dto.TextVerifiedWakeUpReq{
|
||||||
@ -496,50 +610,59 @@ func (e SmsTextVerified) getExtraActivation(id string) (string, string, int) {
|
|||||||
status, err := client.Post(wakeUp, req, headers, &resp)
|
status, err := client.Post(wakeUp, req, headers, &resp)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
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)
|
e.Log.Errorf("唤醒号码失败 id:%s error: %v", id, err)
|
||||||
return "", "", statuscode.ServerError
|
return result, statuscode.ServerError
|
||||||
|
}
|
||||||
} else if status == http.StatusCreated || status == http.StatusOK {
|
} else if status == http.StatusCreated || status == http.StatusOK {
|
||||||
if resp.Method != "" && resp.Href != "" {
|
if resp.Method != "" && resp.Href != "" {
|
||||||
bytes, err := e.doRequest(&resp)
|
bytes, err := e.doRequest(&resp, apiInfo)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.Log.Errorf("唤醒号码失败 id:%s error: %v", id, err)
|
e.Log.Errorf("唤醒号码失败 id:%s error: %v", id, err)
|
||||||
return "", "", statuscode.ServerError
|
return result, statuscode.ServerError
|
||||||
}
|
}
|
||||||
|
|
||||||
detailResp := dto.TextVerifiedWakeUpResp{}
|
detailResp := dto.TextVerifiedWakeUpResp{}
|
||||||
|
|
||||||
if err := sonic.Unmarshal(bytes, &detailResp); err != nil {
|
if err := sonic.Unmarshal(bytes, &detailResp); err != nil {
|
||||||
e.Log.Errorf("唤醒号码反序列化失败 id:%s error: %v", id, err)
|
e.Log.Errorf("唤醒号码反序列化失败 id:%s error: %v", id, err)
|
||||||
return "", "", statuscode.ServerError
|
return result, statuscode.ServerError
|
||||||
}
|
}
|
||||||
|
|
||||||
return detailResp.ReservationId, detailResp.Id, statuscode.Success
|
result.Id = detailResp.Id
|
||||||
|
result.ReservationId = detailResp.ReservationId
|
||||||
|
result.UsageWindowEnd = detailResp.UsageWindowEnd
|
||||||
|
result.UsageWindowStart = detailResp.UsageWindowStart
|
||||||
|
return result, statuscode.Success
|
||||||
} else {
|
} else {
|
||||||
e.Log.Errorf("唤醒号码失败 id:%s error: %v", id, err)
|
e.Log.Errorf("唤醒号码失败 id:%s error: %v", id, err)
|
||||||
return "", "", statuscode.ServerError
|
return result, statuscode.ServerError
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", "", statuscode.ServerError
|
return result, statuscode.ServerError
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取价格
|
// 获取价格
|
||||||
// getType 0-短效 1-长效
|
// getType 0-短效 1-长效
|
||||||
// returns decimal.Decimal(单价), int(状态code)
|
// returns decimal.Decimal(单价), int(状态code)
|
||||||
func (e *SmsTextVerified) GetPrice(typ int, serviceName string) (decimal.Decimal, int) {
|
func (e *SmsTextVerified) GetPrice(typ int, serviceName string, apiInfo *dto.SmsPlatformKeyQueueDto) (decimal.Decimal, int) {
|
||||||
switch typ {
|
switch typ {
|
||||||
case 1:
|
case 1:
|
||||||
return e.GetRentalPrice(serviceName)
|
return e.GetRentalPrice(serviceName, apiInfo)
|
||||||
case 0:
|
case 0:
|
||||||
return e.GetVerificationPrice(serviceName)
|
return e.GetVerificationPrice(serviceName, apiInfo)
|
||||||
default:
|
default:
|
||||||
return decimal.Zero, statuscode.SmsInvalidType
|
return decimal.Zero, statuscode.SmsInvalidType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取长租价格
|
// 获取长租价格
|
||||||
func (e *SmsTextVerified) GetRentalPrice(serviceName string) (decimal.Decimal, int) {
|
func (e *SmsTextVerified) GetRentalPrice(serviceName string, apiInfo *dto.SmsPlatformKeyQueueDto) (decimal.Decimal, int) {
|
||||||
req := dto.TextVerifiedPriceReq{
|
req := dto.TextVerifiedPriceReq{
|
||||||
ServiceName: serviceName,
|
ServiceName: serviceName,
|
||||||
Capability: "sms",
|
Capability: "sms",
|
||||||
@ -548,7 +671,7 @@ func (e *SmsTextVerified) GetRentalPrice(serviceName string) (decimal.Decimal, i
|
|||||||
Duration: "thirtyDay",
|
Duration: "thirtyDay",
|
||||||
}
|
}
|
||||||
|
|
||||||
client, code := e.GetTextVerifiedAuthClient()
|
client, code := e.GetTextVerifiedAuthClient(apiInfo)
|
||||||
|
|
||||||
if code != statuscode.Success {
|
if code != statuscode.Success {
|
||||||
e.Log.Errorf("获取授权请求失败,status %d", code)
|
e.Log.Errorf("获取授权请求失败,status %d", code)
|
||||||
@ -571,7 +694,7 @@ func (e *SmsTextVerified) GetRentalPrice(serviceName string) (decimal.Decimal, i
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取单次验证码价格
|
// 获取单次验证码价格
|
||||||
func (e *SmsTextVerified) GetVerificationPrice(sericeName string) (decimal.Decimal, int) {
|
func (e *SmsTextVerified) GetVerificationPrice(sericeName string, apiInfo *dto.SmsPlatformKeyQueueDto) (decimal.Decimal, int) {
|
||||||
params := map[string]interface{}{
|
params := map[string]interface{}{
|
||||||
"serviceName": sericeName,
|
"serviceName": sericeName,
|
||||||
"areaCode": false,
|
"areaCode": false,
|
||||||
@ -580,7 +703,7 @@ func (e *SmsTextVerified) GetVerificationPrice(sericeName string) (decimal.Decim
|
|||||||
"numberType": "mobile",
|
"numberType": "mobile",
|
||||||
}
|
}
|
||||||
|
|
||||||
client, code := e.GetTextVerifiedAuthClient()
|
client, code := e.GetTextVerifiedAuthClient(apiInfo)
|
||||||
|
|
||||||
if code != statuscode.Success {
|
if code != statuscode.Success {
|
||||||
e.Log.Errorf("获取授权请求失败,status %d", code)
|
e.Log.Errorf("获取授权请求失败,status %d", code)
|
||||||
@ -602,14 +725,14 @@ func (e *SmsTextVerified) GetVerificationPrice(sericeName string) (decimal.Decim
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 单次接收
|
// 单次接收
|
||||||
func (e *SmsTextVerified) CreateVerification(serviceCode string) (dto.TextVerifiedResp, int) {
|
func (e *SmsTextVerified) CreateVerification(serviceCode string, apiInfo *dto.SmsPlatformKeyQueueDto) (dto.TextVerifiedResp, int) {
|
||||||
req := dto.TextVerifiedCreateRewalReq{
|
req := dto.TextVerifiedCreateRewalReq{
|
||||||
ServiceName: serviceCode,
|
ServiceName: serviceCode,
|
||||||
Capability: "sms",
|
Capability: "sms",
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := dto.TextVerifiedResp{}
|
resp := dto.TextVerifiedResp{}
|
||||||
client, code := e.GetTextVerifiedAuthClient()
|
client, code := e.GetTextVerifiedAuthClient(apiInfo)
|
||||||
|
|
||||||
if code != statuscode.Success {
|
if code != statuscode.Success {
|
||||||
return resp, code
|
return resp, code
|
||||||
@ -634,8 +757,8 @@ func (e *SmsTextVerified) CreateVerification(serviceCode string) (dto.TextVerifi
|
|||||||
|
|
||||||
// 取消验证码
|
// 取消验证码
|
||||||
// typ 0-短效 1-长效
|
// typ 0-短效 1-长效
|
||||||
func (e SmsTextVerified) CancelRental(id string, typ int) int {
|
func (e SmsTextVerified) CancelRental(id string, typ int, apiInfo *dto.SmsPlatformKeyQueueDto) int {
|
||||||
client, code := e.GetTextVerifiedAuthClient()
|
client, code := e.GetTextVerifiedAuthClient(apiInfo)
|
||||||
|
|
||||||
if code != statuscode.Success {
|
if code != statuscode.Success {
|
||||||
return code
|
return code
|
||||||
@ -670,8 +793,8 @@ func (e SmsTextVerified) CancelRental(id string, typ int) int {
|
|||||||
return statuscode.Success
|
return statuscode.Success
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *SmsTextVerified) GetTextVerifiedAuthClient() (*httphelper.HTTPClient, int) {
|
func (e *SmsTextVerified) GetTextVerifiedAuthClient(apiInfo *dto.SmsPlatformKeyQueueDto) (*httphelper.HTTPClient, int) {
|
||||||
header, err := e.GetAuthHeader()
|
header, err := e.GetAuthHeader(apiInfo)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.Log.Errorf("取消验证码获取token失败 error: %v", err)
|
e.Log.Errorf("取消验证码获取token失败 error: %v", err)
|
||||||
@ -794,10 +917,10 @@ func (e *SmsTextVerified) TextVerifiedWebHook(req *dto.TextVerifiedWebHookReq) e
|
|||||||
|
|
||||||
// 续期
|
// 续期
|
||||||
// typ 0-短效 1-长效
|
// typ 0-短效 1-长效
|
||||||
func (e *SmsTextVerified) Renew(activationId string, status bool) int {
|
func (e *SmsTextVerified) Renew(activationId string, status bool, apiInfo *dto.SmsPlatformKeyQueueDto) int {
|
||||||
url := fmt.Sprintf(updateRentalRenewStatus, activationId)
|
url := fmt.Sprintf(updateRentalRenewStatus, activationId)
|
||||||
|
|
||||||
client, code := e.GetTextVerifiedAuthClient()
|
client, code := e.GetTextVerifiedAuthClient(apiInfo)
|
||||||
|
|
||||||
if code != statuscode.Success {
|
if code != statuscode.Success {
|
||||||
e.Log.Errorf("获取长效续期失败 %d", code)
|
e.Log.Errorf("获取长效续期失败 %d", code)
|
||||||
@ -826,9 +949,201 @@ func (e *SmsTextVerified) Renew(activationId string, status bool) int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 手动续期
|
||||||
|
// 续费id
|
||||||
|
func (e *SmsTextVerified) ManualRenewal(bilingCycleId string, apiInfo *dto.SmsPlatformKeyQueueDto) int {
|
||||||
|
url := fmt.Sprintf(manualRenewal, bilingCycleId)
|
||||||
|
|
||||||
|
client, code := e.GetTextVerifiedAuthClient(apiInfo)
|
||||||
|
|
||||||
|
if code != statuscode.Success {
|
||||||
|
e.Log.Errorf("获取长效续期失败 %d", code)
|
||||||
|
return statuscode.ServerError
|
||||||
|
}
|
||||||
|
|
||||||
|
headers := map[string]string{
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
}
|
||||||
|
mapData := dto.TextVerifiedManualRenewalResp{}
|
||||||
|
statuCode, err := client.Post(url, nil, headers, &mapData)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("修改长效续期状态失败 %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if statuCode >= 200 && statuCode < 300 {
|
||||||
|
return statuscode.Success
|
||||||
|
} else {
|
||||||
|
e.Log.Errorf("修改长效续期状态失败 %d", statuCode)
|
||||||
|
// 没有需要续费的号码 重新设置可自动续费之后再试一次
|
||||||
|
if statuCode == 400 && strings.Contains(err.Error(), "Nothing to renew") {
|
||||||
|
return statuscode.NothingToRenew
|
||||||
|
}
|
||||||
|
|
||||||
|
return statuscode.ServerError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重新初始化短信记录
|
||||||
|
func (e *SmsTextVerified) InitSmsLogs() error {
|
||||||
|
// 获取平台密钥信息
|
||||||
|
smsPlatformKeyRedis := NewSmsPlatformKeyRedis(e.Orm, e.Log)
|
||||||
|
apiInfo, err := smsPlatformKeyRedis.GetApiInfo("textverified", "")
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("获取平台密钥失败: %v", err)
|
||||||
|
return fmt.Errorf("获取平台密钥失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
phoneLogs, err := e.GetUserByNumber("textverified")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("获取系统内号码记录失败 %v", err)
|
||||||
|
return fmt.Errorf("获取系统内号码记录失败 %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(phoneLogs) > 0 {
|
||||||
|
for _, v := range phoneLogs {
|
||||||
|
e.GetCode(v.ActivationId, v.Type, v.UserId, v.Service, v.ServiceCode, &apiInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据电话号码获取系统内记录
|
||||||
|
func (e *SmsTextVerified) GetUserByNumber(platformCode string) ([]models.SmsPhone, error) {
|
||||||
|
var result []models.SmsPhone
|
||||||
|
|
||||||
|
if err := e.Orm.Model(&models.SmsPhone{}).Where("platform_code = ?", platformCode).Find(&result).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取平台下 所有账号的获取记录
|
||||||
|
// map[string][]dto.VerificationDTO key:account
|
||||||
|
func (e *SmsTextVerified) GetAllPlatformNumbers() (map[string][]dto.VerificationDTO, error) {
|
||||||
|
// 获取平台密钥信息
|
||||||
|
smsPlatformKeyRedis := NewSmsPlatformKeyRedis(e.Orm, e.Log)
|
||||||
|
platformKeys, err := smsPlatformKeyRedis.GetPlatformKeys(global.SmsPlatformTextVerified)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("获取平台密钥失败: %v", err)
|
||||||
|
return nil, fmt.Errorf("获取平台密钥失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
allNumbers := make(map[string][]dto.VerificationDTO)
|
||||||
|
|
||||||
|
for _, v := range platformKeys {
|
||||||
|
numbersw, err1 := e.GetAllNumbers(&v)
|
||||||
|
|
||||||
|
if err1 != nil {
|
||||||
|
e.Log.Errorf("获取平台密钥失败: %v", err1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
allNumbers[v.Account] = numbersw
|
||||||
|
}
|
||||||
|
|
||||||
|
return allNumbers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SmsTextVerified) GetAllNumbers(apiInfo *dto.SmsPlatformKeyQueueDto) ([]dto.VerificationDTO, error) {
|
||||||
|
phones, err := e.GetNumbers(apiInfo)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("获取长效号码列表失败 %v", err)
|
||||||
|
return phones, fmt.Errorf("获取长效号码列表失败 %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return phones, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取号码
|
||||||
|
func (e *SmsTextVerified) GetNumbers(apiInfo *dto.SmsPlatformKeyQueueDto) ([]dto.VerificationDTO, error) {
|
||||||
|
client, code := e.GetTextVerifiedAuthClient(apiInfo)
|
||||||
|
result := make([]dto.VerificationDTO, 0)
|
||||||
|
|
||||||
|
if code != statuscode.Success {
|
||||||
|
e.Log.Errorf("获取长效号码列表失败 %d", code)
|
||||||
|
return result, fmt.Errorf("获取长效号码列表失败 %d", code)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := dto.TextVerifiedGetNumbersResp{}
|
||||||
|
statusCode, err := client.Get(getNumbers, map[string]string{}, &resp)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("获取长效号码列表失败 %v", err)
|
||||||
|
return result, fmt.Errorf("获取长效号码列表失败 %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if statusCode == http.StatusOK {
|
||||||
|
// 添加第一页数据
|
||||||
|
for _, item := range resp.Data {
|
||||||
|
item.Number = "1" + item.Number
|
||||||
|
item.ApiKey = apiInfo.ApiKey
|
||||||
|
|
||||||
|
result = append(result, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理分页数据
|
||||||
|
if resp.HasNext {
|
||||||
|
paginatedData, err := e.fetchPaginatedData(resp.Links.Next, apiInfo)
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
result = append(result, paginatedData...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
} else if statusCode == http.StatusUnauthorized {
|
||||||
|
return e.GetNumbers(apiInfo)
|
||||||
|
} else {
|
||||||
|
e.Log.Errorf("获取长效号码列表失败 %d", statusCode)
|
||||||
|
return result, fmt.Errorf("获取长效号码列表失败 %d", statusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 获取授权header头
|
// 获取授权header头
|
||||||
func (e *SmsTextVerified) GetAuthHeader() (map[string]string, error) {
|
// fetchPaginatedData 获取分页数据的通用方法
|
||||||
token, err := e.GetToken()
|
// 处理TextVerified API的分页响应,自动获取所有页面的数据
|
||||||
|
func (e *SmsTextVerified) fetchPaginatedData(nextLink *dto.TextVerifiedResp, apiInfo *dto.SmsPlatformKeyQueueDto) ([]dto.VerificationDTO, error) {
|
||||||
|
var result []dto.VerificationDTO
|
||||||
|
|
||||||
|
for nextLink != nil {
|
||||||
|
nextData, err := e.doRequest(nextLink, apiInfo)
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("获取分页数据失败: %v", err)
|
||||||
|
return result, fmt.Errorf("获取分页数据失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nextResp := dto.TextVerifiedGetNumbersResp{}
|
||||||
|
err = sonic.Unmarshal(nextData, &nextResp)
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("解析分页数据失败: %v", err)
|
||||||
|
return result, fmt.Errorf("解析分页数据失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range nextResp.Data {
|
||||||
|
item.Number = "1" + item.Number
|
||||||
|
item.ApiKey = apiInfo.ApiKey
|
||||||
|
result = append(result, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否还有下一页
|
||||||
|
if nextResp.HasNext {
|
||||||
|
nextLink = nextResp.Links.Next
|
||||||
|
} else {
|
||||||
|
nextLink = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SmsTextVerified) GetAuthHeader(apiInfo *dto.SmsPlatformKeyQueueDto) (map[string]string, error) {
|
||||||
|
token, err := e.GetToken(apiInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -853,3 +1168,70 @@ func getIdByUrl(URLString string) string {
|
|||||||
func isValidID(s string) bool {
|
func isValidID(s string) bool {
|
||||||
return idPattern.MatchString(s)
|
return idPattern.MatchString(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取长效租赁号码列表
|
||||||
|
// next 分页url
|
||||||
|
func (e *SmsTextVerified) GetRentalList(next string, apiInfo *dto.SmsPlatformKeyQueueDto) (dto.TextVerifiedGetRentalListResp, error) {
|
||||||
|
var result dto.TextVerifiedGetRentalListResp
|
||||||
|
|
||||||
|
client, code := e.GetTextVerifiedAuthClient(apiInfo)
|
||||||
|
if code != statuscode.Success {
|
||||||
|
e.Log.Errorf("获取长效号码列表失败 %d", code)
|
||||||
|
return result, fmt.Errorf("获取长效号码列表失败 %d", code)
|
||||||
|
}
|
||||||
|
|
||||||
|
if next == "" {
|
||||||
|
next = rentalList
|
||||||
|
} else {
|
||||||
|
client.BaseURL = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := dto.TextVerifiedGetRentalListResp{}
|
||||||
|
statusCode, err := client.Get(next, map[string]string{}, &resp)
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("获取长效号码列表失败 %v", err)
|
||||||
|
return result, fmt.Errorf("获取长效号码列表失败 %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if statusCode == http.StatusOK {
|
||||||
|
result = resp
|
||||||
|
return result, nil
|
||||||
|
} else if statusCode == http.StatusUnauthorized {
|
||||||
|
return e.GetRentalList(next, apiInfo)
|
||||||
|
} else {
|
||||||
|
e.Log.Errorf("获取长效号码列表失败 %d", statusCode)
|
||||||
|
return result, fmt.Errorf("获取长效号码列表失败 %d", statusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取续费周期详情
|
||||||
|
func (e *SmsTextVerified) GetBillingCycle(billingCycleId string, apiInfo *dto.SmsPlatformKeyQueueDto) (dto.TextVerifiedGetBillingCycleResp, error) {
|
||||||
|
var result dto.TextVerifiedGetBillingCycleResp
|
||||||
|
|
||||||
|
client, code := e.GetTextVerifiedAuthClient(apiInfo)
|
||||||
|
if code != statuscode.Success {
|
||||||
|
e.Log.Errorf("获取计费周期详情失败 %d", code)
|
||||||
|
return result, fmt.Errorf("获取计费周期详情失败 %d", code)
|
||||||
|
}
|
||||||
|
|
||||||
|
next := fmt.Sprintf(getBillingCycles, billingCycleId)
|
||||||
|
|
||||||
|
resp := dto.TextVerifiedGetBillingCycleResp{}
|
||||||
|
statusCode, err := client.Get(next, map[string]string{}, &resp)
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("获取计费周期详情失败 %v", err)
|
||||||
|
return result, fmt.Errorf("获取计费周期详情失败 %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if statusCode >= http.StatusOK && statusCode < http.StatusMultipleChoices {
|
||||||
|
result = resp
|
||||||
|
return result, nil
|
||||||
|
} else if statusCode == http.StatusUnauthorized {
|
||||||
|
return e.GetBillingCycle(billingCycleId, apiInfo)
|
||||||
|
} else if statusCode == http.StatusNotFound {
|
||||||
|
return result, LogNotFund
|
||||||
|
} else {
|
||||||
|
e.Log.Errorf("获取计费周期详情失败 %d", statusCode)
|
||||||
|
return result, fmt.Errorf("获取计费周期详情失败 %d", statusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
224
app/admin/service/sms_text_verified_enhanced.go
Normal file
224
app/admin/service/sms_text_verified_enhanced.go
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"go-admin/app/admin/service/dto"
|
||||||
|
"go-admin/common/global"
|
||||||
|
"go-admin/common/statuscode"
|
||||||
|
"go-admin/config"
|
||||||
|
"go-admin/utils/httphelper"
|
||||||
|
"go-admin/utils/redishelper"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-admin-team/go-admin-core/sdk/service"
|
||||||
|
"github.com/go-redis/redis/v8"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SmsTextVerifiedEnhanced 增强版TextVerified服务,支持token自动刷新
|
||||||
|
type SmsTextVerifiedEnhanced struct {
|
||||||
|
service.Service
|
||||||
|
maxRetries int // 最大重试次数
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSmsTextVerifiedEnhanced 创建增强版TextVerified服务实例
|
||||||
|
func NewSmsTextVerifiedEnhanced() *SmsTextVerifiedEnhanced {
|
||||||
|
return &SmsTextVerifiedEnhanced{
|
||||||
|
maxRetries: 2, // 默认最大重试2次
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthenticatedRequest 带自动token刷新的HTTP请求包装器
|
||||||
|
// 当遇到401未授权错误时,会自动刷新token并重试请求
|
||||||
|
func (e *SmsTextVerifiedEnhanced) AuthenticatedRequest(
|
||||||
|
apiInfo *dto.SmsPlatformKeyQueueDto,
|
||||||
|
requestFunc func(client *httphelper.HTTPClient) (int, error),
|
||||||
|
) (int, error) {
|
||||||
|
var lastErr error
|
||||||
|
|
||||||
|
// 尝试执行请求,包含重试逻辑
|
||||||
|
for attempt := 0; attempt <= e.maxRetries; attempt++ {
|
||||||
|
// 获取认证客户端
|
||||||
|
client, code := e.GetTextVerifiedAuthClient(apiInfo)
|
||||||
|
if code != statuscode.Success {
|
||||||
|
e.Log.Errorf("获取授权客户端失败,状态码: %d", code)
|
||||||
|
return code, fmt.Errorf("获取授权客户端失败,状态码: %d", code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行请求
|
||||||
|
status, err := requestFunc(client)
|
||||||
|
|
||||||
|
// 如果请求成功,直接返回
|
||||||
|
if err == nil {
|
||||||
|
return status, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否为401未授权错误
|
||||||
|
if e.isUnauthorizedError(status, err) {
|
||||||
|
e.Log.Warnf("检测到token过期或未授权错误 (尝试 %d/%d): %v", attempt+1, e.maxRetries+1, err)
|
||||||
|
|
||||||
|
// 如果不是最后一次尝试,清除token缓存并重试
|
||||||
|
if attempt < e.maxRetries {
|
||||||
|
if clearErr := e.clearTokenCache(apiInfo); clearErr != nil {
|
||||||
|
e.Log.Errorf("清除token缓存失败: %v", clearErr)
|
||||||
|
}
|
||||||
|
e.Log.Infof("正在重试请求... (尝试 %d/%d)", attempt+2, e.maxRetries+1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果不是401错误或已达到最大重试次数,记录错误并退出循环
|
||||||
|
lastErr = err
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回最后一次的错误
|
||||||
|
return statuscode.ServerError, fmt.Errorf("请求失败,已重试%d次: %v", e.maxRetries, lastErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// isUnauthorizedError 检查错误是否为401未授权错误
|
||||||
|
func (e *SmsTextVerifiedEnhanced) isUnauthorizedError(status int, err error) bool {
|
||||||
|
if status == http.StatusUnauthorized {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
errorMsg := strings.ToLower(err.Error())
|
||||||
|
// 检查常见的未授权错误消息
|
||||||
|
return strings.Contains(errorMsg, "unauthorized") ||
|
||||||
|
strings.Contains(errorMsg, "401") ||
|
||||||
|
strings.Contains(errorMsg, "invalid token") ||
|
||||||
|
strings.Contains(errorMsg, "token expired") ||
|
||||||
|
strings.Contains(errorMsg, "authentication failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// clearTokenCache 清除Redis中的token缓存
|
||||||
|
func (e *SmsTextVerifiedEnhanced) clearTokenCache(apiInfo *dto.SmsPlatformKeyQueueDto) error {
|
||||||
|
key := fmt.Sprintf(global.TextVerifiedToken, apiInfo.ApiKey)
|
||||||
|
err := redishelper.DefaultRedis.DeleteString(key)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("删除token缓存失败: %v", err)
|
||||||
|
}
|
||||||
|
e.Log.Infof("已清除token缓存: %s", key)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTextVerifiedAuthClient 获取认证的HTTP客户端
|
||||||
|
// 这个方法与原版相同,但可以在这里添加额外的逻辑
|
||||||
|
func (e *SmsTextVerifiedEnhanced) GetTextVerifiedAuthClient(apiInfo *dto.SmsPlatformKeyQueueDto) (*httphelper.HTTPClient, int) {
|
||||||
|
header, err := e.GetAuthHeader(apiInfo)
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("获取授权头失败: %v", err)
|
||||||
|
return nil, statuscode.ServerError
|
||||||
|
}
|
||||||
|
client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.SmsTextVerified.Url, header)
|
||||||
|
return client, statuscode.Success
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAuthHeader 获取认证头
|
||||||
|
func (e *SmsTextVerifiedEnhanced) GetAuthHeader(apiInfo *dto.SmsPlatformKeyQueueDto) (map[string]string, error) {
|
||||||
|
token, err := e.GetToken(apiInfo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
headers := map[string]string{
|
||||||
|
"Authorization": "Bearer " + token,
|
||||||
|
}
|
||||||
|
return headers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetToken 获取token(与原版相同的逻辑)
|
||||||
|
func (e *SmsTextVerifiedEnhanced) GetToken(apiInfo *dto.SmsPlatformKeyQueueDto) (string, error) {
|
||||||
|
key := fmt.Sprintf(global.TextVerifiedToken, apiInfo.ApiKey)
|
||||||
|
token, err := redishelper.DefaultRedis.GetString(key)
|
||||||
|
if err != nil && errors.Is(err, redis.Nil) {
|
||||||
|
// token不存在,重新登录获取
|
||||||
|
return e.Login(apiInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
if token == "" {
|
||||||
|
return e.Login(apiInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
return token, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Login 登录获取token(与原版相同的逻辑)
|
||||||
|
func (e *SmsTextVerifiedEnhanced) Login(apiInfo *dto.SmsPlatformKeyQueueDto) (string, error) {
|
||||||
|
resp := dto.TextVerifiedLoginResp{}
|
||||||
|
var token string
|
||||||
|
client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.SmsTextVerified.Url, nil)
|
||||||
|
headers := map[string]string{
|
||||||
|
"X-API-USERNAME": apiInfo.ApiSecret,
|
||||||
|
"X-API-KEY": apiInfo.ApiKey,
|
||||||
|
}
|
||||||
|
_, err1 := client.Post("/api/pub/v2/auth", nil, headers, &resp)
|
||||||
|
|
||||||
|
if err1 != nil {
|
||||||
|
e.Log.Errorf("TextVerified登录失败 error: %v", err1)
|
||||||
|
return "", err1
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.Token == "" {
|
||||||
|
e.Log.Errorf("TextVerified登录失败,返回的Token为空")
|
||||||
|
return "", errors.New("TextVerified登录失败,返回的Token为空")
|
||||||
|
}
|
||||||
|
|
||||||
|
token = resp.Token
|
||||||
|
|
||||||
|
if resp.ExpiresIn >= 10 {
|
||||||
|
resp.ExpiresIn = resp.ExpiresIn - 10 // 提前10秒过期
|
||||||
|
key := fmt.Sprintf(global.TextVerifiedToken, apiInfo.ApiKey)
|
||||||
|
if err := redishelper.DefaultRedis.SetStringExpire(key, token, time.Duration(resp.ExpiresIn)*time.Second); err != nil {
|
||||||
|
e.Log.Errorf("TextVerified登录失败,缓存Token失败 error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return token, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用示例:增强版的GetCode方法
|
||||||
|
func (e *SmsTextVerifiedEnhanced) GetCodeEnhanced(messageId string, typ int, userId int, service, serviceCode string, apiInfo *dto.SmsPlatformKeyQueueDto) (string, int) {
|
||||||
|
reservationType := ""
|
||||||
|
if typ == 0 {
|
||||||
|
reservationType = "verification"
|
||||||
|
} else {
|
||||||
|
reservationType = "renewable"
|
||||||
|
}
|
||||||
|
url := fmt.Sprintf("/api/pub/v2/sms?reservationId=%s&reservationType=%s", messageId, reservationType)
|
||||||
|
|
||||||
|
var parsedCode string
|
||||||
|
var finalStatus int
|
||||||
|
|
||||||
|
// 使用增强的请求方法
|
||||||
|
status, err := e.AuthenticatedRequest(apiInfo, func(client *httphelper.HTTPClient) (int, error) {
|
||||||
|
resp := dto.TextVerifiedSmsResp{}
|
||||||
|
status, err := client.Get(url, nil, &resp)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return status, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理响应数据...
|
||||||
|
if len(resp.Data) > 0 {
|
||||||
|
// 这里可以添加原有的验证码处理逻辑
|
||||||
|
parsedCode = resp.Data[0].ParsedCode
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.StatusOK, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("获取验证码失败: %v", err)
|
||||||
|
finalStatus = statuscode.ServerError
|
||||||
|
} else {
|
||||||
|
finalStatus = status
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsedCode, finalStatus
|
||||||
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"go-admin/common/global"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/go-admin-team/go-admin-core/logger"
|
"github.com/go-admin-team/go-admin-core/logger"
|
||||||
@ -13,7 +14,15 @@ func TestSmsTextVerifiedLogin(t *testing.T) {
|
|||||||
s.Orm = db
|
s.Orm = db
|
||||||
s.Log = logger.NewHelper(logger.DefaultLogger)
|
s.Log = logger.NewHelper(logger.DefaultLogger)
|
||||||
|
|
||||||
token, err := s.Login()
|
smsPlatformKeyRedis := NewSmsPlatformKeyRedis(s.Orm, s.Log)
|
||||||
|
apiInfo, err := smsPlatformKeyRedis.GetRoundRobinKey(global.SmsPlatformTextVerified)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
s.Log.Errorf("获取API信息失败: %v", err)
|
||||||
|
t.Error("获取API信息失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := s.Login(apiInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Login failed: %v", err)
|
t.Errorf("Login failed: %v", err)
|
||||||
} else {
|
} else {
|
||||||
@ -27,9 +36,16 @@ func TestSmsTextVerifiedGetServices(t *testing.T) {
|
|||||||
s := SmsTextVerified{}
|
s := SmsTextVerified{}
|
||||||
s.Orm = db
|
s.Orm = db
|
||||||
s.Log = logger.NewHelper(logger.DefaultLogger)
|
s.Log = logger.NewHelper(logger.DefaultLogger)
|
||||||
|
smsPlatformKeyRedis := NewSmsPlatformKeyRedis(s.Orm, s.Log)
|
||||||
|
apiInfo, err := smsPlatformKeyRedis.GetRoundRobinKey(global.SmsPlatformTextVerified)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
s.Log.Errorf("获取API信息失败: %v", err)
|
||||||
|
t.Error("获取API信息失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Now, test GetServices with the valid token
|
// Now, test GetServices with the valid token
|
||||||
servicesResp, err := s.GetServices()
|
servicesResp, err := s.GetServices(apiInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("GetServices failed: %v", err)
|
t.Errorf("GetServices failed: %v", err)
|
||||||
} else {
|
} else {
|
||||||
@ -51,3 +67,18 @@ func TestSyncTextVerifiedServices(t *testing.T) {
|
|||||||
t.Log("SyncTextVerifiedServices succeeded")
|
t.Log("SyncTextVerifiedServices succeeded")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestInitSmsLogs(t *testing.T) {
|
||||||
|
db := initSetting()
|
||||||
|
|
||||||
|
s := SmsTextVerified{}
|
||||||
|
s.Orm = db
|
||||||
|
s.Log = logger.NewHelper(logger.DefaultLogger)
|
||||||
|
|
||||||
|
err := s.InitSmsLogs()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("InitSmsLogs failed: %v", err)
|
||||||
|
} else {
|
||||||
|
t.Log("InitSmsLogs succeeded")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -37,11 +37,11 @@ func (j RenewalJob) Exec(args interface{}) error {
|
|||||||
|
|
||||||
// 定时短信续期任务
|
// 定时短信续期任务
|
||||||
func (j SmsRenewalJob) Exec(args interface{}) error {
|
func (j SmsRenewalJob) Exec(args interface{}) error {
|
||||||
smsService := service.SmsServices{}
|
smsPhoneService := service.SmsRenewalLog{}
|
||||||
smsService.Orm = GetDb()
|
smsPhoneService.Orm = GetDb()
|
||||||
smsService.Log = logger.NewHelper(logger.DefaultLogger)
|
smsPhoneService.Log = logger.NewHelper(logger.DefaultLogger)
|
||||||
|
|
||||||
return smsService.AutoRenewal()
|
return smsPhoneService.AutoRenewal()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 过期任务
|
// 过期任务
|
||||||
|
|||||||
@ -23,7 +23,7 @@ func TestTrxJob(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func initSetting() {
|
func initSetting() {
|
||||||
dsn := "root:123456@tcp(127.0.0.1:3306)/proxy_server?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms"
|
dsn := "root:123456@tcp(127.0.0.1:3306)/proxy_server_prod?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms"
|
||||||
db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})
|
db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})
|
||||||
sdk.Runtime.SetDb("default", db)
|
sdk.Runtime.SetDb("default", db)
|
||||||
config.ExtConfig.TrxGridUrl = "https://api.trongrid.io"
|
config.ExtConfig.TrxGridUrl = "https://api.trongrid.io"
|
||||||
|
|||||||
@ -223,4 +223,12 @@ func initBusinesses() {
|
|||||||
memberApiService.Log = cliProxyService.Log
|
memberApiService.Log = cliProxyService.Log
|
||||||
|
|
||||||
memberApiService.InitApis()
|
memberApiService.InitApis()
|
||||||
|
|
||||||
|
// 创建SMS平台密钥服务实例并初始化Redis缓存
|
||||||
|
smsPlatformKey := service.SmsPlatformKey{}
|
||||||
|
smsPlatformKey.Orm = cliProxyService.Orm
|
||||||
|
smsPlatformKey.Log = cliProxyService.Log
|
||||||
|
|
||||||
|
// 初始化Redis缓存队列
|
||||||
|
smsPlatformKey.InitQueque()
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
cmd/proxy-server
BIN
cmd/proxy-server
Binary file not shown.
@ -1,6 +1,6 @@
|
|||||||
package global
|
package global
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// TextVerified Token
|
// TextVerified Token {apiKey}
|
||||||
TextVerifiedToken = "TextVerifiedToken"
|
TextVerifiedToken = "TextVerifiedToken:%s"
|
||||||
)
|
)
|
||||||
|
|||||||
6
common/rediskey/sms-platform-key.go
Normal file
6
common/rediskey/sms-platform-key.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package rediskey
|
||||||
|
|
||||||
|
const (
|
||||||
|
//平台key
|
||||||
|
SmsPlatformKey = "sms_platform_key:%s"
|
||||||
|
)
|
||||||
@ -41,6 +41,8 @@ var StatusCodeZh = map[int]string{
|
|||||||
SmsInvalidType: "短信验证码_无效类型",
|
SmsInvalidType: "短信验证码_无效类型",
|
||||||
SmsOutOfStockOrUnavailable: "短信验证码_缺货或服务不可用",
|
SmsOutOfStockOrUnavailable: "短信验证码_缺货或服务不可用",
|
||||||
SmsRentalRefundNotPermitted: "短信验证码_租赁退款不允许",
|
SmsRentalRefundNotPermitted: "短信验证码_租赁退款不允许",
|
||||||
|
SmsRentalCantRenew: "短信验证码_无法续期",
|
||||||
|
SmsRenewalLogExisted: "短信验证码_重复续期",
|
||||||
}
|
}
|
||||||
|
|
||||||
var StatusCodeEn = map[int]string{
|
var StatusCodeEn = map[int]string{
|
||||||
@ -74,6 +76,8 @@ var StatusCodeEn = map[int]string{
|
|||||||
SmsInvalidType: "sms type invalid",
|
SmsInvalidType: "sms type invalid",
|
||||||
SmsOutOfStockOrUnavailable: "sms out of stock or unavailable",
|
SmsOutOfStockOrUnavailable: "sms out of stock or unavailable",
|
||||||
SmsRentalRefundNotPermitted: "sms rental refund not permitted",
|
SmsRentalRefundNotPermitted: "sms rental refund not permitted",
|
||||||
|
SmsRentalCantRenew: "sms rental can not renewal",
|
||||||
|
SmsRenewalLogExisted: "sms renewal log existed",
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMsg 获取状态码对应的消息
|
// GetMsg 获取状态码对应的消息
|
||||||
@ -107,7 +111,8 @@ const (
|
|||||||
AccountOrPasswordError = 10003
|
AccountOrPasswordError = 10003
|
||||||
//密码不一致
|
//密码不一致
|
||||||
PassWordNotMatch = 10004
|
PassWordNotMatch = 10004
|
||||||
|
// 没有需要续费的号码 重新设置可自动续费之后再试一次
|
||||||
|
NothingToRenew = 11005
|
||||||
//
|
//
|
||||||
ServerError = 500
|
ServerError = 500
|
||||||
|
|
||||||
@ -161,4 +166,8 @@ const (
|
|||||||
SmsOutOfStockOrUnavailable = 20024
|
SmsOutOfStockOrUnavailable = 20024
|
||||||
// 短信-租赁退款不允许
|
// 短信-租赁退款不允许
|
||||||
SmsRentalRefundNotPermitted = 20025
|
SmsRentalRefundNotPermitted = 20025
|
||||||
|
// 短信-无法续期
|
||||||
|
SmsRentalCantRenew = 20026
|
||||||
|
// 短信-重复续期
|
||||||
|
SmsRenewalLogExisted = 20028
|
||||||
)
|
)
|
||||||
|
|||||||
221
utils/utility/crypto_helper.go
Normal file
221
utils/utility/crypto_helper.go
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
package utility
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 加密key
|
||||||
|
var CryptoKey = "ProxyServer@#(123321)!Keycrypto"
|
||||||
|
|
||||||
|
// CryptoHelper 加密帮助类
|
||||||
|
type CryptoHelper struct {
|
||||||
|
key []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCryptoHelper 创建新的加密帮助实例
|
||||||
|
// key: 32字节的加密密钥,如果长度不足会自动填充,超出会截断
|
||||||
|
func NewCryptoHelper(key string) *CryptoHelper {
|
||||||
|
// 确保密钥长度为32字节(AES-256)
|
||||||
|
keyBytes := make([]byte, 32)
|
||||||
|
copy(keyBytes, []byte(key))
|
||||||
|
return &CryptoHelper{
|
||||||
|
key: keyBytes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt 加密字符串
|
||||||
|
// plaintext: 要加密的明文
|
||||||
|
// 返回: base64编码的密文和错误信息
|
||||||
|
func (c *CryptoHelper) Encrypt(plaintext string) (string, error) {
|
||||||
|
if plaintext == "" {
|
||||||
|
return "", errors.New("plaintext cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建AES cipher
|
||||||
|
block, err := aes.NewCipher(c.key)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to create cipher: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用GCM模式
|
||||||
|
gcm, err := cipher.NewGCM(block)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to create GCM: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成随机nonce
|
||||||
|
nonce := make([]byte, gcm.NonceSize())
|
||||||
|
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
||||||
|
return "", fmt.Errorf("failed to generate nonce: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加密数据
|
||||||
|
ciphertext := gcm.Seal(nonce, nonce, []byte(plaintext), nil)
|
||||||
|
|
||||||
|
// 返回base64编码的结果
|
||||||
|
return base64.StdEncoding.EncodeToString(ciphertext), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt 解密字符串
|
||||||
|
// ciphertext: base64编码的密文
|
||||||
|
// 返回: 解密后的明文和错误信息
|
||||||
|
func (c *CryptoHelper) Decrypt(ciphertext string) (string, error) {
|
||||||
|
if ciphertext == "" {
|
||||||
|
return "", errors.New("ciphertext cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
// base64解码
|
||||||
|
data, err := base64.StdEncoding.DecodeString(ciphertext)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to decode base64: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建AES cipher
|
||||||
|
block, err := aes.NewCipher(c.key)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to create cipher: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用GCM模式
|
||||||
|
gcm, err := cipher.NewGCM(block)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to create GCM: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查数据长度
|
||||||
|
nonceSize := gcm.NonceSize()
|
||||||
|
if len(data) < nonceSize {
|
||||||
|
return "", errors.New("ciphertext too short")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取nonce和密文
|
||||||
|
nonce, cipherData := data[:nonceSize], data[nonceSize:]
|
||||||
|
|
||||||
|
// 解密数据
|
||||||
|
plaintext, err := gcm.Open(nil, nonce, cipherData, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to decrypt: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(plaintext), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncryptBytes 加密字节数组
|
||||||
|
// data: 要加密的字节数组
|
||||||
|
// 返回: 加密后的字节数组和错误信息
|
||||||
|
func (c *CryptoHelper) EncryptBytes(data []byte) ([]byte, error) {
|
||||||
|
if len(data) == 0 {
|
||||||
|
return nil, errors.New("data cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建AES cipher
|
||||||
|
block, err := aes.NewCipher(c.key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create cipher: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用GCM模式
|
||||||
|
gcm, err := cipher.NewGCM(block)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create GCM: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成随机nonce
|
||||||
|
nonce := make([]byte, gcm.NonceSize())
|
||||||
|
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to generate nonce: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加密数据
|
||||||
|
ciphertext := gcm.Seal(nonce, nonce, data, nil)
|
||||||
|
|
||||||
|
return ciphertext, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecryptBytes 解密字节数组
|
||||||
|
// ciphertext: 加密后的字节数组
|
||||||
|
// 返回: 解密后的字节数组和错误信息
|
||||||
|
func (c *CryptoHelper) DecryptBytes(ciphertext []byte) ([]byte, error) {
|
||||||
|
if len(ciphertext) == 0 {
|
||||||
|
return nil, errors.New("ciphertext cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建AES cipher
|
||||||
|
block, err := aes.NewCipher(c.key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create cipher: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用GCM模式
|
||||||
|
gcm, err := cipher.NewGCM(block)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create GCM: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查数据长度
|
||||||
|
nonceSize := gcm.NonceSize()
|
||||||
|
if len(ciphertext) < nonceSize {
|
||||||
|
return nil, errors.New("ciphertext too short")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取nonce和密文
|
||||||
|
nonce, cipherData := ciphertext[:nonceSize], ciphertext[nonceSize:]
|
||||||
|
|
||||||
|
// 解密数据
|
||||||
|
plaintext, err := gcm.Open(nil, nonce, cipherData, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decrypt: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return plaintext, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateKey 生成随机密钥
|
||||||
|
// 返回: 32字节的随机密钥字符串
|
||||||
|
func GenerateKey() (string, error) {
|
||||||
|
key := make([]byte, 32)
|
||||||
|
if _, err := io.ReadFull(rand.Reader, key); err != nil {
|
||||||
|
return "", fmt.Errorf("failed to generate key: %w", err)
|
||||||
|
}
|
||||||
|
return base64.StdEncoding.EncodeToString(key), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// QuickEncrypt 快速加密函数(使用默认密钥)
|
||||||
|
// plaintext: 要加密的明文
|
||||||
|
// key: 加密密钥
|
||||||
|
// 返回: base64编码的密文和错误信息
|
||||||
|
func QuickEncrypt(plaintext, key string) (string, error) {
|
||||||
|
crypto := NewCryptoHelper(key)
|
||||||
|
return crypto.Encrypt(plaintext)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QuickDecrypt2 快速解密函数(使用默认密钥)
|
||||||
|
// ciphertext: base64编码的密文
|
||||||
|
// 返回: 解密后的明文和错误信息
|
||||||
|
func QuickDecrypt2(ciphertext string) (string, error) {
|
||||||
|
crypto := NewCryptoHelper(CryptoKey)
|
||||||
|
return crypto.Decrypt(ciphertext)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QuickEncrypt2 快速加密函数(使用默认密钥)
|
||||||
|
// plaintext: 要加密的明文
|
||||||
|
// 返回: base64编码的密文和错误信息
|
||||||
|
func QuickEncrypt2(plaintext string) (string, error) {
|
||||||
|
crypto := NewCryptoHelper(CryptoKey)
|
||||||
|
return crypto.Encrypt(plaintext)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QuickDecrypt 快速解密函数(使用默认密钥)
|
||||||
|
// ciphertext: base64编码的密文
|
||||||
|
// key: 解密密钥
|
||||||
|
// 返回: 解密后的明文和错误信息
|
||||||
|
func QuickDecrypt(ciphertext, key string) (string, error) {
|
||||||
|
crypto := NewCryptoHelper(key)
|
||||||
|
return crypto.Decrypt(ciphertext)
|
||||||
|
}
|
||||||
371
utils/utility/generic_queue.go
Normal file
371
utils/utility/generic_queue.go
Normal file
@ -0,0 +1,371 @@
|
|||||||
|
package utility
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GenericQueue 通用循环队列
|
||||||
|
// 支持存储任意类型的数据,实现负载均衡和容错
|
||||||
|
type GenericQueue[T any] struct {
|
||||||
|
data []T // 存储数据的数组
|
||||||
|
current int // 当前指针位置
|
||||||
|
size int // 队列中的元素数量
|
||||||
|
capacity int // 队列容量
|
||||||
|
mutex sync.RWMutex // 读写锁,保证线程安全
|
||||||
|
lastUsed map[int]time.Time // 记录每个位置的最后使用时间
|
||||||
|
cooldown time.Duration // 冷却时间,避免频繁使用同一个元素
|
||||||
|
comparer func(T, T) bool // 比较函数,用于检查重复元素
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// QuequeMap 全局队列映射表,用于管理多个命名队列
|
||||||
|
// 使用interface{}类型以支持不同泛型类型的队列
|
||||||
|
QuequeMap = make(map[string]interface{})
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewGenericQueue 创建新的通用循环队列
|
||||||
|
// capacity: 队列容量
|
||||||
|
// comparer: 比较函数,用于检查重复元素(可选)
|
||||||
|
// cooldown: 元素使用冷却时间(可选,默认0表示无冷却)
|
||||||
|
func NewGenericQueue[T any](capacity int, comparer func(T, T) bool, cooldown ...time.Duration) *GenericQueue[T] {
|
||||||
|
cd := time.Duration(0)
|
||||||
|
if len(cooldown) > 0 {
|
||||||
|
cd = cooldown[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
return &GenericQueue[T]{
|
||||||
|
data: make([]T, capacity),
|
||||||
|
capacity: capacity,
|
||||||
|
lastUsed: make(map[int]time.Time),
|
||||||
|
cooldown: cd,
|
||||||
|
comparer: comparer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add 添加元素到队列
|
||||||
|
// item: 要添加的元素
|
||||||
|
// 返回: 是否添加成功
|
||||||
|
func (q *GenericQueue[T]) Add(item T) bool {
|
||||||
|
q.mutex.Lock()
|
||||||
|
defer q.mutex.Unlock()
|
||||||
|
|
||||||
|
if q.size >= q.capacity {
|
||||||
|
return false // 队列已满
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果提供了比较函数,检查是否已存在相同的元素
|
||||||
|
if q.comparer != nil {
|
||||||
|
for i := 0; i < q.size; i++ {
|
||||||
|
if q.comparer(q.data[i], item) {
|
||||||
|
return false // 已存在
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
q.data[q.size] = item
|
||||||
|
q.size++
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove 从队列中移除指定的元素
|
||||||
|
// item: 要移除的元素
|
||||||
|
// 返回: 是否移除成功
|
||||||
|
func (q *GenericQueue[T]) Remove(item T) bool {
|
||||||
|
q.mutex.Lock()
|
||||||
|
defer q.mutex.Unlock()
|
||||||
|
|
||||||
|
if q.comparer == nil {
|
||||||
|
return false // 没有比较函数无法移除
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < q.size; i++ {
|
||||||
|
if q.comparer(q.data[i], item) {
|
||||||
|
// 将后面的元素前移
|
||||||
|
for j := i; j < q.size-1; j++ {
|
||||||
|
q.data[j] = q.data[j+1]
|
||||||
|
}
|
||||||
|
q.size--
|
||||||
|
|
||||||
|
// 调整current指针
|
||||||
|
if q.current >= q.size && q.size > 0 {
|
||||||
|
q.current = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理lastUsed记录
|
||||||
|
delete(q.lastUsed, i)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNext 获取下一个可用的元素(轮询方式)
|
||||||
|
// 返回: 元素和是否获取成功
|
||||||
|
func (q *GenericQueue[T]) GetNext() (T, bool) {
|
||||||
|
q.mutex.Lock()
|
||||||
|
defer q.mutex.Unlock()
|
||||||
|
|
||||||
|
var zero T
|
||||||
|
if q.size == 0 {
|
||||||
|
return zero, false // 队列为空
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有设置冷却时间,直接返回下一个元素
|
||||||
|
if q.cooldown == 0 {
|
||||||
|
item := q.data[q.current]
|
||||||
|
q.lastUsed[q.current] = time.Now()
|
||||||
|
q.current = (q.current + 1) % q.size
|
||||||
|
return item, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 寻找可用的元素(考虑冷却时间)
|
||||||
|
startPos := q.current
|
||||||
|
for {
|
||||||
|
lastUsed, exists := q.lastUsed[q.current]
|
||||||
|
|
||||||
|
// 如果元素从未使用过,或者已过冷却时间
|
||||||
|
if !exists || time.Since(lastUsed) >= q.cooldown {
|
||||||
|
item := q.data[q.current]
|
||||||
|
q.lastUsed[q.current] = time.Now()
|
||||||
|
q.current = (q.current + 1) % q.size
|
||||||
|
return item, true
|
||||||
|
}
|
||||||
|
|
||||||
|
q.current = (q.current + 1) % q.size
|
||||||
|
|
||||||
|
// 如果遍历了一圈都没找到可用的元素
|
||||||
|
if q.current == startPos {
|
||||||
|
// 返回当前元素,忽略冷却时间
|
||||||
|
item := q.data[q.current]
|
||||||
|
q.lastUsed[q.current] = time.Now()
|
||||||
|
q.current = (q.current + 1) % q.size
|
||||||
|
return item, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRandom 随机获取一个元素
|
||||||
|
// 返回: 元素和是否获取成功
|
||||||
|
func (q *GenericQueue[T]) GetRandom() (T, bool) {
|
||||||
|
q.mutex.RLock()
|
||||||
|
defer q.mutex.RUnlock()
|
||||||
|
|
||||||
|
var zero T
|
||||||
|
if q.size == 0 {
|
||||||
|
return zero, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用当前时间作为随机种子
|
||||||
|
index := int(time.Now().UnixNano()) % q.size
|
||||||
|
item := q.data[index]
|
||||||
|
q.lastUsed[index] = time.Now()
|
||||||
|
return item, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAll 获取所有元素的副本
|
||||||
|
// 返回: 元素切片
|
||||||
|
func (q *GenericQueue[T]) GetAll() []T {
|
||||||
|
q.mutex.RLock()
|
||||||
|
defer q.mutex.RUnlock()
|
||||||
|
|
||||||
|
items := make([]T, q.size)
|
||||||
|
copy(items, q.data[:q.size])
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size 获取队列中的元素数量
|
||||||
|
// 返回: 元素数量
|
||||||
|
func (q *GenericQueue[T]) Size() int {
|
||||||
|
q.mutex.RLock()
|
||||||
|
defer q.mutex.RUnlock()
|
||||||
|
return q.size
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty 检查队列是否为空
|
||||||
|
// 返回: 是否为空
|
||||||
|
func (q *GenericQueue[T]) IsEmpty() bool {
|
||||||
|
q.mutex.RLock()
|
||||||
|
defer q.mutex.RUnlock()
|
||||||
|
return q.size == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsFull 检查队列是否已满
|
||||||
|
// 返回: 是否已满
|
||||||
|
func (q *GenericQueue[T]) IsFull() bool {
|
||||||
|
q.mutex.RLock()
|
||||||
|
defer q.mutex.RUnlock()
|
||||||
|
return q.size >= q.capacity
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear 清空队列
|
||||||
|
func (q *GenericQueue[T]) Clear() {
|
||||||
|
q.mutex.Lock()
|
||||||
|
defer q.mutex.Unlock()
|
||||||
|
|
||||||
|
q.size = 0
|
||||||
|
q.current = 0
|
||||||
|
q.lastUsed = make(map[int]time.Time)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCooldown 设置元素使用冷却时间
|
||||||
|
// cooldown: 冷却时间
|
||||||
|
func (q *GenericQueue[T]) SetCooldown(cooldown time.Duration) {
|
||||||
|
q.mutex.Lock()
|
||||||
|
defer q.mutex.Unlock()
|
||||||
|
q.cooldown = cooldown
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUsageInfo 获取元素使用信息
|
||||||
|
// 返回: 位置使用时间映射
|
||||||
|
func (q *GenericQueue[T]) GetUsageInfo() map[int]time.Time {
|
||||||
|
q.mutex.RLock()
|
||||||
|
defer q.mutex.RUnlock()
|
||||||
|
|
||||||
|
usage := make(map[int]time.Time)
|
||||||
|
for k, v := range q.lastUsed {
|
||||||
|
usage[k] = v
|
||||||
|
}
|
||||||
|
return usage
|
||||||
|
}
|
||||||
|
|
||||||
|
// BatchAdd 批量添加元素
|
||||||
|
// items: 要添加的元素切片
|
||||||
|
// 返回: 成功添加的数量
|
||||||
|
func (q *GenericQueue[T]) BatchAdd(items []T) int {
|
||||||
|
count := 0
|
||||||
|
for _, item := range items {
|
||||||
|
if q.Add(item) {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace 替换所有元素
|
||||||
|
// items: 新的元素切片
|
||||||
|
// 返回: 是否替换成功
|
||||||
|
func (q *GenericQueue[T]) Replace(items []T) bool {
|
||||||
|
if len(items) > q.capacity {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
q.mutex.Lock()
|
||||||
|
defer q.mutex.Unlock()
|
||||||
|
|
||||||
|
q.size = 0
|
||||||
|
q.current = 0
|
||||||
|
q.lastUsed = make(map[int]time.Time)
|
||||||
|
|
||||||
|
for _, item := range items {
|
||||||
|
q.data[q.size] = item
|
||||||
|
q.size++
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplaceItem 替换指定的单个元素
|
||||||
|
// oldItem: 要被替换的元素
|
||||||
|
// newItem: 新的元素
|
||||||
|
// 返回: 是否替换成功
|
||||||
|
func (q *GenericQueue[T]) ReplaceItem(oldItem, newItem T) bool {
|
||||||
|
q.mutex.Lock()
|
||||||
|
defer q.mutex.Unlock()
|
||||||
|
|
||||||
|
if q.comparer == nil {
|
||||||
|
return false // 没有比较函数无法查找元素
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < q.size; i++ {
|
||||||
|
if q.comparer(q.data[i], oldItem) {
|
||||||
|
q.data[i] = newItem
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false // 未找到要替换的元素
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enqueue 入队操作(队列尾部添加元素)
|
||||||
|
// item: 要添加的元素
|
||||||
|
// 返回: 是否添加成功
|
||||||
|
func (q *GenericQueue[T]) Enqueue(item T) bool {
|
||||||
|
return q.Add(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dequeue 出队操作(从队列头部移除并返回元素)
|
||||||
|
// 返回: 元素和是否成功
|
||||||
|
func (q *GenericQueue[T]) Dequeue() (T, bool) {
|
||||||
|
q.mutex.Lock()
|
||||||
|
defer q.mutex.Unlock()
|
||||||
|
|
||||||
|
var zero T
|
||||||
|
if q.size == 0 {
|
||||||
|
return zero, false // 队列为空
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取队列头部元素
|
||||||
|
item := q.data[0]
|
||||||
|
|
||||||
|
// 将后面的元素前移
|
||||||
|
for i := 0; i < q.size-1; i++ {
|
||||||
|
q.data[i] = q.data[i+1]
|
||||||
|
}
|
||||||
|
q.size--
|
||||||
|
|
||||||
|
// 调整current指针
|
||||||
|
if q.current > 0 {
|
||||||
|
q.current--
|
||||||
|
}
|
||||||
|
if q.current >= q.size && q.size > 0 {
|
||||||
|
q.current = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重新映射lastUsed(因为索引发生了变化)
|
||||||
|
newLastUsed := make(map[int]time.Time)
|
||||||
|
for index, lastTime := range q.lastUsed {
|
||||||
|
if index > 0 {
|
||||||
|
newLastUsed[index-1] = lastTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
q.lastUsed = newLastUsed
|
||||||
|
|
||||||
|
return item, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Peek 查看队列头部元素(不移除)
|
||||||
|
// 返回: 元素和是否成功
|
||||||
|
func (q *GenericQueue[T]) Peek() (T, bool) {
|
||||||
|
q.mutex.RLock()
|
||||||
|
defer q.mutex.RUnlock()
|
||||||
|
|
||||||
|
var zero T
|
||||||
|
if q.size == 0 {
|
||||||
|
return zero, false // 队列为空
|
||||||
|
}
|
||||||
|
|
||||||
|
return q.data[0], true
|
||||||
|
}
|
||||||
|
|
||||||
|
// PeekLast 查看队列尾部元素(不移除)
|
||||||
|
// 返回: 元素和是否成功
|
||||||
|
func (q *GenericQueue[T]) PeekLast() (T, bool) {
|
||||||
|
q.mutex.RLock()
|
||||||
|
defer q.mutex.RUnlock()
|
||||||
|
|
||||||
|
var zero T
|
||||||
|
if q.size == 0 {
|
||||||
|
return zero, false // 队列为空
|
||||||
|
}
|
||||||
|
|
||||||
|
return q.data[q.size-1], true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApiKeyInfo API密钥信息结构体
|
||||||
|
type ApiKeyInfo struct {
|
||||||
|
Key string `json:"key"` // API密钥
|
||||||
|
Name string `json:"name"` // 密钥名称
|
||||||
|
Weight int `json:"weight"` // 权重
|
||||||
|
Enabled bool `json:"enabled"` // 是否启用
|
||||||
|
Metadata map[string]string `json:"metadata"` // 元数据
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user