1、平台多ApiKey支持
2、平台号码跟系统号码对比自动续费
This commit is contained in:
		
							
								
								
									
										214
									
								
								app/admin/apis/sms_abnormal_number.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								app/admin/apis/sms_abnormal_number.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,214 @@ | |||||||
|  | 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 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, "同步成功") | ||||||
|  | } | ||||||
							
								
								
									
										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(), "删除成功") | ||||||
|  | } | ||||||
							
								
								
									
										30
									
								
								app/admin/models/sms_abnormal_number.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								app/admin/models/sms_abnormal_number.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | |||||||
|  | package models | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  |  | ||||||
|  | 	"go-admin/common/models" | ||||||
|  |  | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type SmsAbnormalNumber struct { | ||||||
|  |     models.Model | ||||||
|  |      | ||||||
|  |     Account string `json:"account" gorm:"type:varchar(255);comment:账号"`  | ||||||
|  |     PlatformCode string `json:"platformCode" gorm:"type:varchar(20);comment:平台code"`  | ||||||
|  |     Phone string `json:"phone" gorm:"type:varchar(30);comment:电话号码"`  | ||||||
|  |     models.ModelTime | ||||||
|  |     models.ControlBy | ||||||
|  | } | ||||||
|  |  | ||||||
|  | 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-长效"` | ||||||
|  | |||||||
							
								
								
									
										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 | ||||||
|  | } | ||||||
							
								
								
									
										29
									
								
								app/admin/router/sms_abnormal_number.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								app/admin/router/sms_abnormal_number.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | |||||||
|  | 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.POST("/sync-state", api.SyncState) //同步差异 | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										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) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										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 | ||||||
|  | } | ||||||
							
								
								
									
										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"` | ||||||
|  | } | ||||||
| @ -152,8 +152,9 @@ type VerificationDTO struct { | |||||||
| 	// * nonrenewableActive: 服务活跃中。 | 	// * nonrenewableActive: 服务活跃中。 | ||||||
| 	// * nonrenewableExpired: 服务已过期。 | 	// * nonrenewableExpired: 服务已过期。 | ||||||
| 	// * 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:"是否自动续期"` | ||||||
| } | } | ||||||
|  |  | ||||||
| // 长效详情 | // 长效详情 | ||||||
|  | |||||||
							
								
								
									
										250
									
								
								app/admin/service/sms_abnormal_number.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										250
									
								
								app/admin/service/sms_abnormal_number.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,250 @@ | |||||||
|  | package service | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"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/common/global" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type SmsAbnormalNumber struct { | ||||||
|  | 	service.Service | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 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, | ||||||
|  | 				}) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	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" | ||||||
| @ -93,15 +94,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 | ||||||
| 	} | 	} | ||||||
| @ -139,13 +147,21 @@ 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, startTime, endTime, 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 | ||||||
| @ -179,15 +195,15 @@ 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, *time.Time, *time.Time, 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), "", nil, nil, 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) | 		resp, code := service.getExtraActivation(req.NewActivationId, apiInfo) | ||||||
| 		return resp.ReservationId, resp.Id, resp.UsageWindowStart, resp.UsageWindowEnd, code | 		return resp.ReservationId, resp.Id, resp.UsageWindowStart, resp.UsageWindowEnd, code | ||||||
| 	default: | 	default: | ||||||
| 		return "", "", nil, nil, statuscode.SmsPlatformUnavailable | 		return "", "", nil, nil, statuscode.SmsPlatformUnavailable | ||||||
| @ -300,7 +316,7 @@ func (e *SmsPhone) DoGetNumber(balanceService *MemberBalance, req *dto.GetNumber | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	now := time.Now() | 	now := time.Now() | ||||||
| 	activationId, phone, code, expireTime, startTime, endTime := e.GetNumberManage(req.PlatformCode, req.Type, req.ServiceCode, price, req.Period) | 	activationId, phone, code, expireTime, startTime, endTime, 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 | ||||||
| @ -320,6 +336,7 @@ func (e *SmsPhone) DoGetNumber(balanceService *MemberBalance, req *dto.GetNumber | |||||||
| 	smsPhone.Actived = 1 | 	smsPhone.Actived = 1 | ||||||
| 	smsPhone.StartTime = startTime | 	smsPhone.StartTime = startTime | ||||||
| 	smsPhone.EndTime = endTime | 	smsPhone.EndTime = endTime | ||||||
|  | 	smsPhone.ApiKey = apiInfo.ApiKey | ||||||
|  |  | ||||||
| 	smsPhone.Price = price | 	smsPhone.Price = price | ||||||
| 	if req.Type == 1 { | 	if req.Type == 1 { | ||||||
| @ -361,7 +378,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) | ||||||
| @ -386,24 +403,32 @@ func (e *SmsPhone) DoGetNumber(balanceService *MemberBalance, req *dto.GetNumber | |||||||
| // *expirateTime 号码过期时间 | // *expirateTime 号码过期时间 | ||||||
| // *startTime 长效号码单次接码开始使用(textverified有用) | // *startTime 长效号码单次接码开始使用(textverified有用) | ||||||
| // *endTime 长效号码单次接码过期时间(textverified有用) | // *endTime 长效号码单次接码过期时间(textverified有用) | ||||||
| func (e *SmsPhone) GetNumberManage(platformCode string, typ int, serviceCode string, price decimal.Decimal, period int) (string, string, int, *time.Time, *time.Time, *time.Time) { | func (e *SmsPhone) GetNumberManage(platformCode string, typ int, serviceCode string, | ||||||
|  | 	price decimal.Decimal, period int) (string, string, int, *time.Time, *time.Time, *time.Time, *dto.SmsPlatformKeyQueueDto) { | ||||||
|  | 	smsPlatformKeyRedis := NewSmsPlatformKeyRedis(e.Orm, e.Log) | ||||||
|  | 	queue, err := smsPlatformKeyRedis.GetRoundRobinKey(platformCode) | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Log.Errorf("获取短信平台队列失败, %s", err) | ||||||
|  | 		return "", "", statuscode.ServerError, nil, nil, nil, 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) | ||||||
|  |  | ||||||
| 		return strconv.Itoa(activationId), phone, code, nil, nil, nil | 		return strconv.Itoa(activationId), phone, code, nil, nil, nil, queue | ||||||
| 	case global.SmsPlatformTextVerified: | 	case global.SmsPlatformTextVerified: | ||||||
| 		service := SmsTextVerified{Service: e.Service} | 		service := SmsTextVerified{Service: e.Service} | ||||||
| 		resp, code := service.GetNumberAndWakeUp(typ, serviceCode, price, period) | 		resp, code := service.GetNumberAndWakeUp(typ, serviceCode, price, period, queue) | ||||||
|  |  | ||||||
| 		if code != statuscode.Success { | 		if code != statuscode.Success { | ||||||
| 			return "", "", code, nil, resp.StartTime, resp.EndTime | 			return "", "", code, nil, resp.StartTime, resp.EndTime, queue | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		return resp.Id, resp.Phone, code, resp.EndAt, resp.StartTime, resp.EndTime | 		return resp.Id, resp.Phone, code, resp.EndAt, resp.StartTime, resp.EndTime, queue | ||||||
| 	default: | 	default: | ||||||
| 		return "", "", statuscode.SmsPlatformUnavailable, nil, nil, nil | 		return "", "", statuscode.SmsPlatformUnavailable, nil, nil, nil, queue | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -445,10 +470,29 @@ 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 { | ||||||
|  | 		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, | 		code, codeStatus := e.GetCodeManage(item.PlatformCode, item.NewActivationId, | ||||||
| 			item.Type, item.UserId, item.Service, item.ServiceCode) | 			item.Type, item.UserId, item.Service, item.ServiceCode, apiInfo) | ||||||
|  |  | ||||||
| 		if code == "" { | 		if code == "" { | ||||||
| 			var expireTime time.Time | 			var expireTime time.Time | ||||||
| @ -510,15 +554,15 @@ func (e *SmsPhone) SyncCodes() error { | |||||||
| } | } | ||||||
|  |  | ||||||
| // 获取验证码 | // 获取验证码 | ||||||
| func (e *SmsPhone) GetCodeManage(platformCode string, messageId string, typ int, userId int, smsService, serviceCode string) (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, userId, smsService, serviceCode) | 		return service.GetCode(messageId, typ, userId, smsService, serviceCode, apiInfo) | ||||||
| 	default: | 	default: | ||||||
| 		return "", statuscode.SmsPlatformUnavailable | 		return "", statuscode.SmsPlatformUnavailable | ||||||
| 	} | 	} | ||||||
| @ -685,7 +729,7 @@ func (e *SmsPhone) handleInsufficientBalance(phone models.SmsPhone) error { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// 调用平台取消续费接口 | 	// 调用平台取消续费接口 | ||||||
| 	code := e.ChangeAutoRenewManage(phone.PlatformCode, phone.ActivationId, false) | 	code := e.ChangeAutoRenewManage(phone.PlatformCode, phone.ApiKey, phone.ActivationId, false) | ||||||
| 	if code != statuscode.Success { | 	if code != statuscode.Success { | ||||||
| 		params["auto_renewal"] = 1 | 		params["auto_renewal"] = 1 | ||||||
| 		params["remark"] = "" | 		params["remark"] = "" | ||||||
| @ -778,7 +822,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 | ||||||
| @ -792,16 +836,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 | ||||||
| 	} | 	} | ||||||
|  | |||||||
							
								
								
									
										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 | ||||||
							
								
								
									
										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) | ||||||
|  | } | ||||||
| @ -49,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) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -13,9 +13,10 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| func initSetting() *gorm.DB { | func initSetting() *gorm.DB { | ||||||
| 	dsn := "root:123456@tcp(127.0.0.1:3306)/proxy-server-prod?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms" | 	dsn := "root:123456@tcp(127.0.0.1:3306)/proxy_server?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms" | ||||||
| 	db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{}) | 	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" | ||||||
|  | |||||||
| @ -32,7 +32,7 @@ type SmsTextVerified struct { | |||||||
| // 获取收到的验证码 | // 获取收到的验证码 | ||||||
| // messageId 短效或长效号码的ID | // messageId 短效或长效号码的ID | ||||||
| // typ 0-短效 1-长效 | // typ 0-短效 1-长效 | ||||||
| func (e SmsTextVerified) GetCode(messageId string, typ int, userId int, service, serviceCode string) (string, int) { | func (e SmsTextVerified) GetCode(messageId string, typ int, userId int, service, serviceCode string, apiInfo *dto.SmsPlatformKeyQueueDto) (string, int) { | ||||||
| 	reservationType := "" | 	reservationType := "" | ||||||
|  |  | ||||||
| 	if typ == 0 { | 	if typ == 0 { | ||||||
| @ -41,7 +41,7 @@ func (e SmsTextVerified) GetCode(messageId string, typ int, userId int, service, | |||||||
| 		reservationType = "renewable" | 		reservationType = "renewable" | ||||||
| 	} | 	} | ||||||
| 	url := fmt.Sprintf(getSmsCode, messageId, reservationType) | 	url := fmt.Sprintf(getSmsCode, messageId, reservationType) | ||||||
| 	client, code := e.GetTextVerifiedAuthClient() | 	client, code := e.GetTextVerifiedAuthClient(apiInfo) | ||||||
|  |  | ||||||
| 	if code != http.StatusOK { | 	if code != http.StatusOK { | ||||||
| 		e.Log.Errorf("获取授权请求失败,status %d", code) | 		e.Log.Errorf("获取授权请求失败,status %d", code) | ||||||
| @ -168,13 +168,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) | ||||||
|  |  | ||||||
| @ -192,7 +192,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) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @ -201,15 +202,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 | ||||||
| @ -225,9 +227,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 | ||||||
| 	} | 	} | ||||||
| @ -244,7 +246,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 | ||||||
| @ -321,17 +331,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 | ||||||
| 		} | 		} | ||||||
| @ -347,14 +365,14 @@ func (e *SmsTextVerified) SyncPrices() error { | |||||||
| } | } | ||||||
|  |  | ||||||
| // 获取号码并唤醒 | // 获取号码并唤醒 | ||||||
| func (e *SmsTextVerified) GetNumberAndWakeUp(tye int, serviceCode string, price decimal.Decimal, period int) (dto.SmsPhoneGetPhoneResp, int) { | 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) | 	resp, code := e.GetNumberForApi(tye, serviceCode, price, period, apiInfo) | ||||||
|  |  | ||||||
| 	if code != statuscode.Success { | 	if code != statuscode.Success { | ||||||
| 		return resp, code | 		return resp, code | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	wakeUpResp, code := e.getExtraActivation(resp.Id) | 	wakeUpResp, code := e.getExtraActivation(resp.Id, apiInfo) | ||||||
|  |  | ||||||
| 	if code != statuscode.Success { | 	if code != statuscode.Success { | ||||||
| 		return resp, code | 		return resp, code | ||||||
| @ -372,7 +390,7 @@ func (e *SmsTextVerified) GetNumberAndWakeUp(tye int, serviceCode string, price | |||||||
| // 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 | ||||||
| @ -380,10 +398,10 @@ func (e *SmsTextVerified) GetNumberForApi(typ int, serviceCode string, price dec | |||||||
| 	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) | ||||||
| @ -410,11 +428,11 @@ func (e *SmsTextVerified) GetNumberForApi(typ int, serviceCode string, price dec | |||||||
| 			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) | ||||||
| @ -441,7 +459,7 @@ func (e *SmsTextVerified) GetNumberForApi(typ int, serviceCode string, price dec | |||||||
| 					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 | ||||||
| @ -477,13 +495,13 @@ func (e *SmsTextVerified) GetNumberForApi(typ int, serviceCode string, price dec | |||||||
| } | } | ||||||
|  |  | ||||||
| // 执行查询请求 | // 执行查询请求 | ||||||
| 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) | ||||||
| @ -501,7 +519,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", | ||||||
| @ -513,7 +531,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) | ||||||
| @ -541,10 +559,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 | ||||||
| 	} | 	} | ||||||
| @ -561,8 +579,8 @@ func (e *SmsTextVerified) GetRentalDetail(id string) (dto.VerificationRentalDeta | |||||||
|  |  | ||||||
| // 唤醒号码 | // 唤醒号码 | ||||||
| // returns activationId,messageId,startTime(可用开始时间),endTime(可用结束时间), code, | // returns activationId,messageId,startTime(可用开始时间),endTime(可用结束时间), code, | ||||||
| func (e SmsTextVerified) getExtraActivation(id string) (dto.TextVerifiedWakeUpResp, int) { | func (e SmsTextVerified) getExtraActivation(id string, apiInfo *dto.SmsPlatformKeyQueueDto) (dto.TextVerifiedWakeUpResp, int) { | ||||||
| 	client, code := e.GetTextVerifiedAuthClient() | 	client, code := e.GetTextVerifiedAuthClient(apiInfo) | ||||||
| 	result := dto.TextVerifiedWakeUpResp{} | 	result := dto.TextVerifiedWakeUpResp{} | ||||||
|  |  | ||||||
| 	if code != statuscode.Success { | 	if code != statuscode.Success { | ||||||
| @ -584,7 +602,7 @@ func (e SmsTextVerified) getExtraActivation(id string) (dto.TextVerifiedWakeUpRe | |||||||
| 		return result, 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) | ||||||
| @ -615,19 +633,19 @@ func (e SmsTextVerified) getExtraActivation(id string) (dto.TextVerifiedWakeUpRe | |||||||
| // 获取价格 | // 获取价格 | ||||||
| // 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", | ||||||
| @ -636,7 +654,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) | ||||||
| @ -659,7 +677,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, | ||||||
| @ -668,7 +686,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) | ||||||
| @ -690,14 +708,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 | ||||||
| @ -722,8 +740,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 | ||||||
| @ -758,8 +776,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) | ||||||
| @ -882,10 +900,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) | ||||||
| @ -916,18 +934,13 @@ func (e *SmsTextVerified) Renew(activationId string, status bool) int { | |||||||
|  |  | ||||||
| // 重新初始化短信记录 | // 重新初始化短信记录 | ||||||
| func (e *SmsTextVerified) InitSmsLogs() error { | func (e *SmsTextVerified) InitSmsLogs() error { | ||||||
| 	// phones, err := e.GetNumbers() | 	// 获取平台密钥信息 | ||||||
|  | 	smsPlatformKeyRedis := NewSmsPlatformKeyRedis(e.Orm, e.Log) | ||||||
| 	// if err != nil { | 	apiInfo, err := smsPlatformKeyRedis.GetApiInfo("textverified", "") | ||||||
| 	// 	e.Log.Errorf("获取长效号码列表失败 %v", err) | 	if err != nil { | ||||||
| 	// 	return fmt.Errorf("获取长效号码列表失败 %v", err) | 		e.Log.Errorf("获取平台密钥失败: %v", err) | ||||||
| 	// } | 		return fmt.Errorf("获取平台密钥失败: %v", err) | ||||||
|  | 	} | ||||||
| 	// numbers := make([]string, 0) |  | ||||||
|  |  | ||||||
| 	// for _, v := range phones { |  | ||||||
| 	// 	numbers = append(numbers, v.Number) |  | ||||||
| 	// } |  | ||||||
|  |  | ||||||
| 	phoneLogs, err := e.GetUserByNumber("textverified") | 	phoneLogs, err := e.GetUserByNumber("textverified") | ||||||
|  |  | ||||||
| @ -938,7 +951,7 @@ func (e *SmsTextVerified) InitSmsLogs() error { | |||||||
|  |  | ||||||
| 	if len(phoneLogs) > 0 { | 	if len(phoneLogs) > 0 { | ||||||
| 		for _, v := range phoneLogs { | 		for _, v := range phoneLogs { | ||||||
| 			e.GetCode(v.ActivationId, v.Type, v.UserId, v.Service, v.ServiceCode) | 			e.GetCode(v.ActivationId, v.Type, v.UserId, v.Service, v.ServiceCode, &apiInfo) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @ -956,9 +969,48 @@ func (e *SmsTextVerified) GetUserByNumber(platformCode string) ([]models.SmsPhon | |||||||
| 	return result, nil | 	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() ([]dto.VerificationDTO, error) { | func (e *SmsTextVerified) GetNumbers(apiInfo *dto.SmsPlatformKeyQueueDto) ([]dto.VerificationDTO, error) { | ||||||
| 	client, code := e.GetTextVerifiedAuthClient() | 	client, code := e.GetTextVerifiedAuthClient(apiInfo) | ||||||
| 	result := make([]dto.VerificationDTO, 0) | 	result := make([]dto.VerificationDTO, 0) | ||||||
|  |  | ||||||
| 	if code != statuscode.Success { | 	if code != statuscode.Success { | ||||||
| @ -967,7 +1019,7 @@ func (e *SmsTextVerified) GetNumbers() ([]dto.VerificationDTO, error) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	resp := dto.TextVerifiedGetNumbersResp{} | 	resp := dto.TextVerifiedGetNumbersResp{} | ||||||
| 	statusCode, err := client.Get(getNumbers, map[string]string{}, resp) | 	statusCode, err := client.Get(getNumbers, map[string]string{}, &resp) | ||||||
|  |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		e.Log.Errorf("获取长效号码列表失败 %v", err) | 		e.Log.Errorf("获取长效号码列表失败 %v", err) | ||||||
| @ -976,11 +1028,15 @@ func (e *SmsTextVerified) GetNumbers() ([]dto.VerificationDTO, error) { | |||||||
|  |  | ||||||
| 	if statusCode == http.StatusOK { | 	if statusCode == http.StatusOK { | ||||||
| 		// 添加第一页数据 | 		// 添加第一页数据 | ||||||
| 		result = append(result, resp.Data...) | 		for _, item := range resp.Data { | ||||||
|  | 			item.Number = "1" + item.Number | ||||||
|  |  | ||||||
|  | 			result = append(result, item) | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		// 处理分页数据 | 		// 处理分页数据 | ||||||
| 		if resp.HasNext { | 		if resp.HasNext { | ||||||
| 			paginatedData, err := e.fetchPaginatedData(resp.Links.Next) | 			paginatedData, err := e.fetchPaginatedData(resp.Links.Next, apiInfo) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return result, err | 				return result, err | ||||||
| 			} | 			} | ||||||
| @ -989,7 +1045,7 @@ func (e *SmsTextVerified) GetNumbers() ([]dto.VerificationDTO, error) { | |||||||
|  |  | ||||||
| 		return result, nil | 		return result, nil | ||||||
| 	} else if statusCode == http.StatusUnauthorized { | 	} else if statusCode == http.StatusUnauthorized { | ||||||
| 		return e.GetNumbers() | 		return e.GetNumbers(apiInfo) | ||||||
| 	} else { | 	} else { | ||||||
| 		e.Log.Errorf("获取长效号码列表失败 %d", statusCode) | 		e.Log.Errorf("获取长效号码列表失败 %d", statusCode) | ||||||
| 		return result, fmt.Errorf("获取长效号码列表失败 %d", statusCode) | 		return result, fmt.Errorf("获取长效号码列表失败 %d", statusCode) | ||||||
| @ -999,11 +1055,11 @@ func (e *SmsTextVerified) GetNumbers() ([]dto.VerificationDTO, error) { | |||||||
| // 获取授权header头 | // 获取授权header头 | ||||||
| // fetchPaginatedData 获取分页数据的通用方法 | // fetchPaginatedData 获取分页数据的通用方法 | ||||||
| // 处理TextVerified API的分页响应,自动获取所有页面的数据 | // 处理TextVerified API的分页响应,自动获取所有页面的数据 | ||||||
| func (e *SmsTextVerified) fetchPaginatedData(nextLink *dto.TextVerifiedResp) ([]dto.VerificationDTO, error) { | func (e *SmsTextVerified) fetchPaginatedData(nextLink *dto.TextVerifiedResp, apiInfo *dto.SmsPlatformKeyQueueDto) ([]dto.VerificationDTO, error) { | ||||||
| 	var result []dto.VerificationDTO | 	var result []dto.VerificationDTO | ||||||
|  |  | ||||||
| 	for nextLink != nil { | 	for nextLink != nil { | ||||||
| 		nextData, err := e.doRequest(nextLink) | 		nextData, err := e.doRequest(nextLink, apiInfo) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			e.Log.Errorf("获取分页数据失败: %v", err) | 			e.Log.Errorf("获取分页数据失败: %v", err) | ||||||
| 			return result, fmt.Errorf("获取分页数据失败: %v", err) | 			return result, fmt.Errorf("获取分页数据失败: %v", err) | ||||||
| @ -1016,7 +1072,10 @@ func (e *SmsTextVerified) fetchPaginatedData(nextLink *dto.TextVerifiedResp) ([] | |||||||
| 			return result, fmt.Errorf("解析分页数据失败: %v", err) | 			return result, fmt.Errorf("解析分页数据失败: %v", err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		result = append(result, nextResp.Data...) | 		for _, item := range nextResp.Data { | ||||||
|  | 			item.Number = "1" + item.Number | ||||||
|  | 			result = append(result, item) | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		// 检查是否还有下一页 | 		// 检查是否还有下一页 | ||||||
| 		if nextResp.HasNext { | 		if nextResp.HasNext { | ||||||
| @ -1029,8 +1088,8 @@ func (e *SmsTextVerified) fetchPaginatedData(nextLink *dto.TextVerifiedResp) ([] | |||||||
| 	return result, nil | 	return result, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (e *SmsTextVerified) GetAuthHeader() (map[string]string, error) { | func (e *SmsTextVerified) GetAuthHeader(apiInfo *dto.SmsPlatformKeyQueueDto) (map[string]string, error) { | ||||||
| 	token, err := e.GetToken() | 	token, err := e.GetToken(apiInfo) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  | |||||||
							
								
								
									
										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 { | ||||||
|  | |||||||
| @ -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" | ||||||
|  | ) | ||||||
							
								
								
									
										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