Compare commits
	
		
			9 Commits
		
	
	
		
			f73639a96c
			...
			master
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 837900462a | |||
| 1a5cffa2cc | |||
| 73c6cf9844 | |||
| 996cd27fcb | |||
| 9c826f4966 | |||
| cbefd85f25 | |||
| baa6552994 | |||
| 0f084e1461 | |||
| ab61808bf1 | 
							
								
								
									
										314
									
								
								app/admin/apis/sms_abnormal_number.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										314
									
								
								app/admin/apis/sms_abnormal_number.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,314 @@
 | 
				
			|||||||
 | 
					package apis
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/gin-gonic/gin"
 | 
				
			||||||
 | 
						"github.com/go-admin-team/go-admin-core/sdk/api"
 | 
				
			||||||
 | 
						"github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth/user"
 | 
				
			||||||
 | 
						_ "github.com/go-admin-team/go-admin-core/sdk/pkg/response"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"go-admin/app/admin/models"
 | 
				
			||||||
 | 
						"go-admin/app/admin/service"
 | 
				
			||||||
 | 
						"go-admin/app/admin/service/dto"
 | 
				
			||||||
 | 
						"go-admin/common/actions"
 | 
				
			||||||
 | 
						"go-admin/common/middleware"
 | 
				
			||||||
 | 
						"go-admin/common/statuscode"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SmsAbnormalNumber struct {
 | 
				
			||||||
 | 
						api.Api
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetPage 获取异常号码统计列表
 | 
				
			||||||
 | 
					// @Summary 获取异常号码统计列表
 | 
				
			||||||
 | 
					// @Description 获取异常号码统计列表
 | 
				
			||||||
 | 
					// @Tags 异常号码统计
 | 
				
			||||||
 | 
					// @Param platformCode query string false "平台code"
 | 
				
			||||||
 | 
					// @Param phone query string false "电话号码"
 | 
				
			||||||
 | 
					// @Param pageSize query int false "页条数"
 | 
				
			||||||
 | 
					// @Param pageIndex query int false "页码"
 | 
				
			||||||
 | 
					// @Success 200 {object} response.Response{data=response.Page{list=[]models.SmsAbnormalNumber}} "{"code": 200, "data": [...]}"
 | 
				
			||||||
 | 
					// @Router /api/v1/sms-abnormal-number [get]
 | 
				
			||||||
 | 
					// @Security Bearer
 | 
				
			||||||
 | 
					func (e SmsAbnormalNumber) GetPage(c *gin.Context) {
 | 
				
			||||||
 | 
						req := dto.SmsAbnormalNumberGetPageReq{}
 | 
				
			||||||
 | 
						s := service.SmsAbnormalNumber{}
 | 
				
			||||||
 | 
						err := e.MakeContext(c).
 | 
				
			||||||
 | 
							MakeOrm().
 | 
				
			||||||
 | 
							Bind(&req).
 | 
				
			||||||
 | 
							MakeService(&s.Service).
 | 
				
			||||||
 | 
							Errors
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Logger.Error(err)
 | 
				
			||||||
 | 
							e.Error(500, err, err.Error())
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p := actions.GetPermissionFromContext(c)
 | 
				
			||||||
 | 
						list := make([]models.SmsAbnormalNumber, 0)
 | 
				
			||||||
 | 
						var count int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = s.GetPage(&req, p, &list, &count)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Error(500, err, fmt.Sprintf("获取异常号码统计失败,\r\n失败信息 %s", err.Error()))
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Get 获取异常号码统计
 | 
				
			||||||
 | 
					// @Summary 获取异常号码统计
 | 
				
			||||||
 | 
					// @Description 获取异常号码统计
 | 
				
			||||||
 | 
					// @Tags 异常号码统计
 | 
				
			||||||
 | 
					// @Param id path int false "id"
 | 
				
			||||||
 | 
					// @Success 200 {object} response.Response{data=models.SmsAbnormalNumber} "{"code": 200, "data": [...]}"
 | 
				
			||||||
 | 
					// @Router /api/v1/sms-abnormal-number/{id} [get]
 | 
				
			||||||
 | 
					// @Security Bearer
 | 
				
			||||||
 | 
					func (e SmsAbnormalNumber) Get(c *gin.Context) {
 | 
				
			||||||
 | 
						req := dto.SmsAbnormalNumberGetReq{}
 | 
				
			||||||
 | 
						s := service.SmsAbnormalNumber{}
 | 
				
			||||||
 | 
						err := e.MakeContext(c).
 | 
				
			||||||
 | 
							MakeOrm().
 | 
				
			||||||
 | 
							Bind(&req).
 | 
				
			||||||
 | 
							MakeService(&s.Service).
 | 
				
			||||||
 | 
							Errors
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Logger.Error(err)
 | 
				
			||||||
 | 
							e.Error(500, err, err.Error())
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var object models.SmsAbnormalNumber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p := actions.GetPermissionFromContext(c)
 | 
				
			||||||
 | 
						err = s.Get(&req, p, &object)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Error(500, err, fmt.Sprintf("获取异常号码统计失败,\r\n失败信息 %s", err.Error()))
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						e.OK(object, "查询成功")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Insert 创建异常号码统计
 | 
				
			||||||
 | 
					// @Summary 创建异常号码统计
 | 
				
			||||||
 | 
					// @Description 创建异常号码统计
 | 
				
			||||||
 | 
					// @Tags 异常号码统计
 | 
				
			||||||
 | 
					// @Accept application/json
 | 
				
			||||||
 | 
					// @Product application/json
 | 
				
			||||||
 | 
					// @Param data body dto.SmsAbnormalNumberInsertReq true "data"
 | 
				
			||||||
 | 
					// @Success 200 {object} response.Response	"{"code": 200, "message": "添加成功"}"
 | 
				
			||||||
 | 
					// @Router /api/v1/sms-abnormal-number [post]
 | 
				
			||||||
 | 
					// @Security Bearer
 | 
				
			||||||
 | 
					func (e SmsAbnormalNumber) Insert(c *gin.Context) {
 | 
				
			||||||
 | 
						req := dto.SmsAbnormalNumberInsertReq{}
 | 
				
			||||||
 | 
						s := service.SmsAbnormalNumber{}
 | 
				
			||||||
 | 
						err := e.MakeContext(c).
 | 
				
			||||||
 | 
							MakeOrm().
 | 
				
			||||||
 | 
							Bind(&req).
 | 
				
			||||||
 | 
							MakeService(&s.Service).
 | 
				
			||||||
 | 
							Errors
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Logger.Error(err)
 | 
				
			||||||
 | 
							e.Error(500, err, err.Error())
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// 设置创建人
 | 
				
			||||||
 | 
						req.SetCreateBy(user.GetUserId(c))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = s.Insert(&req)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Error(500, err, fmt.Sprintf("创建异常号码统计失败,\r\n失败信息 %s", err.Error()))
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						e.OK(req.GetId(), "创建成功")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Update 修改异常号码统计
 | 
				
			||||||
 | 
					// @Summary 修改异常号码统计
 | 
				
			||||||
 | 
					// @Description 修改异常号码统计
 | 
				
			||||||
 | 
					// @Tags 异常号码统计
 | 
				
			||||||
 | 
					// @Accept application/json
 | 
				
			||||||
 | 
					// @Product application/json
 | 
				
			||||||
 | 
					// @Param id path int true "id"
 | 
				
			||||||
 | 
					// @Param data body dto.SmsAbnormalNumberUpdateReq true "body"
 | 
				
			||||||
 | 
					// @Success 200 {object} response.Response	"{"code": 200, "message": "修改成功"}"
 | 
				
			||||||
 | 
					// @Router /api/v1/sms-abnormal-number/{id} [put]
 | 
				
			||||||
 | 
					// @Security Bearer
 | 
				
			||||||
 | 
					func (e SmsAbnormalNumber) Update(c *gin.Context) {
 | 
				
			||||||
 | 
						req := dto.SmsAbnormalNumberUpdateReq{}
 | 
				
			||||||
 | 
						s := service.SmsAbnormalNumber{}
 | 
				
			||||||
 | 
						err := e.MakeContext(c).
 | 
				
			||||||
 | 
							MakeOrm().
 | 
				
			||||||
 | 
							Bind(&req).
 | 
				
			||||||
 | 
							MakeService(&s.Service).
 | 
				
			||||||
 | 
							Errors
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Logger.Error(err)
 | 
				
			||||||
 | 
							e.Error(500, err, err.Error())
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						req.SetUpdateBy(user.GetUserId(c))
 | 
				
			||||||
 | 
						p := actions.GetPermissionFromContext(c)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = s.Update(&req, p)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Error(500, err, fmt.Sprintf("修改异常号码统计失败,\r\n失败信息 %s", err.Error()))
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						e.OK(req.GetId(), "修改成功")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Delete 删除异常号码统计
 | 
				
			||||||
 | 
					// @Summary 删除异常号码统计
 | 
				
			||||||
 | 
					// @Description 删除异常号码统计
 | 
				
			||||||
 | 
					// @Tags 异常号码统计
 | 
				
			||||||
 | 
					// @Param data body dto.SmsAbnormalNumberDeleteReq true "body"
 | 
				
			||||||
 | 
					// @Success 200 {object} response.Response	"{"code": 200, "message": "删除成功"}"
 | 
				
			||||||
 | 
					// @Router /api/v1/sms-abnormal-number [delete]
 | 
				
			||||||
 | 
					// @Security Bearer
 | 
				
			||||||
 | 
					func (e SmsAbnormalNumber) Delete(c *gin.Context) {
 | 
				
			||||||
 | 
						s := service.SmsAbnormalNumber{}
 | 
				
			||||||
 | 
						req := dto.SmsAbnormalNumberDeleteReq{}
 | 
				
			||||||
 | 
						err := e.MakeContext(c).
 | 
				
			||||||
 | 
							MakeOrm().
 | 
				
			||||||
 | 
							Bind(&req).
 | 
				
			||||||
 | 
							MakeService(&s.Service).
 | 
				
			||||||
 | 
							Errors
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Logger.Error(err)
 | 
				
			||||||
 | 
							e.Error(500, err, err.Error())
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// req.SetUpdateBy(user.GetUserId(c))
 | 
				
			||||||
 | 
						p := actions.GetPermissionFromContext(c)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = s.Remove(&req, p)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Error(500, err, fmt.Sprintf("删除异常号码统计失败,\r\n失败信息 %s", err.Error()))
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						e.OK(req.GetId(), "删除成功")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 同步平台和系统的差异
 | 
				
			||||||
 | 
					func (e SmsAbnormalNumber) SyncState(c *gin.Context) {
 | 
				
			||||||
 | 
						s := service.SmsAbnormalNumber{}
 | 
				
			||||||
 | 
						err := e.MakeContext(c).
 | 
				
			||||||
 | 
							MakeOrm().
 | 
				
			||||||
 | 
							MakeService(&s.Service).
 | 
				
			||||||
 | 
							Errors
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Logger.Error(err)
 | 
				
			||||||
 | 
							e.Error(500, err, err.Error())
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = s.SyncState()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Error(500, err, fmt.Sprintf("同步异常号码统计失败,\r\n失败信息 %s", err.Error()))
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						e.OK(nil, "同步成功")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 重用号码
 | 
				
			||||||
 | 
					func (e SmsAbnormalNumber) ReUseAbnormalNumber(c *gin.Context) {
 | 
				
			||||||
 | 
						req := dto.ReUseAbnormalNumberReq{}
 | 
				
			||||||
 | 
						s := service.SmsAbnormalNumber{}
 | 
				
			||||||
 | 
						err := e.MakeContext(c).
 | 
				
			||||||
 | 
							MakeOrm().
 | 
				
			||||||
 | 
							Bind(&req).
 | 
				
			||||||
 | 
							MakeService(&s.Service).
 | 
				
			||||||
 | 
							Errors
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Logger.Error(err)
 | 
				
			||||||
 | 
							e.Error(500, err, err.Error())
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						lang := "en"
 | 
				
			||||||
 | 
						userId, err := middleware.GetUserIdByApiKey(c)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Logger.Error(err)
 | 
				
			||||||
 | 
							e.Error(statuscode.Unauthorized, nil, statuscode.GetMsg(statuscode.Unauthorized, lang))
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp, code := s.ReUseAbnormalNumber(userId, &req)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if code != statuscode.Success {
 | 
				
			||||||
 | 
							e.Error(code, nil, statuscode.GetMsg(code, lang))
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						e.OK(resp, "success")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 清空数据
 | 
				
			||||||
 | 
					func (e SmsAbnormalNumber) Clean(c *gin.Context) {
 | 
				
			||||||
 | 
						s := service.SmsAbnormalNumber{}
 | 
				
			||||||
 | 
						err := e.MakeContext(c).
 | 
				
			||||||
 | 
							MakeOrm().
 | 
				
			||||||
 | 
							MakeService(&s.Service).
 | 
				
			||||||
 | 
							Errors
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Logger.Error(err)
 | 
				
			||||||
 | 
							e.Error(500, err, err.Error())
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						lang := "en"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Logger.Error(err)
 | 
				
			||||||
 | 
							e.Error(statuscode.Unauthorized, nil, statuscode.GetMsg(statuscode.Unauthorized, lang))
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = s.Clean()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Error(500, err, fmt.Sprintf("清除数据失败\r\n失败信息 %s", err.Error()))
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						e.OK(nil, "success")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 批量取消
 | 
				
			||||||
 | 
					func (e SmsAbnormalNumber) BatchCancel(c *gin.Context) {
 | 
				
			||||||
 | 
						req := dto.BatchCancelAbnormalNumberReq{}
 | 
				
			||||||
 | 
						s := service.SmsAbnormalNumber{}
 | 
				
			||||||
 | 
						err := e.MakeContext(c).
 | 
				
			||||||
 | 
							MakeOrm().
 | 
				
			||||||
 | 
							Bind(&req).
 | 
				
			||||||
 | 
							MakeService(&s.Service).
 | 
				
			||||||
 | 
							Errors
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Logger.Error(err)
 | 
				
			||||||
 | 
							e.Error(500, err, err.Error())
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						lang := "en"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Logger.Error(err)
 | 
				
			||||||
 | 
							e.Error(statuscode.Unauthorized, nil, statuscode.GetMsg(statuscode.Unauthorized, lang))
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = s.BatchCancel(&req)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Error(500, err, fmt.Sprintf("批量取消异常号码统计失败,\r\n失败信息 %s", err.Error()))
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						e.OK(nil, "success")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -2,6 +2,7 @@ package apis
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/gin-gonic/gin"
 | 
						"github.com/gin-gonic/gin"
 | 
				
			||||||
	"github.com/go-admin-team/go-admin-core/sdk/api"
 | 
						"github.com/go-admin-team/go-admin-core/sdk/api"
 | 
				
			||||||
@ -747,3 +748,35 @@ func (e SmsPhone) OpenWeakUp(c *gin.Context) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	e.OK(resp, "success")
 | 
						e.OK(resp, "success")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 手动续费
 | 
				
			||||||
 | 
					func (e SmsPhone) ManualRenewal(c *gin.Context) {
 | 
				
			||||||
 | 
						req := dto.ManualRenewalReq{}
 | 
				
			||||||
 | 
						s := service.SmsRenewalLog{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := e.MakeContext(c).
 | 
				
			||||||
 | 
							MakeOrm().
 | 
				
			||||||
 | 
							Bind(&req).
 | 
				
			||||||
 | 
							MakeService(&s.Service).
 | 
				
			||||||
 | 
							Errors
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Logger.Error(err)
 | 
				
			||||||
 | 
							e.Error(500, err, err.Error())
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						errorIds, err := s.ManualRenewal(req.ActivationIds)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Logger.Error(err)
 | 
				
			||||||
 | 
							e.Error(500, err, err.Error())
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var data string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(errorIds) > 0 {
 | 
				
			||||||
 | 
							data = strings.Join(errorIds, ",")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						e.OK(data, "success")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										192
									
								
								app/admin/apis/sms_platform_key.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								app/admin/apis/sms_platform_key.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,192 @@
 | 
				
			|||||||
 | 
					package apis
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/gin-gonic/gin"
 | 
				
			||||||
 | 
						"github.com/go-admin-team/go-admin-core/sdk/api"
 | 
				
			||||||
 | 
						"github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth/user"
 | 
				
			||||||
 | 
						_ "github.com/go-admin-team/go-admin-core/sdk/pkg/response"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"go-admin/app/admin/models"
 | 
				
			||||||
 | 
						"go-admin/app/admin/service"
 | 
				
			||||||
 | 
						"go-admin/app/admin/service/dto"
 | 
				
			||||||
 | 
						"go-admin/common/actions"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SmsPlatformKey struct {
 | 
				
			||||||
 | 
						api.Api
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetPage 获取平台密钥管理列表
 | 
				
			||||||
 | 
					// @Summary 获取平台密钥管理列表
 | 
				
			||||||
 | 
					// @Description 获取平台密钥管理列表
 | 
				
			||||||
 | 
					// @Tags 平台密钥管理
 | 
				
			||||||
 | 
					// @Param platformCode query string false "平台code"
 | 
				
			||||||
 | 
					// @Param pageSize query int false "页条数"
 | 
				
			||||||
 | 
					// @Param pageIndex query int false "页码"
 | 
				
			||||||
 | 
					// @Success 200 {object} response.Response{data=response.Page{list=[]models.SmsPlatformKey}} "{"code": 200, "data": [...]}"
 | 
				
			||||||
 | 
					// @Router /api/v1/sms-platform-key [get]
 | 
				
			||||||
 | 
					// @Security Bearer
 | 
				
			||||||
 | 
					func (e SmsPlatformKey) GetPage(c *gin.Context) {
 | 
				
			||||||
 | 
						req := dto.SmsPlatformKeyGetPageReq{}
 | 
				
			||||||
 | 
						s := service.SmsPlatformKey{}
 | 
				
			||||||
 | 
						err := e.MakeContext(c).
 | 
				
			||||||
 | 
							MakeOrm().
 | 
				
			||||||
 | 
							Bind(&req).
 | 
				
			||||||
 | 
							MakeService(&s.Service).
 | 
				
			||||||
 | 
							Errors
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Logger.Error(err)
 | 
				
			||||||
 | 
							e.Error(500, err, err.Error())
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p := actions.GetPermissionFromContext(c)
 | 
				
			||||||
 | 
						list := make([]models.SmsPlatformKey, 0)
 | 
				
			||||||
 | 
						var count int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = s.GetPage(&req, p, &list, &count)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Error(500, err, fmt.Sprintf("获取平台密钥管理失败,\r\n失败信息 %s", err.Error()))
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Get 获取平台密钥管理
 | 
				
			||||||
 | 
					// @Summary 获取平台密钥管理
 | 
				
			||||||
 | 
					// @Description 获取平台密钥管理
 | 
				
			||||||
 | 
					// @Tags 平台密钥管理
 | 
				
			||||||
 | 
					// @Param id path int false "id"
 | 
				
			||||||
 | 
					// @Success 200 {object} response.Response{data=models.SmsPlatformKey} "{"code": 200, "data": [...]}"
 | 
				
			||||||
 | 
					// @Router /api/v1/sms-platform-key/{id} [get]
 | 
				
			||||||
 | 
					// @Security Bearer
 | 
				
			||||||
 | 
					func (e SmsPlatformKey) Get(c *gin.Context) {
 | 
				
			||||||
 | 
						req := dto.SmsPlatformKeyGetReq{}
 | 
				
			||||||
 | 
						s := service.SmsPlatformKey{}
 | 
				
			||||||
 | 
						err := e.MakeContext(c).
 | 
				
			||||||
 | 
							MakeOrm().
 | 
				
			||||||
 | 
							Bind(&req).
 | 
				
			||||||
 | 
							MakeService(&s.Service).
 | 
				
			||||||
 | 
							Errors
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Logger.Error(err)
 | 
				
			||||||
 | 
							e.Error(500, err, err.Error())
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var object models.SmsPlatformKey
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p := actions.GetPermissionFromContext(c)
 | 
				
			||||||
 | 
						err = s.Get(&req, p, &object)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Error(500, err, fmt.Sprintf("获取平台密钥管理失败,\r\n失败信息 %s", err.Error()))
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						e.OK(object, "查询成功")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Insert 创建平台密钥管理
 | 
				
			||||||
 | 
					// @Summary 创建平台密钥管理
 | 
				
			||||||
 | 
					// @Description 创建平台密钥管理
 | 
				
			||||||
 | 
					// @Tags 平台密钥管理
 | 
				
			||||||
 | 
					// @Accept application/json
 | 
				
			||||||
 | 
					// @Product application/json
 | 
				
			||||||
 | 
					// @Param data body dto.SmsPlatformKeyInsertReq true "data"
 | 
				
			||||||
 | 
					// @Success 200 {object} response.Response	"{"code": 200, "message": "添加成功"}"
 | 
				
			||||||
 | 
					// @Router /api/v1/sms-platform-key [post]
 | 
				
			||||||
 | 
					// @Security Bearer
 | 
				
			||||||
 | 
					func (e SmsPlatformKey) Insert(c *gin.Context) {
 | 
				
			||||||
 | 
						req := dto.SmsPlatformKeyInsertReq{}
 | 
				
			||||||
 | 
						s := service.SmsPlatformKey{}
 | 
				
			||||||
 | 
						err := e.MakeContext(c).
 | 
				
			||||||
 | 
							MakeOrm().
 | 
				
			||||||
 | 
							Bind(&req).
 | 
				
			||||||
 | 
							MakeService(&s.Service).
 | 
				
			||||||
 | 
							Errors
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Logger.Error(err)
 | 
				
			||||||
 | 
							e.Error(500, err, err.Error())
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// 设置创建人
 | 
				
			||||||
 | 
						req.SetCreateBy(user.GetUserId(c))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = s.Insert(&req)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Error(500, err, fmt.Sprintf("创建平台密钥管理失败,\r\n失败信息 %s", err.Error()))
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						e.OK(req.GetId(), "创建成功")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Update 修改平台密钥管理
 | 
				
			||||||
 | 
					// @Summary 修改平台密钥管理
 | 
				
			||||||
 | 
					// @Description 修改平台密钥管理
 | 
				
			||||||
 | 
					// @Tags 平台密钥管理
 | 
				
			||||||
 | 
					// @Accept application/json
 | 
				
			||||||
 | 
					// @Product application/json
 | 
				
			||||||
 | 
					// @Param id path int true "id"
 | 
				
			||||||
 | 
					// @Param data body dto.SmsPlatformKeyUpdateReq true "body"
 | 
				
			||||||
 | 
					// @Success 200 {object} response.Response	"{"code": 200, "message": "修改成功"}"
 | 
				
			||||||
 | 
					// @Router /api/v1/sms-platform-key/{id} [put]
 | 
				
			||||||
 | 
					// @Security Bearer
 | 
				
			||||||
 | 
					func (e SmsPlatformKey) Update(c *gin.Context) {
 | 
				
			||||||
 | 
						req := dto.SmsPlatformKeyUpdateReq{}
 | 
				
			||||||
 | 
						s := service.SmsPlatformKey{}
 | 
				
			||||||
 | 
						err := e.MakeContext(c).
 | 
				
			||||||
 | 
							MakeOrm().
 | 
				
			||||||
 | 
							Bind(&req).
 | 
				
			||||||
 | 
							MakeService(&s.Service).
 | 
				
			||||||
 | 
							Errors
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Logger.Error(err)
 | 
				
			||||||
 | 
							e.Error(500, err, err.Error())
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						req.SetUpdateBy(user.GetUserId(c))
 | 
				
			||||||
 | 
						p := actions.GetPermissionFromContext(c)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = s.Update(&req, p)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Error(500, err, fmt.Sprintf("修改平台密钥管理失败,\r\n失败信息 %s", err.Error()))
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						e.OK(req.GetId(), "修改成功")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Delete 删除平台密钥管理
 | 
				
			||||||
 | 
					// @Summary 删除平台密钥管理
 | 
				
			||||||
 | 
					// @Description 删除平台密钥管理
 | 
				
			||||||
 | 
					// @Tags 平台密钥管理
 | 
				
			||||||
 | 
					// @Param data body dto.SmsPlatformKeyDeleteReq true "body"
 | 
				
			||||||
 | 
					// @Success 200 {object} response.Response	"{"code": 200, "message": "删除成功"}"
 | 
				
			||||||
 | 
					// @Router /api/v1/sms-platform-key [delete]
 | 
				
			||||||
 | 
					// @Security Bearer
 | 
				
			||||||
 | 
					func (e SmsPlatformKey) Delete(c *gin.Context) {
 | 
				
			||||||
 | 
						s := service.SmsPlatformKey{}
 | 
				
			||||||
 | 
						req := dto.SmsPlatformKeyDeleteReq{}
 | 
				
			||||||
 | 
						err := e.MakeContext(c).
 | 
				
			||||||
 | 
							MakeOrm().
 | 
				
			||||||
 | 
							Bind(&req).
 | 
				
			||||||
 | 
							MakeService(&s.Service).
 | 
				
			||||||
 | 
							Errors
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Logger.Error(err)
 | 
				
			||||||
 | 
							e.Error(500, err, err.Error())
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// req.SetUpdateBy(user.GetUserId(c))
 | 
				
			||||||
 | 
						p := actions.GetPermissionFromContext(c)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = s.Remove(&req, p)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Error(500, err, fmt.Sprintf("删除平台密钥管理失败,\r\n失败信息 %s", err.Error()))
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						e.OK(req.GetId(), "删除成功")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -12,6 +12,8 @@ import (
 | 
				
			|||||||
	"go-admin/app/admin/service"
 | 
						"go-admin/app/admin/service"
 | 
				
			||||||
	"go-admin/app/admin/service/dto"
 | 
						"go-admin/app/admin/service/dto"
 | 
				
			||||||
	"go-admin/common/actions"
 | 
						"go-admin/common/actions"
 | 
				
			||||||
 | 
						"go-admin/common/middleware"
 | 
				
			||||||
 | 
						"go-admin/common/statuscode"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type SmsRenewalLog struct {
 | 
					type SmsRenewalLog struct {
 | 
				
			||||||
@ -43,7 +45,7 @@ func (e SmsRenewalLog) GetPage(c *gin.Context) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	p := actions.GetPermissionFromContext(c)
 | 
						p := actions.GetPermissionFromContext(c)
 | 
				
			||||||
	list := make([]models.SmsRenewalLog, 0)
 | 
						list := make([]dto.SmsRenewalLogResp, 0)
 | 
				
			||||||
	var count int64
 | 
						var count int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = s.GetPage(&req, p, &list, &count)
 | 
						err = s.GetPage(&req, p, &list, &count)
 | 
				
			||||||
@ -190,3 +192,70 @@ func (e SmsRenewalLog) Delete(c *gin.Context) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	e.OK(req.GetId(), "删除成功")
 | 
						e.OK(req.GetId(), "删除成功")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 手动扣费
 | 
				
			||||||
 | 
					func (e SmsRenewalLog) ManualDeduct(c *gin.Context) {
 | 
				
			||||||
 | 
						req := dto.ManualDeductReq{}
 | 
				
			||||||
 | 
						s := service.SmsRenewalLog{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := e.MakeContext(c).
 | 
				
			||||||
 | 
							MakeOrm().
 | 
				
			||||||
 | 
							Bind(&req).
 | 
				
			||||||
 | 
							MakeService(&s.Service).
 | 
				
			||||||
 | 
							Errors
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Logger.Error(err)
 | 
				
			||||||
 | 
							e.Error(500, err, err.Error())
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						userId, err := middleware.GetUserIdByApiKey(c)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Logger.Error("获取用户id失败", err)
 | 
				
			||||||
 | 
							e.Error(statuscode.Unauthorized, nil, statuscode.GetMsg(statuscode.Unauthorized, "en"))
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						result, code := s.ManualDeduct(&req, userId)
 | 
				
			||||||
 | 
						if code != statuscode.Success {
 | 
				
			||||||
 | 
							e.Logger.Error(statuscode.GetMsg(code, "en"))
 | 
				
			||||||
 | 
							e.Error(code, nil, statuscode.GetMsg(code, "en"))
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						e.OK(result, "success")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 获取详情根据tradeOrderNo
 | 
				
			||||||
 | 
					func (e *SmsRenewalLog) GetByTradeOrderNo(c *gin.Context) {
 | 
				
			||||||
 | 
						req := dto.ManualDeductDetailReq{}
 | 
				
			||||||
 | 
						s := service.SmsRenewalLog{}
 | 
				
			||||||
 | 
						err := e.MakeContext(c).
 | 
				
			||||||
 | 
							MakeOrm().
 | 
				
			||||||
 | 
							Bind(&req).
 | 
				
			||||||
 | 
							MakeService(&s.Service).
 | 
				
			||||||
 | 
							Errors
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Logger.Error(err)
 | 
				
			||||||
 | 
							e.Error(500, err, err.Error())
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						userId, err := middleware.GetUserIdByApiKey(c)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Logger.Error("获取用户id失败", err)
 | 
				
			||||||
 | 
							e.Error(statuscode.Unauthorized, nil, statuscode.GetMsg(statuscode.Unauthorized, "en"))
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						result, code := s.GetRenewalDetailByTradeOrderNo(&req, userId)
 | 
				
			||||||
 | 
						if code != statuscode.Success {
 | 
				
			||||||
 | 
							e.Logger.Error(statuscode.GetMsg(code, "en"))
 | 
				
			||||||
 | 
							e.Error(code, nil, statuscode.GetMsg(code, "en"))
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						e.OK(result, "success")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										33
									
								
								app/admin/models/sms_abnormal_number.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								app/admin/models/sms_abnormal_number.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					package models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"go-admin/common/models"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SmsAbnormalNumber struct {
 | 
				
			||||||
 | 
						models.Model
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Account      string    `json:"account" gorm:"type:varchar(255);comment:账号"`
 | 
				
			||||||
 | 
						PlatformCode string    `json:"platformCode" gorm:"type:varchar(20);comment:平台code"`
 | 
				
			||||||
 | 
						ApiKey       string    `json:"apiKey" gorm:"type:varchar(255);comment:api key"`
 | 
				
			||||||
 | 
						Phone        string    `json:"phone" gorm:"type:varchar(30);comment:电话号码"`
 | 
				
			||||||
 | 
						ActivationId string    `json:"activationId" gorm:"type:varchar(255);comment:激活id"`
 | 
				
			||||||
 | 
						ReceivedTime time.Time `json:"receivedTime" gorm:"type:datetime;comment:接收号码时间"`
 | 
				
			||||||
 | 
						Reused       int       `json:"reused" gorm:"type:tinyint(1);default:2;comment:是否重用 1-是 2-否"`
 | 
				
			||||||
 | 
						models.ModelTime
 | 
				
			||||||
 | 
						models.ControlBy
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (SmsAbnormalNumber) TableName() string {
 | 
				
			||||||
 | 
						return "sms_abnormal_number"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *SmsAbnormalNumber) Generate() models.ActiveRecord {
 | 
				
			||||||
 | 
						o := *e
 | 
				
			||||||
 | 
						return &o
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *SmsAbnormalNumber) GetId() interface{} {
 | 
				
			||||||
 | 
						return e.Id
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -12,6 +12,7 @@ type SmsPhone struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	PlatformCode    string          `json:"platformCode" gorm:"type:varchar(20);comment:平台code"`
 | 
						PlatformCode    string          `json:"platformCode" gorm:"type:varchar(20);comment:平台code"`
 | 
				
			||||||
	UserId          int             `json:"userId" gorm:"type:bigint;comment:用户Id"`
 | 
						UserId          int             `json:"userId" gorm:"type:bigint;comment:用户Id"`
 | 
				
			||||||
 | 
						ApiKey          string          `json:"apiKey" gorm:"type:varchar(255);comment:apiKey"`
 | 
				
			||||||
	Service         string          `json:"service" gorm:"type:varchar(50);comment:sms 服务"`
 | 
						Service         string          `json:"service" gorm:"type:varchar(50);comment:sms 服务"`
 | 
				
			||||||
	ServiceCode     string          `json:"serviceCode" gorm:"type:varchar(30);comment:服务code"`
 | 
						ServiceCode     string          `json:"serviceCode" gorm:"type:varchar(30);comment:服务code"`
 | 
				
			||||||
	Type            int             `json:"type" gorm:"type:tinyint;comment:类型 0-短效 1-长效"`
 | 
						Type            int             `json:"type" gorm:"type:tinyint;comment:类型 0-短效 1-长效"`
 | 
				
			||||||
@ -23,9 +24,14 @@ type SmsPhone struct {
 | 
				
			|||||||
	Code            string          `json:"code" gorm:"type:varchar(10);comment:验证码"`
 | 
						Code            string          `json:"code" gorm:"type:varchar(10);comment:验证码"`
 | 
				
			||||||
	Status          int             `json:"status" gorm:"type:tinyint;comment:状态 1-等待验证码 2-已获取"`
 | 
						Status          int             `json:"status" gorm:"type:tinyint;comment:状态 1-等待验证码 2-已获取"`
 | 
				
			||||||
	ExpireTime      *time.Time      `json:"expireTime" gorm:"type:datetime;comment:过期时间"`
 | 
						ExpireTime      *time.Time      `json:"expireTime" gorm:"type:datetime;comment:过期时间"`
 | 
				
			||||||
 | 
						StartTime       *time.Time      `json:"startTime" gorm:"type:datetime;comment:开始时间"`
 | 
				
			||||||
 | 
						EndTime         *time.Time      `json:"endTime" gorm:"type:datetime;comment:结束时间 单次接码结束时间"`
 | 
				
			||||||
	Actived         int             `json:"actived" gorm:"type:tinyint;comment:是否激活(长期租赁如果第一次没接收到验证码 则不会激活号码) 1-未激活 2-已激活 3-已失效"`
 | 
						Actived         int             `json:"actived" gorm:"type:tinyint;comment:是否激活(长期租赁如果第一次没接收到验证码 则不会激活号码) 1-未激活 2-已激活 3-已失效"`
 | 
				
			||||||
	Price           decimal.Decimal `json:"price" gorm:"type:decimal(10,2);comment:价格"`
 | 
						Price           decimal.Decimal `json:"price" gorm:"type:decimal(10,2);comment:价格"`
 | 
				
			||||||
	AutoRenewal     int             `json:"autoRenewal" gorm:"type:tinyint;comment:是否自动续费 1-自动续费 2-手动续费"`
 | 
						AutoRenewal     int             `json:"autoRenewal" gorm:"type:tinyint;comment:是否自动续费 1-自动续费 2-手动续费"`
 | 
				
			||||||
 | 
						Remark          string          `json:"remark" gorm:"type:varchar(255);comment:备注"`
 | 
				
			||||||
 | 
						BillingCycleId  string          `json:"billingCycleId" gorm:"type:varchar(50);comment:手动续费id"`
 | 
				
			||||||
 | 
						PlatformName    string          `json:"platformName" gorm:"-"`
 | 
				
			||||||
	models.ModelTime
 | 
						models.ModelTime
 | 
				
			||||||
	models.ControlBy
 | 
						models.ControlBy
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										31
									
								
								app/admin/models/sms_platform_key.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								app/admin/models/sms_platform_key.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					package models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"go-admin/common/models"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SmsPlatformKey struct {
 | 
				
			||||||
 | 
						models.Model
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						PlatformCode string `json:"platformCode" gorm:"type:varchar(20);comment:平台code"`
 | 
				
			||||||
 | 
						Account      string `json:"account" gorm:"type:varchar(50);comment:账号"`
 | 
				
			||||||
 | 
						ApiKey       string `json:"apiKey" gorm:"type:varchar(500);comment:平台key"`
 | 
				
			||||||
 | 
						ApiSecret    string `json:"apiSecret" gorm:"type:varchar(255);comment:平台私钥"`
 | 
				
			||||||
 | 
						Status       int64  `json:"status" gorm:"type:tinyint;comment:状态 1-启用 2-禁用"`
 | 
				
			||||||
 | 
						Remark       string `json:"remark" gorm:"type:varchar(255);comment:备注"`
 | 
				
			||||||
 | 
						models.ModelTime
 | 
				
			||||||
 | 
						models.ControlBy
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (SmsPlatformKey) TableName() string {
 | 
				
			||||||
 | 
						return "sms_platform_key"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *SmsPlatformKey) Generate() models.ActiveRecord {
 | 
				
			||||||
 | 
						o := *e
 | 
				
			||||||
 | 
						return &o
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *SmsPlatformKey) GetId() interface{} {
 | 
				
			||||||
 | 
						return e.Id
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -10,7 +10,7 @@ type SmsReceiveLog struct {
 | 
				
			|||||||
	UserId      int    `json:"userId" gorm:"type:bigint;comment:用户id"`
 | 
						UserId      int    `json:"userId" gorm:"type:bigint;comment:用户id"`
 | 
				
			||||||
	Service     string `json:"service" gorm:"type:varchar(30);comment:服务"`
 | 
						Service     string `json:"service" gorm:"type:varchar(30);comment:服务"`
 | 
				
			||||||
	ServiceCode string `json:"serviceCode" gorm:"type:varchar(30);comment:服务code"`
 | 
						ServiceCode string `json:"serviceCode" gorm:"type:varchar(30);comment:服务code"`
 | 
				
			||||||
	MessageId   int    `json:"messageId" gorm:"type:int;comment:短信id"`
 | 
						MessageId   string `json:"messageId" gorm:"type:int;comment:短信id"`
 | 
				
			||||||
	Phone       string `json:"phone" gorm:"type:varchar(30);comment:号码"`
 | 
						Phone       string `json:"phone" gorm:"type:varchar(30);comment:号码"`
 | 
				
			||||||
	Code        string `json:"code" gorm:"type:varchar(20);comment:验证码"`
 | 
						Code        string `json:"code" gorm:"type:varchar(20);comment:验证码"`
 | 
				
			||||||
	Status      int    `json:"status" gorm:"type:tinyint;comment:状态 0-等待验证码 1-成功 2-失败"`
 | 
						Status      int    `json:"status" gorm:"type:tinyint;comment:状态 0-等待验证码 1-成功 2-失败"`
 | 
				
			||||||
 | 
				
			|||||||
@ -12,11 +12,23 @@ type SmsRenewalLog struct {
 | 
				
			|||||||
	models.Model
 | 
						models.Model
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	PhoneId        int             `json:"phoneId" gorm:"type:bigint;comment:号码id"`
 | 
						PhoneId        int             `json:"phoneId" gorm:"type:bigint;comment:号码id"`
 | 
				
			||||||
 | 
						Phone          string          `json:"phone" gorm:"type:varchar(50);comment:号码"`
 | 
				
			||||||
 | 
						PayOrderNo     string          `json:"payOrderNo" gorm:"type:varchar(50);comment:本平台支付订单号"`
 | 
				
			||||||
 | 
						TradeOrderNo   string          `json:"tradeOrderNo" gorm:"type:varchar(50);comment:交易订单号"`
 | 
				
			||||||
 | 
						Status         int             `json:"status" gorm:"type:tinyint;comment:状态 1-处理中(冻结) 2-成功 3-失败(解冻)"`
 | 
				
			||||||
	Type           int             `json:"type" gorm:"type:tinyint;comment:类型 0-长效 1-短效"`
 | 
						Type           int             `json:"type" gorm:"type:tinyint;comment:类型 0-长效 1-短效"`
 | 
				
			||||||
 | 
						Category       int             `json:"category" gorm:"type:tinyint;comment:类别 1-购买号码 2-续费号码"`
 | 
				
			||||||
	UserId         int             `json:"userId" gorm:"type:bigint;comment:用户id"`
 | 
						UserId         int             `json:"userId" gorm:"type:bigint;comment:用户id"`
 | 
				
			||||||
	Amount         decimal.Decimal `json:"amount" gorm:"type:decimal(10,2);comment:扣费金额"`
 | 
						Amount         decimal.Decimal `json:"amount" gorm:"type:decimal(10,2);comment:扣费金额"`
 | 
				
			||||||
	BeforeTime     time.Time       `json:"beforeTime" gorm:"type:datetime;comment:续费前过期时间"`
 | 
						BeforeTime     time.Time       `json:"beforeTime" gorm:"type:datetime;comment:续费前过期时间"`
 | 
				
			||||||
 | 
						TargetTime     *time.Time      `json:"targetTime" gorm:"type:datetime;comment:续费目标过期时间"`
 | 
				
			||||||
	Period         int             `json:"period" gorm:"type:int;comment:时间段"`
 | 
						Period         int             `json:"period" gorm:"type:int;comment:时间段"`
 | 
				
			||||||
 | 
						Remark         string          `json:"remark" gorm:"type:varchar(255);comment:备注"`
 | 
				
			||||||
 | 
						Username       string          `json:"username" gorm:"->"`
 | 
				
			||||||
 | 
						PlatformCode   string          `json:"platformCode" gorm:"->"`
 | 
				
			||||||
 | 
						ApiKey         string          `json:"apiKey" gorm:"->"`
 | 
				
			||||||
 | 
						BillingCycleId string          `json:"billingCycleId" gorm:"->"`
 | 
				
			||||||
 | 
						ActivationId   string          `json:"activationId" gorm:"->"`
 | 
				
			||||||
	models.ModelTime
 | 
						models.ModelTime
 | 
				
			||||||
	models.ControlBy
 | 
						models.ControlBy
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										37
									
								
								app/admin/router/sms_abnormal_number.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								app/admin/router/sms_abnormal_number.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					package router
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"github.com/gin-gonic/gin"
 | 
				
			||||||
 | 
						jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"go-admin/app/admin/apis"
 | 
				
			||||||
 | 
						"go-admin/common/actions"
 | 
				
			||||||
 | 
						"go-admin/common/middleware"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						routerCheckRole = append(routerCheckRole, registerSmsAbnormalNumberRouter)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// registerSmsAbnormalNumberRouter
 | 
				
			||||||
 | 
					func registerSmsAbnormalNumberRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) {
 | 
				
			||||||
 | 
						api := apis.SmsAbnormalNumber{}
 | 
				
			||||||
 | 
						r := v1.Group("/sms-abnormal-number").
 | 
				
			||||||
 | 
							Use(authMiddleware.MiddlewareFunc()).
 | 
				
			||||||
 | 
							Use(middleware.AuthCheckRole())
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							r.GET("", actions.PermissionAction(), api.GetPage)
 | 
				
			||||||
 | 
							r.GET("/:id", actions.PermissionAction(), api.Get)
 | 
				
			||||||
 | 
							r.POST("", api.Insert)
 | 
				
			||||||
 | 
							r.PUT("/:id", actions.PermissionAction(), api.Update)
 | 
				
			||||||
 | 
							r.DELETE("", api.Delete)
 | 
				
			||||||
 | 
							r.DELETE("clear", actions.PermissionAction(), api.Clean)
 | 
				
			||||||
 | 
							r.PUT("/batch-cancel", actions.PermissionAction(), api.BatchCancel)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						r1 := v1.Group("/sms-abnormal-number")
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							r1.POST("/sync-state", api.SyncState)     //同步差异
 | 
				
			||||||
 | 
							r1.PUT("/reuse", api.ReUseAbnormalNumber) //重用号码
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -45,6 +45,7 @@ func registerSmsPhoneRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddl
 | 
				
			|||||||
		r.GET("/:id", actions.PermissionAction(), api.Get)
 | 
							r.GET("/:id", actions.PermissionAction(), api.Get)
 | 
				
			||||||
		r.POST("", api.Insert)
 | 
							r.POST("", api.Insert)
 | 
				
			||||||
		r.PUT("/:id", actions.PermissionAction(), api.Update)
 | 
							r.PUT("/:id", actions.PermissionAction(), api.Update)
 | 
				
			||||||
 | 
							r.PUT("/manual-renewal", api.ManualRenewal)
 | 
				
			||||||
		r.DELETE("", api.Delete)
 | 
							r.DELETE("", api.Delete)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										27
									
								
								app/admin/router/sms_platform_key.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								app/admin/router/sms_platform_key.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					package router
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"github.com/gin-gonic/gin"
 | 
				
			||||||
 | 
						jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"go-admin/app/admin/apis"
 | 
				
			||||||
 | 
						"go-admin/common/middleware"
 | 
				
			||||||
 | 
						"go-admin/common/actions"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						routerCheckRole = append(routerCheckRole, registerSmsPlatformKeyRouter)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// registerSmsPlatformKeyRouter
 | 
				
			||||||
 | 
					func registerSmsPlatformKeyRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) {
 | 
				
			||||||
 | 
						api := apis.SmsPlatformKey{}
 | 
				
			||||||
 | 
						r := v1.Group("/sms-platform-key").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole())
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							r.GET("", actions.PermissionAction(), api.GetPage)
 | 
				
			||||||
 | 
							r.GET("/:id", actions.PermissionAction(), api.Get)
 | 
				
			||||||
 | 
							r.POST("", api.Insert)
 | 
				
			||||||
 | 
							r.PUT("/:id", actions.PermissionAction(), api.Update)
 | 
				
			||||||
 | 
							r.DELETE("", api.Delete)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -5,8 +5,8 @@ import (
 | 
				
			|||||||
	jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth"
 | 
						jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"go-admin/app/admin/apis"
 | 
						"go-admin/app/admin/apis"
 | 
				
			||||||
	"go-admin/common/middleware"
 | 
					 | 
				
			||||||
	"go-admin/common/actions"
 | 
						"go-admin/common/actions"
 | 
				
			||||||
 | 
						"go-admin/common/middleware"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
@ -16,6 +16,14 @@ func init() {
 | 
				
			|||||||
// registerSmsRenewalLogRouter
 | 
					// registerSmsRenewalLogRouter
 | 
				
			||||||
func registerSmsRenewalLogRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) {
 | 
					func registerSmsRenewalLogRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) {
 | 
				
			||||||
	api := apis.SmsRenewalLog{}
 | 
						api := apis.SmsRenewalLog{}
 | 
				
			||||||
 | 
						r1 := v1.Group("sms-renewal-log").Use(middleware.FrontedAuth)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							// 手动扣费续期
 | 
				
			||||||
 | 
							r1.POST("manual-deduct", api.ManualDeduct)
 | 
				
			||||||
 | 
							//根据交易订单号获取续期详情
 | 
				
			||||||
 | 
							r1.GET("detail-byorderno", api.GetByTradeOrderNo)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	r := v1.Group("/sms-renewal-log").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole())
 | 
						r := v1.Group("/sms-renewal-log").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole())
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		r.GET("", actions.PermissionAction(), api.GetPage)
 | 
							r.GET("", actions.PermissionAction(), api.GetPage)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										93
									
								
								app/admin/service/dto/sms_abnormal_number.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								app/admin/service/dto/sms_abnormal_number.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,93 @@
 | 
				
			|||||||
 | 
					package dto
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"go-admin/app/admin/models"
 | 
				
			||||||
 | 
						"go-admin/common/dto"
 | 
				
			||||||
 | 
						common "go-admin/common/models"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SmsAbnormalNumberGetPageReq struct {
 | 
				
			||||||
 | 
						dto.Pagination     `search:"-"`
 | 
				
			||||||
 | 
					    PlatformCode string `form:"platformCode"  search:"type:exact;column:platform_code;table:sms_abnormal_number" comment:"平台code"`
 | 
				
			||||||
 | 
					    Phone string `form:"phone"  search:"type:contains;column:phone;table:sms_abnormal_number" comment:"电话号码"`
 | 
				
			||||||
 | 
					    SmsAbnormalNumberOrder
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SmsAbnormalNumberOrder struct {
 | 
				
			||||||
 | 
					    Id string `form:"idOrder"  search:"type:order;column:id;table:sms_abnormal_number"`
 | 
				
			||||||
 | 
					    Account string `form:"accountOrder"  search:"type:order;column:account;table:sms_abnormal_number"`
 | 
				
			||||||
 | 
					    PlatformCode string `form:"platformCodeOrder"  search:"type:order;column:platform_code;table:sms_abnormal_number"`
 | 
				
			||||||
 | 
					    Phone string `form:"phoneOrder"  search:"type:order;column:phone;table:sms_abnormal_number"`
 | 
				
			||||||
 | 
					    CreatedAt string `form:"createdAtOrder"  search:"type:order;column:created_at;table:sms_abnormal_number"`
 | 
				
			||||||
 | 
					    UpdatedAt string `form:"updatedAtOrder"  search:"type:order;column:updated_at;table:sms_abnormal_number"`
 | 
				
			||||||
 | 
					    DeletedAt string `form:"deletedAtOrder"  search:"type:order;column:deleted_at;table:sms_abnormal_number"`
 | 
				
			||||||
 | 
					    CreateBy string `form:"createByOrder"  search:"type:order;column:create_by;table:sms_abnormal_number"`
 | 
				
			||||||
 | 
					    UpdateBy string `form:"updateByOrder"  search:"type:order;column:update_by;table:sms_abnormal_number"`
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *SmsAbnormalNumberGetPageReq) GetNeedSearch() interface{} {
 | 
				
			||||||
 | 
						return *m
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SmsAbnormalNumberInsertReq struct {
 | 
				
			||||||
 | 
					    Id int `json:"-" comment:"主键id"` // 主键id
 | 
				
			||||||
 | 
					    Account string `json:"account" comment:"账号"`
 | 
				
			||||||
 | 
					    PlatformCode string `json:"platformCode" comment:"平台code"`
 | 
				
			||||||
 | 
					    Phone string `json:"phone" comment:"电话号码"`
 | 
				
			||||||
 | 
					    common.ControlBy
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *SmsAbnormalNumberInsertReq) Generate(model *models.SmsAbnormalNumber)  {
 | 
				
			||||||
 | 
					    if s.Id == 0 {
 | 
				
			||||||
 | 
					        model.Model = common.Model{ Id: s.Id }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    model.Account = s.Account
 | 
				
			||||||
 | 
					    model.PlatformCode = s.PlatformCode
 | 
				
			||||||
 | 
					    model.Phone = s.Phone
 | 
				
			||||||
 | 
					    model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *SmsAbnormalNumberInsertReq) GetId() interface{} {
 | 
				
			||||||
 | 
						return s.Id
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SmsAbnormalNumberUpdateReq struct {
 | 
				
			||||||
 | 
					    Id int `uri:"id" comment:"主键id"` // 主键id
 | 
				
			||||||
 | 
					    Account string `json:"account" comment:"账号"`
 | 
				
			||||||
 | 
					    PlatformCode string `json:"platformCode" comment:"平台code"`
 | 
				
			||||||
 | 
					    Phone string `json:"phone" comment:"电话号码"`
 | 
				
			||||||
 | 
					    common.ControlBy
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *SmsAbnormalNumberUpdateReq) Generate(model *models.SmsAbnormalNumber)  {
 | 
				
			||||||
 | 
					    if s.Id == 0 {
 | 
				
			||||||
 | 
					        model.Model = common.Model{ Id: s.Id }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    model.Account = s.Account
 | 
				
			||||||
 | 
					    model.PlatformCode = s.PlatformCode
 | 
				
			||||||
 | 
					    model.Phone = s.Phone
 | 
				
			||||||
 | 
					    model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *SmsAbnormalNumberUpdateReq) GetId() interface{} {
 | 
				
			||||||
 | 
						return s.Id
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SmsAbnormalNumberGetReq 功能获取请求参数
 | 
				
			||||||
 | 
					type SmsAbnormalNumberGetReq struct {
 | 
				
			||||||
 | 
					     Id int `uri:"id"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func (s *SmsAbnormalNumberGetReq) GetId() interface{} {
 | 
				
			||||||
 | 
						return s.Id
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SmsAbnormalNumberDeleteReq 功能删除请求参数
 | 
				
			||||||
 | 
					type SmsAbnormalNumberDeleteReq struct {
 | 
				
			||||||
 | 
						Ids []int `json:"ids"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *SmsAbnormalNumberDeleteReq) GetId() interface{} {
 | 
				
			||||||
 | 
						return s.Ids
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -14,8 +14,10 @@ type SmsPhoneGetPageReq struct {
 | 
				
			|||||||
	dto.Pagination `search:"-"`
 | 
						dto.Pagination `search:"-"`
 | 
				
			||||||
	Service        string `form:"service"  search:"type:exact;column:service;table:sms_phone" comment:"sms 服务"`
 | 
						Service        string `form:"service"  search:"type:exact;column:service;table:sms_phone" comment:"sms 服务"`
 | 
				
			||||||
	PlatformCode   string `form:"platformCode"  search:"type:exact;column:platform_code;table:sms_phone" comment:"平台code"`
 | 
						PlatformCode   string `form:"platformCode"  search:"type:exact;column:platform_code;table:sms_phone" comment:"平台code"`
 | 
				
			||||||
	ServiceCode    string `form:"serviceCode"  search:"type:exact;column:service_code;table:sms_phone" comment:"服务code"`
 | 
						ServiceCode    string `form:"serviceCode"  search:"type:contains;column:service_code;table:sms_phone" comment:"服务code"`
 | 
				
			||||||
 | 
						Phone          string `form:"phone" search:"type:contains;column:phone;table:sms_phone" comment:"号码"`
 | 
				
			||||||
	Type           int    `form:"type"  search:"-" comment:"类型 0-短效 1-长效"`
 | 
						Type           int    `form:"type"  search:"-" comment:"类型 0-短效 1-长效"`
 | 
				
			||||||
 | 
						IsActived      int    `form:"isActived" search:"-" comment:"是否可用"`
 | 
				
			||||||
	SmsPhoneOrder
 | 
						SmsPhoneOrder
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -152,6 +154,8 @@ type WeakUpReq struct {
 | 
				
			|||||||
type WeakUpResp struct {
 | 
					type WeakUpResp struct {
 | 
				
			||||||
	ActivationId string     `json:"activationId" comment:"激活id"`
 | 
						ActivationId string     `json:"activationId" comment:"激活id"`
 | 
				
			||||||
	MessageId    string     `json:"messageId" commment:"消息id"`
 | 
						MessageId    string     `json:"messageId" commment:"消息id"`
 | 
				
			||||||
 | 
						StartTime    *time.Time `json:"startTime" comment:"接码开始时间"`
 | 
				
			||||||
 | 
						EndTime      *time.Time `json:"endTime" comment:"接码过期时间"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *WeakUpReq) Validate() error {
 | 
					func (s *WeakUpReq) Validate() error {
 | 
				
			||||||
@ -192,7 +196,11 @@ type SmsPhoneCleanMyPhoneReq struct {
 | 
				
			|||||||
type SmsPhoneGetPhoneResp struct {
 | 
					type SmsPhoneGetPhoneResp struct {
 | 
				
			||||||
	Phone          string     `json:"phone"`
 | 
						Phone          string     `json:"phone"`
 | 
				
			||||||
	EndAt          *time.Time `json:"endTime"`
 | 
						EndAt          *time.Time `json:"endTime"`
 | 
				
			||||||
 | 
						StartTime      *time.Time `json:"-" comment:"唤醒后单次接码开始时间"`
 | 
				
			||||||
 | 
						EndTime        *time.Time `json:"-" comment:"唤醒后单次接码过期时间"`
 | 
				
			||||||
 | 
						MessageId      string     `json:"-" comment:"消息id"`
 | 
				
			||||||
	Id             string     `json:"id"`
 | 
						Id             string     `json:"id"`
 | 
				
			||||||
 | 
						BillingCycleId string     `json:"billingCycleId" comment:"续租id"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type DaisysmsPriceResp struct {
 | 
					type DaisysmsPriceResp struct {
 | 
				
			||||||
@ -208,9 +216,45 @@ type OpenGetNumberResp struct {
 | 
				
			|||||||
	Number       string          `json:"number"`
 | 
						Number       string          `json:"number"`
 | 
				
			||||||
	ActivationId string          `json:"activationId"`
 | 
						ActivationId string          `json:"activationId"`
 | 
				
			||||||
	ExpireTime   *time.Time      `json:"expireTime"`
 | 
						ExpireTime   *time.Time      `json:"expireTime"`
 | 
				
			||||||
 | 
						StartTime    *time.Time      `json:"startTime"`
 | 
				
			||||||
 | 
						EndTime      *time.Time      `json:"endTime"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type OpenWakeUpReq struct {
 | 
					type OpenWakeUpReq struct {
 | 
				
			||||||
	PlatformCode string `json:"platformCode" comment:"短信平台"`
 | 
						PlatformCode string `json:"platformCode" comment:"短信平台"`
 | 
				
			||||||
	ActivationId string `json:"activationId" comment:"平台id"`
 | 
						ActivationId string `json:"activationId" comment:"平台id"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ReUseAbnormalNumberReq struct {
 | 
				
			||||||
 | 
						BeginTime    int64  `json:"beginTime" comment:"开始时间"`
 | 
				
			||||||
 | 
						PlatformCode string `json:"platformCode" comment:"短信平台"`
 | 
				
			||||||
 | 
						ServiceCode  string `json:"serviceCode" comment:"服务code"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 批量取消
 | 
				
			||||||
 | 
					type BatchCancelAbnormalNumberReq struct {
 | 
				
			||||||
 | 
						Ids []int `json:"ids" comment:"短信号码id"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ManualRenewalReq struct {
 | 
				
			||||||
 | 
						ActivationIds []string `json:"activationIds" comment:"激活码id"`
 | 
				
			||||||
 | 
						TradeOrderNo  string   `json:"tradeOrderNo" comment:"交易订单号"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 手动续期回调请求
 | 
				
			||||||
 | 
					type ManualRenewCallBackReq struct {
 | 
				
			||||||
 | 
						TradeOrderNo string `json:"tradeOrderNo" comment:"交易订单号"`
 | 
				
			||||||
 | 
						PayOrderNo   string `json:"payOrderNo" comment:"本平台支付订单号"`
 | 
				
			||||||
 | 
						Status       int    `json:"status" comment:"状态 1-处理中 2-失败 3-回滚 4-成功"`
 | 
				
			||||||
 | 
						Remark       string `json:"remark" comment:"备注"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 获取号码响应
 | 
				
			||||||
 | 
					type SmsPhoneGetNumberResp struct {
 | 
				
			||||||
 | 
						ActivationId   string     `json:"activationId" comment:"激活码id"`
 | 
				
			||||||
 | 
						Phone          string     `json:"phone" comment:"号码"`
 | 
				
			||||||
 | 
						ExpireTime     *time.Time `json:"expireTime" comment:"过期时间"`
 | 
				
			||||||
 | 
						StartTime      *time.Time `json:"startTime" comment:"唤醒后单次接码开始时间"`
 | 
				
			||||||
 | 
						EndTime        *time.Time `json:"endTime" comment:"唤醒后单次接码过期时间"`
 | 
				
			||||||
 | 
						BillingCycleId string     `json:"billingCycleId" comment:"续租id"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										117
									
								
								app/admin/service/dto/sms_platform_key.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								app/admin/service/dto/sms_platform_key.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,117 @@
 | 
				
			|||||||
 | 
					package dto
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"go-admin/app/admin/models"
 | 
				
			||||||
 | 
						"go-admin/common/dto"
 | 
				
			||||||
 | 
						common "go-admin/common/models"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SmsPlatformKeyGetPageReq struct {
 | 
				
			||||||
 | 
						dto.Pagination `search:"-"`
 | 
				
			||||||
 | 
						PlatformCode   string `form:"platformCode"  search:"type:exact;column:platform_code;table:sms_platform_key" comment:"平台code"`
 | 
				
			||||||
 | 
						SmsPlatformKeyOrder
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SmsPlatformKeyOrder struct {
 | 
				
			||||||
 | 
						Id           string `form:"idOrder"  search:"type:order;column:id;table:sms_platform_key"`
 | 
				
			||||||
 | 
						PlatformCode string `form:"platformCodeOrder"  search:"type:order;column:platform_code;table:sms_platform_key"`
 | 
				
			||||||
 | 
						ApiKey       string `form:"apiKeyOrder"  search:"type:order;column:api_key;table:sms_platform_key"`
 | 
				
			||||||
 | 
						ApiSecret    string `form:"apiSecretOrder"  search:"type:order;column:api_secret;table:sms_platform_key"`
 | 
				
			||||||
 | 
						Status       string `form:"statusOrder"  search:"type:order;column:status;table:sms_platform_key"`
 | 
				
			||||||
 | 
						Remark       string `form:"remarkOrder"  search:"type:order;column:remark;table:sms_platform_key"`
 | 
				
			||||||
 | 
						CreatedAt    string `form:"createdAtOrder"  search:"type:order;column:created_at;table:sms_platform_key"`
 | 
				
			||||||
 | 
						UpdatedAt    string `form:"updatedAtOrder"  search:"type:order;column:updated_at;table:sms_platform_key"`
 | 
				
			||||||
 | 
						DeletedAt    string `form:"deletedAtOrder"  search:"type:order;column:deleted_at;table:sms_platform_key"`
 | 
				
			||||||
 | 
						CreateBy     string `form:"createByOrder"  search:"type:order;column:create_by;table:sms_platform_key"`
 | 
				
			||||||
 | 
						UpdateBy     string `form:"updateByOrder"  search:"type:order;column:update_by;table:sms_platform_key"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *SmsPlatformKeyGetPageReq) GetNeedSearch() interface{} {
 | 
				
			||||||
 | 
						return *m
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SmsPlatformKeyInsertReq struct {
 | 
				
			||||||
 | 
						Id           int    `json:"-" comment:"主键id"` // 主键id
 | 
				
			||||||
 | 
						PlatformCode string `json:"platformCode" comment:"平台code"`
 | 
				
			||||||
 | 
						Account      string `json:"account" comment:"account"`
 | 
				
			||||||
 | 
						ApiKey       string `json:"apiKey" comment:"平台key"`
 | 
				
			||||||
 | 
						ApiSecret    string `json:"apiSecret" comment:"平台私钥"`
 | 
				
			||||||
 | 
						Status       int64  `json:"status" comment:"状态 1-启用 2-禁用"`
 | 
				
			||||||
 | 
						Remark       string `json:"remark" comment:"备注"`
 | 
				
			||||||
 | 
						common.ControlBy
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *SmsPlatformKeyInsertReq) Generate(model *models.SmsPlatformKey) {
 | 
				
			||||||
 | 
						if s.Id == 0 {
 | 
				
			||||||
 | 
							model.Model = common.Model{Id: s.Id}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						model.PlatformCode = s.PlatformCode
 | 
				
			||||||
 | 
						model.Account = s.Account
 | 
				
			||||||
 | 
						model.ApiKey = s.ApiKey
 | 
				
			||||||
 | 
						model.ApiSecret = s.ApiSecret
 | 
				
			||||||
 | 
						model.Status = s.Status
 | 
				
			||||||
 | 
						model.Remark = s.Remark
 | 
				
			||||||
 | 
						model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *SmsPlatformKeyInsertReq) GetId() interface{} {
 | 
				
			||||||
 | 
						return s.Id
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SmsPlatformKeyUpdateReq struct {
 | 
				
			||||||
 | 
						Id           int    `uri:"id" comment:"主键id"` // 主键id
 | 
				
			||||||
 | 
						PlatformCode string `json:"platformCode" comment:"平台code"`
 | 
				
			||||||
 | 
						Account      string `json:"account" comment:"平台账号"`
 | 
				
			||||||
 | 
						ApiKey       string `json:"apiKey" comment:"平台key"`
 | 
				
			||||||
 | 
						ApiSecret    string `json:"apiSecret" comment:"平台私钥"`
 | 
				
			||||||
 | 
						Status       int64  `json:"status" comment:"状态 1-启用 2-禁用"`
 | 
				
			||||||
 | 
						Remark       string `json:"remark" comment:"备注"`
 | 
				
			||||||
 | 
						common.ControlBy
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *SmsPlatformKeyUpdateReq) Generate(model *models.SmsPlatformKey) {
 | 
				
			||||||
 | 
						if s.Id == 0 {
 | 
				
			||||||
 | 
							model.Model = common.Model{Id: s.Id}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						model.PlatformCode = s.PlatformCode
 | 
				
			||||||
 | 
						model.Account = s.Account
 | 
				
			||||||
 | 
						model.ApiKey = s.ApiKey
 | 
				
			||||||
 | 
						model.ApiSecret = s.ApiSecret
 | 
				
			||||||
 | 
						model.Status = s.Status
 | 
				
			||||||
 | 
						model.Remark = s.Remark
 | 
				
			||||||
 | 
						model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *SmsPlatformKeyUpdateReq) GetId() interface{} {
 | 
				
			||||||
 | 
						return s.Id
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SmsPlatformKeyGetReq 功能获取请求参数
 | 
				
			||||||
 | 
					type SmsPlatformKeyGetReq struct {
 | 
				
			||||||
 | 
						Id int `uri:"id"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *SmsPlatformKeyGetReq) GetId() interface{} {
 | 
				
			||||||
 | 
						return s.Id
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SmsPlatformKeyDeleteReq 功能删除请求参数
 | 
				
			||||||
 | 
					type SmsPlatformKeyDeleteReq struct {
 | 
				
			||||||
 | 
						Ids []int `json:"ids"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *SmsPlatformKeyDeleteReq) GetId() interface{} {
 | 
				
			||||||
 | 
						return s.Ids
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SmsPlatformKeyQueueDto struct {
 | 
				
			||||||
 | 
						PlatformCode string `json:"platformCode" comment:"平台code"`
 | 
				
			||||||
 | 
						Account      string `json:"account" comment:"平台账号"`
 | 
				
			||||||
 | 
						ApiKey       string `json:"apiKey" comment:"平台key"`
 | 
				
			||||||
 | 
						ApiSecret    string `json:"apiSecret" comment:"平台私钥"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SmsPlatformKeyGroupDto struct {
 | 
				
			||||||
 | 
						PlatformCode string `json:"platformCode"`
 | 
				
			||||||
 | 
						Count        int    `json:"count"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -13,6 +13,8 @@ import (
 | 
				
			|||||||
type SmsRenewalLogGetPageReq struct {
 | 
					type SmsRenewalLogGetPageReq struct {
 | 
				
			||||||
	dto.Pagination `search:"-"`
 | 
						dto.Pagination `search:"-"`
 | 
				
			||||||
	Type           int64  `form:"type"  search:"type:exact;column:type;table:sms_renewal_log" comment:"类型 0-长效 1-短效"`
 | 
						Type           int64  `form:"type"  search:"type:exact;column:type;table:sms_renewal_log" comment:"类型 0-长效 1-短效"`
 | 
				
			||||||
 | 
						Category       int    `form:"category"  search:"type:exact;column:category;table:sms_renewal_log" comment:"类别 0-普通续费 1-号码补偿"`
 | 
				
			||||||
 | 
						UserName       string `form:"userName" search:"-"`
 | 
				
			||||||
	SmsRenewalLogOrder
 | 
						SmsRenewalLogOrder
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -108,3 +110,43 @@ type SmsRenewalLogDeleteReq struct {
 | 
				
			|||||||
func (s *SmsRenewalLogDeleteReq) GetId() interface{} {
 | 
					func (s *SmsRenewalLogDeleteReq) GetId() interface{} {
 | 
				
			||||||
	return s.Ids
 | 
						return s.Ids
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SmsRenewalLogResp struct {
 | 
				
			||||||
 | 
						Id           int             `json:"id" comment:"主键id"` // 主键id
 | 
				
			||||||
 | 
						PhoneId      int             `json:"phoneId" comment:"号码id"`
 | 
				
			||||||
 | 
						Phone        string          `json:"phone" comment:"电话号码"`
 | 
				
			||||||
 | 
						TradeOrderNo string          `json:"tradeOrderNo" comment:"交易订单号"`
 | 
				
			||||||
 | 
						PayOrderNo   string          `json:"payOrderNo" comment:"支付订单号"`
 | 
				
			||||||
 | 
						Type         int             `json:"type" comment:"类型 0-长效 1-短效"`
 | 
				
			||||||
 | 
						Category     int             `json:"category" comment:"类别 1-购买 2-续期"`
 | 
				
			||||||
 | 
						UserId       int             `json:"userId" comment:"用户id"`
 | 
				
			||||||
 | 
						Amount       decimal.Decimal `json:"amount" comment:"扣费金额"`
 | 
				
			||||||
 | 
						BeforeTime   time.Time       `json:"beforeTime" comment:"续费前过期时间"`
 | 
				
			||||||
 | 
						Period       int             `json:"period" comment:"时间(天)"`
 | 
				
			||||||
 | 
						CreatedAt    time.Time       `json:"createdAt" comment:"创建时间"`
 | 
				
			||||||
 | 
						Status       int             `json:"status" comment:"状态 1-预扣费 2-成功 3-失败"`
 | 
				
			||||||
 | 
						// UpdatedAt  time.Time       `json:"updatedAt" comment:"更新时间"`
 | 
				
			||||||
 | 
						// DeletedAt  time.Time       `json:"deletedAt" comment:"删除时间"`
 | 
				
			||||||
 | 
						UserName string `json:"username" comment:"用户名"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 手动扣费
 | 
				
			||||||
 | 
					type ManualDeductReq struct {
 | 
				
			||||||
 | 
						ActivationId string    `json:"activationId" comment:"激活id"`
 | 
				
			||||||
 | 
						TradeOrderNo string    `json:"tradeOrderNo" comment:"交易订单号"`
 | 
				
			||||||
 | 
						BeginTime    time.Time `json:"beginTime" comment:"开始时间"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ManualDeductResp struct {
 | 
				
			||||||
 | 
						ActivationId string     `json:"activationId" comment:"激活id"`
 | 
				
			||||||
 | 
						TradeOrderNo string     `json:"tradeOrderNo" comment:"交易订单号"`
 | 
				
			||||||
 | 
						PayOrderNo   string     `json:"payOrderNo" comment:"支付订单号"`
 | 
				
			||||||
 | 
						BeginTime    time.Time  `json:"beginTime" comment:"开始时间"`
 | 
				
			||||||
 | 
						EndTime      *time.Time `json:"endTime" comment:"结束时间"`
 | 
				
			||||||
 | 
						Status       int        `json:"status" comment:"状态 1-预扣费 2-成功 3-失败"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 续费详情
 | 
				
			||||||
 | 
					type ManualDeductDetailReq struct {
 | 
				
			||||||
 | 
						TradeOrderNo string `json:"tradeOrderNo" comment:"交易订单号"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -118,6 +118,7 @@ type SmsServicesGetListResp struct {
 | 
				
			|||||||
	Status         int             `json:"status" comment:"状态"`
 | 
						Status         int             `json:"status" comment:"状态"`
 | 
				
			||||||
	Price          decimal.Decimal `json:"price" comment:"价格"`
 | 
						Price          decimal.Decimal `json:"price" comment:"价格"`
 | 
				
			||||||
	LongPrice      decimal.Decimal `json:"longPrice" comment:"长号码价格"`
 | 
						LongPrice      decimal.Decimal `json:"longPrice" comment:"长号码价格"`
 | 
				
			||||||
 | 
						RenewLongPrice decimal.Decimal `json:"renewLongPrice" comment:"续费长号码价格"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type SmsGetPriceReq struct {
 | 
					type SmsGetPriceReq struct {
 | 
				
			||||||
 | 
				
			|||||||
@ -131,6 +131,7 @@ type VerificationDTO struct {
 | 
				
			|||||||
	Reuse       ReuseInfo        `json:"reuse"`
 | 
						Reuse       ReuseInfo        `json:"reuse"`
 | 
				
			||||||
	Sale        TextVerifiedResp `json:"sale"`
 | 
						Sale        TextVerifiedResp `json:"sale"`
 | 
				
			||||||
	ServiceName string           `json:"serviceName"`
 | 
						ServiceName string           `json:"serviceName"`
 | 
				
			||||||
 | 
						ApiKey      string           `json:"apiKey"`
 | 
				
			||||||
	// State 表示验证或服务的当前状态。
 | 
						// State 表示验证或服务的当前状态。
 | 
				
			||||||
	// 状态是以下枚举值之一,代表了从创建到完成或取消的生命周期:
 | 
						// 状态是以下枚举值之一,代表了从创建到完成或取消的生命周期:
 | 
				
			||||||
	// * verificationPending: 正在等待验证码。
 | 
						// * verificationPending: 正在等待验证码。
 | 
				
			||||||
@ -154,6 +155,7 @@ type VerificationDTO struct {
 | 
				
			|||||||
	// * nonrenewableRefunded: 服务已退款。
 | 
						// * nonrenewableRefunded: 服务已退款。
 | 
				
			||||||
	State                    string  `json:"state" comment:"verificationPending┃verificationCompleted┃verificationCanceled┃verificationTimedOut┃verificationReported┃verificationRefunded┃verificationReused┃verificationReactivated┃renewableActive┃renewableOverdue┃renewableExpired┃renewableRefunded┃nonrenewableActive┃nonrenewableExpired┃nonrenewableRefunded"`
 | 
						State                    string  `json:"state" comment:"verificationPending┃verificationCompleted┃verificationCanceled┃verificationTimedOut┃verificationReported┃verificationRefunded┃verificationReused┃verificationReactivated┃renewableActive┃renewableOverdue┃renewableExpired┃renewableRefunded┃nonrenewableActive┃nonrenewableExpired┃nonrenewableRefunded"`
 | 
				
			||||||
	TotalCost                float64 `json:"totalCost"`
 | 
						TotalCost                float64 `json:"totalCost"`
 | 
				
			||||||
 | 
						IsIncludedForNextRenewal bool    `json:"isIncludedForNextRenewal" comment:"是否自动续期"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 长效详情
 | 
					// 长效详情
 | 
				
			||||||
@ -165,6 +167,7 @@ type VerificationRentalDetailResp struct {
 | 
				
			|||||||
	ID             string           `json:"id"`
 | 
						ID             string           `json:"id"`
 | 
				
			||||||
	Sale           TextVerifiedResp `json:"sale"`
 | 
						Sale           TextVerifiedResp `json:"sale"`
 | 
				
			||||||
	SaleId         string           `json:"saleId"`
 | 
						SaleId         string           `json:"saleId"`
 | 
				
			||||||
 | 
						BillingCycleId string           `json:"billingCycleId" comment:"续租id"`
 | 
				
			||||||
	ServiceName    string           `json:"serviceName"`
 | 
						ServiceName    string           `json:"serviceName"`
 | 
				
			||||||
	State          string           `json:"state"`
 | 
						State          string           `json:"state"`
 | 
				
			||||||
	AlwaysOn       bool             `json:"alwaysOn"`
 | 
						AlwaysOn       bool             `json:"alwaysOn"`
 | 
				
			||||||
@ -212,3 +215,45 @@ type TextVerifiedSmsItemResp struct {
 | 
				
			|||||||
	ParsedCode string `json:"parsedCode"`
 | 
						ParsedCode string `json:"parsedCode"`
 | 
				
			||||||
	Encrypted  bool   `json:"encrypted"`
 | 
						Encrypted  bool   `json:"encrypted"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type TextVerifiedGetNumbersResp struct {
 | 
				
			||||||
 | 
						HasNext bool                            `json:"hasNext" comment:"是否有下一页"`
 | 
				
			||||||
 | 
						Links   TextVerifiedGetNumbersLinksResp `json:"links"`
 | 
				
			||||||
 | 
						Data    []VerificationDTO               `json:"data"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type TextVerifiedGetRentalListResp struct {
 | 
				
			||||||
 | 
						HasNext bool                            `json:"hasNext" comment:"是否有下一页"`
 | 
				
			||||||
 | 
						Links   TextVerifiedGetNumbersLinksResp `json:"links"`
 | 
				
			||||||
 | 
						Data    []VerificationRentalDetailResp  `json:"data"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type TextVerifiedGetNumbersLinksResp struct {
 | 
				
			||||||
 | 
						Next *TextVerifiedResp `json:"next"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type TextVerifiedManualRenewalResp struct {
 | 
				
			||||||
 | 
						CreatedAt      time.Time                         `json:"createdAt" comment:"创建时间"`
 | 
				
			||||||
 | 
						Id             string                            `json:"id" comment:"id"`
 | 
				
			||||||
 | 
						ExcludeRentals []TextVerifiedManualRenewalRental `json:"excludeRentals" comment:"排除的长效详情"`
 | 
				
			||||||
 | 
						IncludeRentals []TextVerifiedManualRenewalRental `json:"includeRentals" comment:"包含的长效详情"`
 | 
				
			||||||
 | 
						IsPaidFor      bool                              `json:"isPaidFor" comment:"是否已支付"`
 | 
				
			||||||
 | 
						TotalCost      decimal.Decimal                   `json:"totalCost" comment:"总费用"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type TextVerifiedManualRenewalRental struct {
 | 
				
			||||||
 | 
						Number         string           `json:"number" comment:"号码"`
 | 
				
			||||||
 | 
						Rental         TextVerifiedResp `json:"rental" comment:"长效详情"`
 | 
				
			||||||
 | 
						RenewalCost    decimal.Decimal  `json:"renewalCost" comment:"续费费用"`
 | 
				
			||||||
 | 
						ServiceName    string           `json:"serviceName" comment:"服务名称"`
 | 
				
			||||||
 | 
						AlreadyRenewed bool             `json:"alreadyRenewed" comment:"是否已续费"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 续费周期详情
 | 
				
			||||||
 | 
					type TextVerifiedGetBillingCycleResp struct {
 | 
				
			||||||
 | 
						Id                   string    `json:"id" comment:"id"`
 | 
				
			||||||
 | 
						RenewedThrough       time.Time `json:"renewedThrough" comment:"已续费到"`
 | 
				
			||||||
 | 
						BillingCycleEndsAt   time.Time `json:"billingCycleEndsAt" comment:"计费周期结束时间"`
 | 
				
			||||||
 | 
						NextAutoRenewAttempt time.Time `json:"nextAutoRenewAttempt" comment:"下次自动续费时间"`
 | 
				
			||||||
 | 
						State                string    `json:"state" comment:"是否激活 active"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -115,7 +115,7 @@ func (e MemberRecharge) CustomRecharge(req *dto.MemberRechargeCustomRechargeReq,
 | 
				
			|||||||
		return errors.New("server error")
 | 
							return errors.New("server error")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cacheExpireDuration := time.Duration(orderExpireTime+1) * time.Minute
 | 
						cacheExpireDuration := time.Duration(orderExpireTime+10) * time.Minute
 | 
				
			||||||
	key := fmt.Sprintf("pre_order:%s", amount)
 | 
						key := fmt.Sprintf("pre_order:%s", amount)
 | 
				
			||||||
	err = e.Orm.Transaction(func(tx *gorm.DB) error {
 | 
						err = e.Orm.Transaction(func(tx *gorm.DB) error {
 | 
				
			||||||
		if err1 := tx.Create(&data).Error; err1 != nil {
 | 
							if err1 := tx.Create(&data).Error; err1 != nil {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										434
									
								
								app/admin/service/sms_abnormal_number.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										434
									
								
								app/admin/service/sms_abnormal_number.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,434 @@
 | 
				
			|||||||
 | 
					package service
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/go-admin-team/go-admin-core/sdk/service"
 | 
				
			||||||
 | 
						"github.com/shopspring/decimal"
 | 
				
			||||||
 | 
						"gorm.io/gorm"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"go-admin/app/admin/models"
 | 
				
			||||||
 | 
						"go-admin/app/admin/service/dto"
 | 
				
			||||||
 | 
						"go-admin/common/actions"
 | 
				
			||||||
 | 
						cDto "go-admin/common/dto"
 | 
				
			||||||
 | 
						"go-admin/common/global"
 | 
				
			||||||
 | 
						"go-admin/common/statuscode"
 | 
				
			||||||
 | 
						"go-admin/utils/utility"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SmsAbnormalNumber struct {
 | 
				
			||||||
 | 
						service.Service
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *SmsAbnormalNumber) Clean() error {
 | 
				
			||||||
 | 
						if err := e.Orm.Exec("truncate table sms_abnormal_number").Error; err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 批量取消
 | 
				
			||||||
 | 
					func (e *SmsAbnormalNumber) BatchCancel(req *dto.BatchCancelAbnormalNumberReq) error {
 | 
				
			||||||
 | 
						var datas []models.SmsAbnormalNumber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := e.Orm.Model(&models.SmsAbnormalNumber{}).Where("id in ?", req.Ids).Find(&datas).Error; err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						utility.SafeGo(func() {
 | 
				
			||||||
 | 
							CancelFunc(e, datas)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func CancelFunc(e *SmsAbnormalNumber, datas []models.SmsAbnormalNumber) {
 | 
				
			||||||
 | 
						phoneService := SmsPhone{Service: e.Service}
 | 
				
			||||||
 | 
						for _, item := range datas {
 | 
				
			||||||
 | 
							phoneService.ChangeAutoRenewManage(item.PlatformCode, item.ApiKey, item.ActivationId, false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							time.Sleep(200 * time.Millisecond)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 重用号码
 | 
				
			||||||
 | 
					func (e *SmsAbnormalNumber) ReUseAbnormalNumber(userId int, req *dto.ReUseAbnormalNumberReq) (dto.OpenGetNumberResp, int) {
 | 
				
			||||||
 | 
						resp := dto.OpenGetNumberResp{}
 | 
				
			||||||
 | 
						balanceService := MemberBalance{Service: e.Service}
 | 
				
			||||||
 | 
						config := dto.GetSysConfigByKEYForServiceResp{}
 | 
				
			||||||
 | 
						configReq := dto.SysConfigByKeyReq{}
 | 
				
			||||||
 | 
						configService := SysConfig{Service: e.Service}
 | 
				
			||||||
 | 
						smsServices := SmsServices{Service: e.Service}
 | 
				
			||||||
 | 
						var price decimal.Decimal
 | 
				
			||||||
 | 
						serviceItem, err := smsServices.GetByCode(req.PlatformCode, req.ServiceCode)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("短信服务报错:%v", err)
 | 
				
			||||||
 | 
							return resp, statuscode.ServerError
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if serviceItem.Code == "" {
 | 
				
			||||||
 | 
							e.Log.Error("短信服务不存在")
 | 
				
			||||||
 | 
							return resp, statuscode.ServerError
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch {
 | 
				
			||||||
 | 
						case req.PlatformCode == global.SmsPlatformDaisysms:
 | 
				
			||||||
 | 
							price = serviceItem.LongPrice
 | 
				
			||||||
 | 
							configReq.ConfigKey = "long_number_premium_daisysms"
 | 
				
			||||||
 | 
						case req.PlatformCode == global.SmsPlatformTextVerified:
 | 
				
			||||||
 | 
							price = serviceItem.LongPrice
 | 
				
			||||||
 | 
							configReq.ConfigKey = "long_number_premium_textverified"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						err = configService.GetWithKey(&configReq, &config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return resp, statuscode.ServerError
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if config.ConfigValue == "" {
 | 
				
			||||||
 | 
							e.Log.Errorf("短期长期租赁浮动百分比不能为空")
 | 
				
			||||||
 | 
							return resp, statuscode.ServerError
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						percent, err := decimal.NewFromString(config.ConfigValue)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("短期或长期租赁费用格式错误")
 | 
				
			||||||
 | 
							return resp, statuscode.ServerError
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						price = price.Mul(decimal.NewFromInt(100).Add(percent)).Div(decimal.NewFromInt(100))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if price.Cmp(decimal.Zero) <= 0 {
 | 
				
			||||||
 | 
							e.Log.Errorf("短期或长期租赁费用不能小于等于0")
 | 
				
			||||||
 | 
							return resp, statuscode.ServerError
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						balance := balanceService.GetBalance(userId)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if balance.LessThan(price) {
 | 
				
			||||||
 | 
							e.Log.Error("余额不足")
 | 
				
			||||||
 | 
							return resp, statuscode.BalanceNotEnough
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						abnomarlNumber, err := e.GetReused(req.PlatformCode, req.BeginTime)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return resp, statuscode.ServerError
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						expireTime := abnomarlNumber.ReceivedTime.AddDate(0, 0, 30)
 | 
				
			||||||
 | 
						startTime := time.Now()
 | 
				
			||||||
 | 
						endTime := startTime
 | 
				
			||||||
 | 
						smsPhone := models.SmsPhone{}
 | 
				
			||||||
 | 
						smsPhone.PlatformCode = req.PlatformCode
 | 
				
			||||||
 | 
						smsPhone.Phone = abnomarlNumber.Phone
 | 
				
			||||||
 | 
						smsPhone.UserId = userId
 | 
				
			||||||
 | 
						smsPhone.Service = serviceItem.Name
 | 
				
			||||||
 | 
						smsPhone.ServiceCode = req.ServiceCode
 | 
				
			||||||
 | 
						smsPhone.Type = 1
 | 
				
			||||||
 | 
						smsPhone.Period = 1
 | 
				
			||||||
 | 
						smsPhone.ActivationId = abnomarlNumber.ActivationId
 | 
				
			||||||
 | 
						smsPhone.MessageId = abnomarlNumber.ActivationId
 | 
				
			||||||
 | 
						smsPhone.NewActivationId = abnomarlNumber.ActivationId
 | 
				
			||||||
 | 
						smsPhone.Actived = 1
 | 
				
			||||||
 | 
						smsPhone.StartTime = &startTime
 | 
				
			||||||
 | 
						smsPhone.EndTime = &endTime
 | 
				
			||||||
 | 
						// smsPhone.ApiKey = apiInfo.ApiKey
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						smsPhone.Price = price
 | 
				
			||||||
 | 
						smsPhone.ExpireTime = &expireTime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//平台不允许取消
 | 
				
			||||||
 | 
						if req.PlatformCode == global.SmsPlatformTextVerified {
 | 
				
			||||||
 | 
							smsPhone.Actived = 2
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						smsPhone.Status = 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = e.Orm.Transaction(func(tx *gorm.DB) error {
 | 
				
			||||||
 | 
							if err1 := tx.Save(&smsPhone).Error; err1 != nil {
 | 
				
			||||||
 | 
								e.Log.Errorf("获取手机号失败", err1)
 | 
				
			||||||
 | 
								return err1
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if err1 := tx.Exec("UPDATE member_balance SET balance = balance -? WHERE user_id =?", price, userId).Error; err1 != nil {
 | 
				
			||||||
 | 
								e.Log.Errorf("更新余额失败", err1)
 | 
				
			||||||
 | 
								return err1
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return resp, statuscode.ServerError
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp.ActivationId = smsPhone.NewActivationId
 | 
				
			||||||
 | 
						resp.ExpireTime = smsPhone.ExpireTime
 | 
				
			||||||
 | 
						resp.Number = smsPhone.Phone
 | 
				
			||||||
 | 
						resp.StartTime = smsPhone.StartTime
 | 
				
			||||||
 | 
						resp.EndTime = smsPhone.EndTime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return resp, statuscode.Success
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 获取重用
 | 
				
			||||||
 | 
					func (e *SmsAbnormalNumber) GetReused(platformCode string, beginTime int64) (models.SmsAbnormalNumber, error) {
 | 
				
			||||||
 | 
						var smsAbnormalNumber models.SmsAbnormalNumber
 | 
				
			||||||
 | 
						query := e.Orm.Model(&smsAbnormalNumber).
 | 
				
			||||||
 | 
							Where("platform_code = ? AND reused = ?", platformCode, 2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if beginTime > 0 {
 | 
				
			||||||
 | 
							query.Where("received_time >= ?", time.Unix(beginTime, 0))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := query.Order("received_time asc").First(&smsAbnormalNumber).Error
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("SmsAbnormalNumberService GetReused error:%s \r\n", err)
 | 
				
			||||||
 | 
							return smsAbnormalNumber, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := e.Orm.Model(&models.SmsAbnormalNumber{}).
 | 
				
			||||||
 | 
							Where("id = ?", smsAbnormalNumber.Id).
 | 
				
			||||||
 | 
							Update("reused", 1).Error; err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("SmsAbnormalNumberService GetReused error:%s \r\n", err)
 | 
				
			||||||
 | 
							return smsAbnormalNumber, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return smsAbnormalNumber, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetPage 获取SmsAbnormalNumber列表
 | 
				
			||||||
 | 
					func (e *SmsAbnormalNumber) GetPage(c *dto.SmsAbnormalNumberGetPageReq, p *actions.DataPermission, list *[]models.SmsAbnormalNumber, count *int64) error {
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
						var data models.SmsAbnormalNumber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = e.Orm.Model(&data).
 | 
				
			||||||
 | 
							Scopes(
 | 
				
			||||||
 | 
								cDto.MakeCondition(c.GetNeedSearch()),
 | 
				
			||||||
 | 
								cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
 | 
				
			||||||
 | 
								actions.Permission(data.TableName(), p),
 | 
				
			||||||
 | 
							).
 | 
				
			||||||
 | 
							Find(list).Limit(-1).Offset(-1).
 | 
				
			||||||
 | 
							Count(count).Error
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("SmsAbnormalNumberService GetPage error:%s \r\n", err)
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Get 获取SmsAbnormalNumber对象
 | 
				
			||||||
 | 
					func (e *SmsAbnormalNumber) Get(d *dto.SmsAbnormalNumberGetReq, p *actions.DataPermission, model *models.SmsAbnormalNumber) error {
 | 
				
			||||||
 | 
						var data models.SmsAbnormalNumber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := e.Orm.Model(&data).
 | 
				
			||||||
 | 
							Scopes(
 | 
				
			||||||
 | 
								actions.Permission(data.TableName(), p),
 | 
				
			||||||
 | 
							).
 | 
				
			||||||
 | 
							First(model, d.GetId()).Error
 | 
				
			||||||
 | 
						if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
 | 
				
			||||||
 | 
							err = errors.New("查看对象不存在或无权查看")
 | 
				
			||||||
 | 
							e.Log.Errorf("Service GetSmsAbnormalNumber error:%s \r\n", err)
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("db error:%s", err)
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Insert 创建SmsAbnormalNumber对象
 | 
				
			||||||
 | 
					func (e *SmsAbnormalNumber) Insert(c *dto.SmsAbnormalNumberInsertReq) error {
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
						var data models.SmsAbnormalNumber
 | 
				
			||||||
 | 
						c.Generate(&data)
 | 
				
			||||||
 | 
						err = e.Orm.Create(&data).Error
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("SmsAbnormalNumberService Insert error:%s \r\n", err)
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Update 修改SmsAbnormalNumber对象
 | 
				
			||||||
 | 
					func (e *SmsAbnormalNumber) Update(c *dto.SmsAbnormalNumberUpdateReq, p *actions.DataPermission) error {
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
						var data = models.SmsAbnormalNumber{}
 | 
				
			||||||
 | 
						e.Orm.Scopes(
 | 
				
			||||||
 | 
							actions.Permission(data.TableName(), p),
 | 
				
			||||||
 | 
						).First(&data, c.GetId())
 | 
				
			||||||
 | 
						c.Generate(&data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						db := e.Orm.Save(&data)
 | 
				
			||||||
 | 
						if err = db.Error; err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("SmsAbnormalNumberService Save error:%s \r\n", err)
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if db.RowsAffected == 0 {
 | 
				
			||||||
 | 
							return errors.New("无权更新该数据")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Remove 删除SmsAbnormalNumber
 | 
				
			||||||
 | 
					func (e *SmsAbnormalNumber) Remove(d *dto.SmsAbnormalNumberDeleteReq, p *actions.DataPermission) error {
 | 
				
			||||||
 | 
						var data models.SmsAbnormalNumber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						db := e.Orm.Model(&data).
 | 
				
			||||||
 | 
							Scopes(
 | 
				
			||||||
 | 
								actions.Permission(data.TableName(), p),
 | 
				
			||||||
 | 
							).Delete(&data, d.GetId())
 | 
				
			||||||
 | 
						if err := db.Error; err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("Service RemoveSmsAbnormalNumber error:%s \r\n", err)
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if db.RowsAffected == 0 {
 | 
				
			||||||
 | 
							return errors.New("无权删除该数据")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 获取平台上不再系统内的自动续期号码
 | 
				
			||||||
 | 
					func (e *SmsAbnormalNumber) SyncState() error {
 | 
				
			||||||
 | 
						textVerified := SmsTextVerified{Service: e.Service}
 | 
				
			||||||
 | 
						// daiSysms := SmsDaisysms{Service: e.Service} 没有列表api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						textVerifiedNumbers, err := textVerified.GetAllPlatformNumbers()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("获取长效号码列表失败 %v", err)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							// 获取系统内textverified平台的自动续期号码
 | 
				
			||||||
 | 
							// 参数: 平台代码, 号码类型(1=长效), 自动续费(1=开启), 激活状态(2=已激活)
 | 
				
			||||||
 | 
							systemNumbers, err := e.GetPhoneByPlatformCode(global.SmsPlatformTextVerified, 1, 1, 2)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								e.Log.Errorf("获取系统内textverified自动续期号码失败: %v", err)
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							e.Log.Infof("系统内textverified自动续期号码数量: %d", len(systemNumbers))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 比较平台号码和系统号码,找出差异
 | 
				
			||||||
 | 
							missingNumbers := e.findMissingNumbers(textVerifiedNumbers, systemNumbers)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if len(missingNumbers) > 0 {
 | 
				
			||||||
 | 
								e.Log.Warnf("发现%d个平台开启自动续费但系统内缺失的号码", len(missingNumbers))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								err = e.Orm.Transaction(func(tx *gorm.DB) error {
 | 
				
			||||||
 | 
									if err1 := tx.Model(&models.SmsAbnormalNumber{}).
 | 
				
			||||||
 | 
										Unscoped().
 | 
				
			||||||
 | 
										Where(" platform_code =?", global.SmsPlatformTextVerified).
 | 
				
			||||||
 | 
										Delete(&models.SmsAbnormalNumber{}).
 | 
				
			||||||
 | 
										Error; err1 != nil {
 | 
				
			||||||
 | 
										return err1
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if err1 := tx.CreateInBatches(missingNumbers, 100).Error; err1 != nil {
 | 
				
			||||||
 | 
										return err1
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									return nil
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									e.Log.Errorf("同步后保存差异数据失败 %v", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								e.Log.Infof("所有平台自动续期号码在系统内都存在")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// findMissingNumbers 比较平台号码和系统号码,找出平台上有但系统内没有的号码
 | 
				
			||||||
 | 
					// 参数:
 | 
				
			||||||
 | 
					//   - platformNumbers: 平台上的号码列表
 | 
				
			||||||
 | 
					//   - systemNumbers: 系统内的号码列表
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// 返回:
 | 
				
			||||||
 | 
					//   - []dto.VerificationDTO: 平台上有但系统内缺失的号码列表
 | 
				
			||||||
 | 
					func (e *SmsAbnormalNumber) findMissingNumbers(platformNumbers map[string][]dto.VerificationDTO, systemNumbers []models.SmsPhone) []models.SmsAbnormalNumber {
 | 
				
			||||||
 | 
						// 创建系统号码的映射表,用于快速查找
 | 
				
			||||||
 | 
						systemNumberMap := make(map[string]bool)
 | 
				
			||||||
 | 
						for _, sysPhone := range systemNumbers {
 | 
				
			||||||
 | 
							// 使用号码作为key
 | 
				
			||||||
 | 
							systemNumberMap[sysPhone.Phone] = true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 找出平台上有但系统内没有的号码
 | 
				
			||||||
 | 
						missingNumbers := make([]models.SmsAbnormalNumber, 0)
 | 
				
			||||||
 | 
						for account, platformPhone := range platformNumbers {
 | 
				
			||||||
 | 
							for _, v := range platformPhone {
 | 
				
			||||||
 | 
								if v.State == "renewableActive" && v.IsIncludedForNextRenewal && !systemNumberMap[v.Number] {
 | 
				
			||||||
 | 
									missingNumbers = append(missingNumbers, models.SmsAbnormalNumber{
 | 
				
			||||||
 | 
										PlatformCode: global.SmsPlatformTextVerified,
 | 
				
			||||||
 | 
										Account:      account,
 | 
				
			||||||
 | 
										Phone:        v.Number,
 | 
				
			||||||
 | 
										ActivationId: v.ID,
 | 
				
			||||||
 | 
										ReceivedTime: v.CreatedAt,
 | 
				
			||||||
 | 
										ApiKey:       v.ApiKey,
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return missingNumbers
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetPhoneByPlatformCode 循环获取对应平台自动续期的号码
 | 
				
			||||||
 | 
					// 为避免一次性查询过多数据,使用分页查询,每次最多查询100条记录
 | 
				
			||||||
 | 
					// @param platformCode 平台代码 (如: textverified, daisysms)
 | 
				
			||||||
 | 
					// @return []models.SmsPhone 号码列表
 | 
				
			||||||
 | 
					// @return error 错误信息
 | 
				
			||||||
 | 
					func (e *SmsAbnormalNumber) GetPhoneByPlatformCode(platformCode string, numberType, autoRenewal, status int) ([]models.SmsPhone, error) {
 | 
				
			||||||
 | 
						var phones []models.SmsPhone
 | 
				
			||||||
 | 
						var allPhones []models.SmsPhone
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 分页参数
 | 
				
			||||||
 | 
						pageSize := 1000 // 每页最多1000条记录
 | 
				
			||||||
 | 
						offset := 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 循环分页查询,避免一次性查询过多数据
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							// 查询条件:
 | 
				
			||||||
 | 
							// 1. platform_code = platformCode (指定平台)
 | 
				
			||||||
 | 
							// 2. type = numberType (号码类型)
 | 
				
			||||||
 | 
							// 3. auto_renewal = autoRenewal (自动续费状态)
 | 
				
			||||||
 | 
							// 4. actived = status (激活状态)
 | 
				
			||||||
 | 
							err := e.Orm.Model(&models.SmsPhone{}).
 | 
				
			||||||
 | 
								Where("platform_code = ? AND type = ? AND auto_renewal = ? AND actived = ? AND expire_time > ?",
 | 
				
			||||||
 | 
									platformCode, numberType, autoRenewal, status, time.Now()).
 | 
				
			||||||
 | 
								Order("id ASC").
 | 
				
			||||||
 | 
								Limit(pageSize).
 | 
				
			||||||
 | 
								Offset(offset).
 | 
				
			||||||
 | 
								Find(&phones).Error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								e.Log.Errorf("查询平台[%s]自动续期号码失败: %v", platformCode, err)
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 如果没有查询到数据,说明已经查询完毕
 | 
				
			||||||
 | 
							if len(phones) == 0 {
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 将本次查询结果添加到总结果中
 | 
				
			||||||
 | 
							allPhones = append(allPhones, phones...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 如果本次查询结果少于pageSize,说明已经是最后一页
 | 
				
			||||||
 | 
							if len(phones) < pageSize {
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 更新偏移量,准备查询下一页
 | 
				
			||||||
 | 
							offset += pageSize
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						e.Log.Infof("平台[%s]查询到%d个自动续期号码", platformCode, len(allPhones))
 | 
				
			||||||
 | 
						return allPhones, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										20
									
								
								app/admin/service/sms_abnormal_number_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								app/admin/service/sms_abnormal_number_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					package service
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/go-admin-team/go-admin-core/logger"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 同步差异号码
 | 
				
			||||||
 | 
					func TestSyncSat(t *testing.T) {
 | 
				
			||||||
 | 
						db := initSetting()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						smsAbnormalNumber := SmsAbnormalNumber{}
 | 
				
			||||||
 | 
						smsAbnormalNumber.Orm = db
 | 
				
			||||||
 | 
						smsAbnormalNumber.Log = logger.NewHelper(logger.DefaultLogger)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := smsAbnormalNumber.SyncState(); err != nil {
 | 
				
			||||||
 | 
							t.Errorf("同步差异号码失败 %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -25,7 +25,9 @@ type SmsDaisysms struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// 同步价格
 | 
					// 同步价格
 | 
				
			||||||
func (e SmsDaisysms) SyncPrices() {
 | 
					func (e SmsDaisysms) SyncPrices() {
 | 
				
			||||||
	prices, err := e.GetPrices()
 | 
						smsPlatformKeyRedis := NewSmsPlatformKeyRedis(e.Orm, e.Log)
 | 
				
			||||||
 | 
						apiInfo, err := smsPlatformKeyRedis.GetRoundRobinKey(global.SmsPlatformDaisysms)
 | 
				
			||||||
 | 
						prices, err := e.GetPrices(apiInfo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		e.Log.Errorf("GetPrices error: %v", err)
 | 
							e.Log.Errorf("GetPrices error: %v", err)
 | 
				
			||||||
@ -44,16 +46,12 @@ func (e SmsDaisysms) SyncPrices() {
 | 
				
			|||||||
// service 服务code
 | 
					// service 服务code
 | 
				
			||||||
// maxPrice 最大价格
 | 
					// maxPrice 最大价格
 | 
				
			||||||
// period 时长(月)
 | 
					// period 时长(月)
 | 
				
			||||||
func (e *SmsDaisysms) GetNumberForApi(getType int, serviceCode string, maxPrice decimal.Decimal, period int) (int, string, int) {
 | 
					func (e *SmsDaisysms) GetNumberForApi(apiInfo *dto.SmsPlatformKeyQueueDto, getType int, serviceCode string, maxPrice decimal.Decimal, period int) (int, string, int) {
 | 
				
			||||||
	acitvationId := 0
 | 
						acitvationId := 0
 | 
				
			||||||
	result := ""
 | 
						result := ""
 | 
				
			||||||
	resultCode := statuscode.Success
 | 
						resultCode := statuscode.Success
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	configResp, code := GetApiKey(e)
 | 
						url := fmt.Sprintf("?api_key=%s&action=getNumber&service=%s", apiInfo.ApiKey, serviceCode)
 | 
				
			||||||
	if code != statuscode.Success {
 | 
					 | 
				
			||||||
		return acitvationId, result, code
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	url := fmt.Sprintf("?api_key=%s&action=getNumber&service=%s", configResp.ConfigValue, serviceCode)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if getType == 1 {
 | 
						if getType == 1 {
 | 
				
			||||||
		url = fmt.Sprintf("%s&duration=%dM", url, period)
 | 
							url = fmt.Sprintf("%s&duration=%dM", url, period)
 | 
				
			||||||
@ -103,13 +101,8 @@ func (e *SmsDaisysms) GetNumberForApi(getType int, serviceCode string, maxPrice
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 设置租赁结束
 | 
					// 设置租赁结束
 | 
				
			||||||
func (e *SmsDaisysms) setStatus(id int) int {
 | 
					func (e *SmsDaisysms) setStatus(id int, apiInfo *dto.SmsPlatformKeyQueueDto) int {
 | 
				
			||||||
	configResp, code := GetApiKey(e)
 | 
						url := fmt.Sprintf("?api_key=%s&action=setStatus&id=%d&status=6", apiInfo.ApiKey, id)
 | 
				
			||||||
	if code != statuscode.Success {
 | 
					 | 
				
			||||||
		return code
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	url := fmt.Sprintf("?api_key=%s&action=setStatus&id=%d&status=6", configResp.ConfigValue, id)
 | 
					 | 
				
			||||||
	client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil)
 | 
						client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil)
 | 
				
			||||||
	bytes, _, err := client.GetRaw(url, nil)
 | 
						bytes, _, err := client.GetRaw(url, nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -127,34 +120,14 @@ func (e *SmsDaisysms) setStatus(id int) int {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func GetApiKey(e *SmsDaisysms) (dto.GetSysConfigByKEYForServiceResp, int) {
 | 
					 | 
				
			||||||
	configService := SysConfig{Service: e.Service}
 | 
					 | 
				
			||||||
	configResp := dto.GetSysConfigByKEYForServiceResp{}
 | 
					 | 
				
			||||||
	err := configService.GetWithKey(&dto.SysConfigByKeyReq{ConfigKey: "sms_key"}, &configResp)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		e.Log.Errorf("获取短信api失败, %s", err)
 | 
					 | 
				
			||||||
		return dto.GetSysConfigByKEYForServiceResp{}, statuscode.ServerError
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if configResp.ConfigValue == "" {
 | 
					 | 
				
			||||||
		e.Log.Error("短信api不能为空")
 | 
					 | 
				
			||||||
		return dto.GetSysConfigByKEYForServiceResp{}, statuscode.ServerError
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return configResp, statuscode.Success
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// GetCodeForApi 获取验证码
 | 
					// GetCodeForApi 获取验证码
 | 
				
			||||||
// messageId 短信id
 | 
					// messageId 短信id
 | 
				
			||||||
// return 验证码, 状态码
 | 
					// return 验证码, 状态码
 | 
				
			||||||
func (e *SmsDaisysms) GetCodeForApi(messageId string) (string, int) {
 | 
					func (e *SmsDaisysms) GetCodeForApi(messageId string, apiInfo *dto.SmsPlatformKeyQueueDto) (string, int) {
 | 
				
			||||||
	result := ""
 | 
						result := ""
 | 
				
			||||||
	key, code := GetApiKey(e)
 | 
						code := statuscode.Success
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if code != statuscode.Success {
 | 
						url := fmt.Sprintf("?api_key=%s&action=getStatus&id=%s", apiInfo.ApiKey, messageId)
 | 
				
			||||||
		return result, code
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	url := fmt.Sprintf("?api_key=%s&action=getStatus&id=%s", key.ConfigValue, messageId)
 | 
					 | 
				
			||||||
	client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil)
 | 
						client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil)
 | 
				
			||||||
	bytes, _, err := client.GetRaw(url, nil)
 | 
						bytes, _, err := client.GetRaw(url, nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -183,13 +156,10 @@ func (e *SmsDaisysms) GetCodeForApi(messageId string) (string, int) {
 | 
				
			|||||||
// getExtraActivation 获取额外的激活
 | 
					// getExtraActivation 获取额外的激活
 | 
				
			||||||
// messageId 短信id
 | 
					// messageId 短信id
 | 
				
			||||||
// return 验证码, 状态码
 | 
					// return 验证码, 状态码
 | 
				
			||||||
func (e *SmsDaisysms) getExtraActivation(activationId string) (int, int) {
 | 
					func (e *SmsDaisysms) getExtraActivation(activationId string, apiInfo *dto.SmsPlatformKeyQueueDto) (int, int) {
 | 
				
			||||||
	result := 0
 | 
						result := 0
 | 
				
			||||||
	key, err := GetApiKey(e)
 | 
					
 | 
				
			||||||
	if err != statuscode.Success {
 | 
						url := fmt.Sprintf("?api_key=%s&action=getExtraActivation&activationId=%s", apiInfo.ApiKey, activationId)
 | 
				
			||||||
		return 0, statuscode.ServerError
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	url := fmt.Sprintf("?api_key=%s&action=getExtraActivation&activationId=%s", key.ConfigValue, activationId)
 | 
					 | 
				
			||||||
	client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil)
 | 
						client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil)
 | 
				
			||||||
	bytes, _, err1 := client.GetRaw(url, nil)
 | 
						bytes, _, err1 := client.GetRaw(url, nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -224,14 +194,9 @@ func (e *SmsDaisysms) getExtraActivation(activationId string) (int, int) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// KeepLongTerm 长期租赁
 | 
					// KeepLongTerm 长期租赁
 | 
				
			||||||
func (e *SmsDaisysms) KeepLongTerm(activationId string) int {
 | 
					func (e *SmsDaisysms) KeepLongTerm(activationId string, apiInfo *dto.SmsPlatformKeyQueueDto) int {
 | 
				
			||||||
	key, code := GetApiKey(e)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if code != statuscode.Success {
 | 
						url := fmt.Sprintf("?api_key=%s&action=keep&id=%s", apiInfo.ApiKey, activationId)
 | 
				
			||||||
		return statuscode.ServerError
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	url := fmt.Sprintf("?api_key=%s&action=keep&id=%s", key.ConfigValue, activationId)
 | 
					 | 
				
			||||||
	client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil)
 | 
						client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil)
 | 
				
			||||||
	bytes, _, err := client.GetRaw(url, nil)
 | 
						bytes, _, err := client.GetRaw(url, nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -250,15 +215,8 @@ func (e *SmsDaisysms) KeepLongTerm(activationId string) int {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 取消租赁
 | 
					// 取消租赁
 | 
				
			||||||
func (e *SmsDaisysms) CancelRental(activationId string) int {
 | 
					func (e *SmsDaisysms) CancelRental(activationId string, apiInfo *dto.SmsPlatformKeyQueueDto) int {
 | 
				
			||||||
	key, code := GetApiKey(e)
 | 
						url := fmt.Sprintf("?api_key=%s&action=setStatus&id=%s&status=8", apiInfo.ApiKey, activationId)
 | 
				
			||||||
 | 
					 | 
				
			||||||
	if code != statuscode.Success {
 | 
					 | 
				
			||||||
		e.Log.Errorf("租赁api请求失败 %s")
 | 
					 | 
				
			||||||
		return statuscode.ServerError
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	url := fmt.Sprintf("?api_key=%s&action=setStatus&id=%s&status=8", key.ConfigValue, activationId)
 | 
					 | 
				
			||||||
	client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil)
 | 
						client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil)
 | 
				
			||||||
	bytes, _, err := client.GetRaw(url, nil)
 | 
						bytes, _, err := client.GetRaw(url, nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -277,16 +235,10 @@ func (e *SmsDaisysms) CancelRental(activationId string) int {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 获取价格
 | 
					// 获取价格
 | 
				
			||||||
func (e *SmsDaisysms) GetPrices() ([]dto.DaisysmsPriceResp, error) {
 | 
					func (e *SmsDaisysms) GetPrices(apiInfo *dto.SmsPlatformKeyQueueDto) ([]dto.DaisysmsPriceResp, error) {
 | 
				
			||||||
	result := make([]dto.DaisysmsPriceResp, 0)
 | 
						result := make([]dto.DaisysmsPriceResp, 0)
 | 
				
			||||||
	key, code := GetApiKey(e)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if code != statuscode.Success {
 | 
						url := fmt.Sprintf("?api_key=%s&action=getPrices", apiInfo.ApiKey)
 | 
				
			||||||
		e.Log.Errorf("租赁api请求失败 %s")
 | 
					 | 
				
			||||||
		return result, errors.New("获取租赁ApiKey失败")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	url := fmt.Sprintf("?api_key=%s&action=getPrices", key.ConfigValue)
 | 
					 | 
				
			||||||
	client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil)
 | 
						client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil)
 | 
				
			||||||
	bytes, status, err := client.GetRaw(url, nil)
 | 
						bytes, status, err := client.GetRaw(url, nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -327,15 +279,9 @@ func (e *SmsDaisysms) GetPrices() ([]dto.DaisysmsPriceResp, error) {
 | 
				
			|||||||
// ChangeAutoRenew 修改自动续期
 | 
					// ChangeAutoRenew 修改自动续期
 | 
				
			||||||
// activationId 短信id
 | 
					// activationId 短信id
 | 
				
			||||||
// status 状态
 | 
					// status 状态
 | 
				
			||||||
func (e *SmsDaisysms) ChangeAutoRenewForApi(activationId string, status bool) int {
 | 
					func (e *SmsDaisysms) ChangeAutoRenewForApi(activationId string, status bool, apiInfo *dto.SmsPlatformKeyQueueDto) int {
 | 
				
			||||||
	key, err := GetApiKey(e)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err != statuscode.Success {
 | 
						url := fmt.Sprintf("?api_key=%s&action=setAutoRenew&id=%s&value=%t", apiInfo.ApiKey, activationId, status)
 | 
				
			||||||
		e.Log.Errorf("查询sms api请求失败 %d", activationId)
 | 
					 | 
				
			||||||
		return statuscode.ServerError
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	url := fmt.Sprintf("?api_key=%s&action=setAutoRenew&id=%s&value=%t", key.ConfigValue, activationId, status)
 | 
					 | 
				
			||||||
	client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil)
 | 
						client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil)
 | 
				
			||||||
	bytes, _, err1 := client.GetRaw(url, nil)
 | 
						bytes, _, err1 := client.GetRaw(url, nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -2,6 +2,7 @@ package service
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
@ -16,6 +17,7 @@ import (
 | 
				
			|||||||
	cDto "go-admin/common/dto"
 | 
						cDto "go-admin/common/dto"
 | 
				
			||||||
	"go-admin/common/global"
 | 
						"go-admin/common/global"
 | 
				
			||||||
	"go-admin/common/statuscode"
 | 
						"go-admin/common/statuscode"
 | 
				
			||||||
 | 
						"go-admin/utils/utility"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type SmsPhone struct {
 | 
					type SmsPhone struct {
 | 
				
			||||||
@ -23,7 +25,7 @@ type SmsPhone struct {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// open-API 获取电话号码
 | 
					// open-API 获取电话号码
 | 
				
			||||||
func (e SmsPhone) OpenGetNumber(req *dto.GetNumberReq, userId int) (dto.OpenGetNumberResp, int) {
 | 
					func (e *SmsPhone) OpenGetNumber(req *dto.GetNumberReq, userId int) (dto.OpenGetNumberResp, int) {
 | 
				
			||||||
	resp := dto.OpenGetNumberResp{}
 | 
						resp := dto.OpenGetNumberResp{}
 | 
				
			||||||
	balanceService := MemberBalance{Service: e.Service}
 | 
						balanceService := MemberBalance{Service: e.Service}
 | 
				
			||||||
	balance, smsPhone, i := e.DoGetNumber(&balanceService, req, userId)
 | 
						balance, smsPhone, i := e.DoGetNumber(&balanceService, req, userId)
 | 
				
			||||||
@ -36,6 +38,8 @@ func (e SmsPhone) OpenGetNumber(req *dto.GetNumberReq, userId int) (dto.OpenGetN
 | 
				
			|||||||
	resp.ActivationId = smsPhone.NewActivationId
 | 
						resp.ActivationId = smsPhone.NewActivationId
 | 
				
			||||||
	resp.ExpireTime = smsPhone.ExpireTime
 | 
						resp.ExpireTime = smsPhone.ExpireTime
 | 
				
			||||||
	resp.Number = smsPhone.Phone
 | 
						resp.Number = smsPhone.Phone
 | 
				
			||||||
 | 
						resp.StartTime = smsPhone.StartTime
 | 
				
			||||||
 | 
						resp.EndTime = smsPhone.EndTime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return resp, statuscode.Success
 | 
						return resp, statuscode.Success
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -64,7 +68,7 @@ func (e *SmsPhone) CancelNumber(req *dto.SmsPhoneCancelNumberReq, userId int) in
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		if code == statuscode.Success {
 | 
							if code == statuscode.Success {
 | 
				
			||||||
			err := e.Orm.Transaction(func(tx *gorm.DB) error {
 | 
								err := e.Orm.Transaction(func(tx *gorm.DB) error {
 | 
				
			||||||
				if err1 := e.Orm.Model(data).Updates(map[string]interface{}{"status": 3, "code": "", "actived": "3", "auto_renewal": 2}).Error; err1 != nil {
 | 
									if err1 := e.Orm.Model(data).Updates(map[string]interface{}{"status": 3, "code": "", "actived": "3", "auto_renewal": 2, "deleted_at": time.Now()}).Error; err1 != nil {
 | 
				
			||||||
					e.Log.Errorf("更新短信号码失败, %s", err1)
 | 
										e.Log.Errorf("更新短信号码失败, %s", err1)
 | 
				
			||||||
					return err1
 | 
										return err1
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
@ -80,6 +84,11 @@ func (e *SmsPhone) CancelNumber(req *dto.SmsPhoneCancelNumberReq, userId int) in
 | 
				
			|||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				e.Log.Errorf("取消租赁后修改失败, %s", err)
 | 
									e.Log.Errorf("取消租赁后修改失败, %s", err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if err1 := e.Orm.
 | 
				
			||||||
 | 
									Where("phone_id = ?", data.Id).Delete(&models.SmsRenewalLog{}).Error; err1 != nil {
 | 
				
			||||||
 | 
									e.Log.Errorf("更新短信号码续费日志失败, %s", err1)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			e.Log.Errorf("取消租赁失败, %s", code)
 | 
								e.Log.Errorf("取消租赁失败, %s", code)
 | 
				
			||||||
			return code
 | 
								return code
 | 
				
			||||||
@ -91,15 +100,22 @@ func (e *SmsPhone) CancelNumber(req *dto.SmsPhoneCancelNumberReq, userId int) in
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// 聚合取消
 | 
					// 聚合取消
 | 
				
			||||||
func (e *SmsPhone) CancelNumberManage(data *models.SmsPhone) int {
 | 
					func (e *SmsPhone) CancelNumberManage(data *models.SmsPhone) int {
 | 
				
			||||||
 | 
						smsPlatformKeyRedis := NewSmsPlatformKeyRedis(e.Orm, e.Log)
 | 
				
			||||||
 | 
						apiInfo, err := smsPlatformKeyRedis.GetApiInfo(data.PlatformCode, data.ApiKey)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("获取平台密钥失败, %s", err)
 | 
				
			||||||
 | 
							return statuscode.ServerError
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch data.PlatformCode {
 | 
						switch data.PlatformCode {
 | 
				
			||||||
	case global.SmsPlatformDaisysms:
 | 
						case global.SmsPlatformDaisysms:
 | 
				
			||||||
		service := SmsDaisysms{Service: e.Service}
 | 
							service := SmsDaisysms{Service: e.Service}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return service.CancelRental(data.NewActivationId)
 | 
							return service.CancelRental(data.NewActivationId, &apiInfo)
 | 
				
			||||||
	case global.SmsPlatformTextVerified:
 | 
						case global.SmsPlatformTextVerified:
 | 
				
			||||||
		service := SmsTextVerified{Service: e.Service}
 | 
							service := SmsTextVerified{Service: e.Service}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return service.CancelRental(data.NewActivationId, data.Type)
 | 
							return service.CancelRental(data.NewActivationId, data.Type, &apiInfo)
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		return statuscode.SmsPlatformUnavailable
 | 
							return statuscode.SmsPlatformUnavailable
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -137,27 +153,41 @@ func (e *SmsPhone) DeleteMyNumber(req *dto.DeleteMyNumberReq, userId int) int {
 | 
				
			|||||||
func (e *SmsPhone) WeakUp(req *dto.WeakUpReq, userId int, defult bool) (dto.WeakUpResp, int) {
 | 
					func (e *SmsPhone) WeakUp(req *dto.WeakUpReq, userId int, defult bool) (dto.WeakUpResp, int) {
 | 
				
			||||||
	smsPhone := models.SmsPhone{}
 | 
						smsPhone := models.SmsPhone{}
 | 
				
			||||||
	result := dto.WeakUpResp{}
 | 
						result := dto.WeakUpResp{}
 | 
				
			||||||
	if err := e.Orm.Model(smsPhone).Where("activation_id =? and user_id= ?", req.ActivationId, userId).First(&smsPhone).Error; err != nil {
 | 
						if err := e.Orm.Model(smsPhone).
 | 
				
			||||||
 | 
							Where("activation_id =? and user_id= ?", req.ActivationId, userId).
 | 
				
			||||||
 | 
							First(&smsPhone).Error; err != nil {
 | 
				
			||||||
		e.Log.Errorf("获取短信号码失败, %s", err)
 | 
							e.Log.Errorf("获取短信号码失败, %s", err)
 | 
				
			||||||
		return result, statuscode.ServerError
 | 
							return result, statuscode.ServerError
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if smsPhone.Status != 1 || defult {
 | 
						if smsPhone.Status != 1 || defult {
 | 
				
			||||||
		newActivationId, messageId, code := e.WeakUpManage(&smsPhone)
 | 
							smsPlatformKeyRedis := NewSmsPlatformKeyRedis(e.Orm, e.Log)
 | 
				
			||||||
 | 
							apiInfo, err := smsPlatformKeyRedis.GetApiInfo(smsPhone.PlatformCode, smsPhone.ApiKey)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								e.Log.Errorf("获取平台密钥失败, %s", err)
 | 
				
			||||||
 | 
								return result, statuscode.ServerError
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							newActivationId, messageId, startTime, endTime, code := e.WeakUpManage(&smsPhone, &apiInfo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if code == statuscode.ServerError {
 | 
							if code == statuscode.ServerError {
 | 
				
			||||||
			return result, code
 | 
								return result, code
 | 
				
			||||||
		} else if code == statuscode.Success {
 | 
							} else if code == statuscode.Success {
 | 
				
			||||||
			if err := e.Orm.Model(smsPhone).Updates(map[string]interface{}{"new_activation_id": newActivationId, "status": 1, "code": "", "message_id": messageId}).Error; err != nil {
 | 
								if err := e.Orm.Model(smsPhone).
 | 
				
			||||||
 | 
									Updates(map[string]interface{}{
 | 
				
			||||||
 | 
										"new_activation_id": newActivationId, "status": 1, "code": "",
 | 
				
			||||||
 | 
										"message_id": messageId, "start_time": startTime, "end_time": endTime}).Error; err != nil {
 | 
				
			||||||
				e.Log.Errorf("更新短信号码失败, %s", err)
 | 
									e.Log.Errorf("更新短信号码失败, %s", err)
 | 
				
			||||||
				return result, statuscode.ServerError
 | 
									return result, statuscode.ServerError
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			result.ActivationId = newActivationId
 | 
								result.ActivationId = newActivationId
 | 
				
			||||||
			result.MessageId = messageId
 | 
								result.MessageId = messageId
 | 
				
			||||||
 | 
								result.StartTime = startTime
 | 
				
			||||||
 | 
								result.EndTime = endTime
 | 
				
			||||||
		} else if code == statuscode.SmsLongNumWaitCode {
 | 
							} else if code == statuscode.SmsLongNumWaitCode {
 | 
				
			||||||
			e.Log.Info("无须唤醒")
 | 
								if err := e.Orm.Model(smsPhone).
 | 
				
			||||||
			if err := e.Orm.Model(smsPhone).Updates(map[string]interface{}{"status": 1, "code": ""}).Error; err != nil {
 | 
									Updates(map[string]interface{}{"status": 1, "code": "", "start_time": startTime, "end_time": endTime}).
 | 
				
			||||||
 | 
									Error; err != nil {
 | 
				
			||||||
				e.Log.Errorf("更新短信号码失败, %s", err)
 | 
									e.Log.Errorf("更新短信号码失败, %s", err)
 | 
				
			||||||
				return result, statuscode.ServerError
 | 
									return result, statuscode.ServerError
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@ -171,18 +201,18 @@ func (e *SmsPhone) WeakUp(req *dto.WeakUpReq, userId int, defult bool) (dto.Weak
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// 唤醒号码
 | 
					// 唤醒号码
 | 
				
			||||||
// returns newActivationId,messageId, code
 | 
					// returns newActivationId,messageId, code
 | 
				
			||||||
func (e *SmsPhone) WeakUpManage(req *models.SmsPhone) (string, string, int) {
 | 
					func (e *SmsPhone) WeakUpManage(req *models.SmsPhone, apiInfo *dto.SmsPlatformKeyQueueDto) (string, string, *time.Time, *time.Time, int) {
 | 
				
			||||||
	switch req.PlatformCode {
 | 
						switch req.PlatformCode {
 | 
				
			||||||
	case global.SmsPlatformDaisysms:
 | 
						case global.SmsPlatformDaisysms:
 | 
				
			||||||
		service := SmsDaisysms{Service: e.Service}
 | 
							service := SmsDaisysms{Service: e.Service}
 | 
				
			||||||
		id, code := service.getExtraActivation(req.ActivationId)
 | 
							id, code := service.getExtraActivation(req.ActivationId, apiInfo)
 | 
				
			||||||
		return strconv.Itoa(id), "", code
 | 
							return strconv.Itoa(id), "", nil, nil, code
 | 
				
			||||||
	case global.SmsPlatformTextVerified:
 | 
						case global.SmsPlatformTextVerified:
 | 
				
			||||||
		service := SmsTextVerified{Service: e.Service}
 | 
							service := SmsTextVerified{Service: e.Service}
 | 
				
			||||||
 | 
							resp, code := service.getExtraActivation(req.NewActivationId, apiInfo, 0)
 | 
				
			||||||
		return service.getExtraActivation(req.NewActivationId)
 | 
							return resp.ReservationId, resp.Id, resp.UsageWindowStart, resp.UsageWindowEnd, code
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		return "", "", statuscode.SmsPlatformUnavailable
 | 
							return "", "", nil, nil, statuscode.SmsPlatformUnavailable
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -201,6 +231,17 @@ func (e *SmsPhone) GetMyPage(req *dto.SmsPhoneGetPageReq, userId int, phone *[]m
 | 
				
			|||||||
		return statuscode.ServerError
 | 
							return statuscode.ServerError
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dictService := SysDictData{}
 | 
				
			||||||
 | 
						dictService.Orm = e.Orm
 | 
				
			||||||
 | 
						dictService.Log = e.Log
 | 
				
			||||||
 | 
						dictDatas, _ := dictService.GetMapByType("sms_platform")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for index := range *phone {
 | 
				
			||||||
 | 
							if dict, ok := dictDatas[(*phone)[index].PlatformCode]; ok {
 | 
				
			||||||
 | 
								(*phone)[index].PlatformName = dict.DictLabel
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return statuscode.Success
 | 
						return statuscode.Success
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -281,7 +322,7 @@ func (e *SmsPhone) DoGetNumber(balanceService *MemberBalance, req *dto.GetNumber
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	now := time.Now()
 | 
						now := time.Now()
 | 
				
			||||||
	activationId, phone, code, expireTime := e.GetNumberManage(req.PlatformCode, req.Type, req.ServiceCode, price, req.Period)
 | 
						numberResp, code, apiInfo := e.GetNumberManage(req.PlatformCode, req.Type, req.ServiceCode, price, req.Period)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if code != statuscode.Success {
 | 
						if code != statuscode.Success {
 | 
				
			||||||
		return decimal.Decimal{}, models.SmsPhone{}, code
 | 
							return decimal.Decimal{}, models.SmsPhone{}, code
 | 
				
			||||||
@ -289,30 +330,38 @@ func (e *SmsPhone) DoGetNumber(balanceService *MemberBalance, req *dto.GetNumber
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	smsPhone := models.SmsPhone{}
 | 
						smsPhone := models.SmsPhone{}
 | 
				
			||||||
	smsPhone.PlatformCode = req.PlatformCode
 | 
						smsPhone.PlatformCode = req.PlatformCode
 | 
				
			||||||
	smsPhone.Phone = phone
 | 
						smsPhone.Phone = numberResp.Phone
 | 
				
			||||||
	smsPhone.UserId = userId
 | 
						smsPhone.UserId = userId
 | 
				
			||||||
	smsPhone.Service = serviceItem.Name
 | 
						smsPhone.Service = serviceItem.Name
 | 
				
			||||||
	smsPhone.ServiceCode = req.ServiceCode
 | 
						smsPhone.ServiceCode = req.ServiceCode
 | 
				
			||||||
	smsPhone.Type = req.Type
 | 
						smsPhone.Type = req.Type
 | 
				
			||||||
	smsPhone.Period = req.Period
 | 
						smsPhone.Period = req.Period
 | 
				
			||||||
	smsPhone.ActivationId = activationId
 | 
						smsPhone.ActivationId = numberResp.ActivationId
 | 
				
			||||||
	smsPhone.MessageId = activationId
 | 
						smsPhone.MessageId = numberResp.ActivationId
 | 
				
			||||||
	smsPhone.NewActivationId = activationId
 | 
						smsPhone.NewActivationId = numberResp.ActivationId
 | 
				
			||||||
	smsPhone.Actived = 1
 | 
						smsPhone.Actived = 1
 | 
				
			||||||
 | 
						smsPhone.StartTime = numberResp.StartTime
 | 
				
			||||||
 | 
						smsPhone.EndTime = numberResp.EndTime
 | 
				
			||||||
 | 
						smsPhone.ApiKey = apiInfo.ApiKey
 | 
				
			||||||
 | 
						smsPhone.BillingCycleId = numberResp.BillingCycleId
 | 
				
			||||||
	smsPhone.Price = price
 | 
						smsPhone.Price = price
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if req.Type == 1 {
 | 
						if req.Type == 1 {
 | 
				
			||||||
		if expireTime != nil {
 | 
							if numberResp.ExpireTime != nil {
 | 
				
			||||||
			smsPhone.ExpireTime = expireTime
 | 
								smsPhone.ExpireTime = numberResp.ExpireTime
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			days := req.Period * 30
 | 
								days := req.Period * 30
 | 
				
			||||||
			now = now.AddDate(0, 0, days)
 | 
								now = now.AddDate(0, 0, days)
 | 
				
			||||||
			smsPhone.ExpireTime = &now
 | 
								smsPhone.ExpireTime = &now
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							//平台不允许取消
 | 
				
			||||||
 | 
							if req.PlatformCode == global.SmsPlatformTextVerified {
 | 
				
			||||||
 | 
								smsPhone.Actived = 2
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		if expireTime != nil {
 | 
							if numberResp.ExpireTime != nil {
 | 
				
			||||||
			smsPhone.ExpireTime = expireTime
 | 
								smsPhone.ExpireTime = numberResp.ExpireTime
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			now = now.Add(time.Minute * time.Duration(serviceItem.ExpirationMinutes))
 | 
								now = now.Add(time.Minute * time.Duration(serviceItem.ExpirationMinutes))
 | 
				
			||||||
			smsPhone.ExpireTime = &now
 | 
								smsPhone.ExpireTime = &now
 | 
				
			||||||
@ -336,7 +385,7 @@ func (e *SmsPhone) DoGetNumber(balanceService *MemberBalance, req *dto.GetNumber
 | 
				
			|||||||
	if req.Type == 1 {
 | 
						if req.Type == 1 {
 | 
				
			||||||
		var code int
 | 
							var code int
 | 
				
			||||||
		if req.PlatformCode == global.SmsPlatformDaisysms {
 | 
							if req.PlatformCode == global.SmsPlatformDaisysms {
 | 
				
			||||||
			code = e.ChangeAutoRenewManage(smsPhone.PlatformCode, smsPhone.NewActivationId, true)
 | 
								code = e.ChangeAutoRenewManage(smsPhone.PlatformCode, smsPhone.ApiKey, smsPhone.NewActivationId, true)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			code = http.StatusOK
 | 
								code = http.StatusOK
 | 
				
			||||||
			e.WeakUp(&dto.WeakUpReq{ActivationId: smsPhone.NewActivationId}, userId, true)
 | 
								e.WeakUp(&dto.WeakUpReq{ActivationId: smsPhone.NewActivationId}, userId, true)
 | 
				
			||||||
@ -351,28 +400,72 @@ func (e *SmsPhone) DoGetNumber(balanceService *MemberBalance, req *dto.GetNumber
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						log := models.SmsRenewalLog{}
 | 
				
			||||||
 | 
						log.PhoneId = smsPhone.Id
 | 
				
			||||||
 | 
						log.PayOrderNo = utility.GenerateTraceID()
 | 
				
			||||||
 | 
						log.Phone = smsPhone.Phone
 | 
				
			||||||
 | 
						log.Type = smsPhone.Type
 | 
				
			||||||
 | 
						log.Category = 1
 | 
				
			||||||
 | 
						log.UserId = userId
 | 
				
			||||||
 | 
						log.Amount = price
 | 
				
			||||||
 | 
						log.BeforeTime = time.Now()
 | 
				
			||||||
 | 
						log.Period = req.Period * 30
 | 
				
			||||||
 | 
						log.Status = 2
 | 
				
			||||||
 | 
						log.TargetTime = smsPhone.ExpireTime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := e.Orm.Save(&log).Error; err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("保存短信续费日志失败,phone_id:%d, %s", smsPhone.Id, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return balance, smsPhone, statuscode.Success
 | 
						return balance, smsPhone, statuscode.Success
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 租赁号码
 | 
					// 租赁号码
 | 
				
			||||||
func (e *SmsPhone) GetNumberManage(platformCode string, typ int, serviceCode string, price decimal.Decimal, period int) (string, string, int, *time.Time) {
 | 
					// return activationId 激活id
 | 
				
			||||||
 | 
					// phone 号码
 | 
				
			||||||
 | 
					// code 状态码
 | 
				
			||||||
 | 
					// *expirateTime 号码过期时间
 | 
				
			||||||
 | 
					// *startTime 长效号码单次接码开始使用(textverified有用)
 | 
				
			||||||
 | 
					// *endTime 长效号码单次接码过期时间(textverified有用)
 | 
				
			||||||
 | 
					func (e *SmsPhone) GetNumberManage(platformCode string, typ int, serviceCode string,
 | 
				
			||||||
 | 
						price decimal.Decimal, period int) (dto.SmsPhoneGetNumberResp, int, *dto.SmsPlatformKeyQueueDto) {
 | 
				
			||||||
 | 
						smsPlatformKeyRedis := NewSmsPlatformKeyRedis(e.Orm, e.Log)
 | 
				
			||||||
 | 
						queue, err := smsPlatformKeyRedis.GetRoundRobinKey(platformCode)
 | 
				
			||||||
 | 
						result := dto.SmsPhoneGetNumberResp{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("获取短信平台队列失败, %s", err)
 | 
				
			||||||
 | 
							return result, statuscode.ServerError, queue
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch platformCode {
 | 
						switch platformCode {
 | 
				
			||||||
	case global.SmsPlatformDaisysms:
 | 
						case global.SmsPlatformDaisysms:
 | 
				
			||||||
		service := SmsDaisysms{Service: e.Service}
 | 
							service := SmsDaisysms{Service: e.Service}
 | 
				
			||||||
		activationId, phone, code := service.GetNumberForApi(typ, serviceCode, price, period)
 | 
							activationId, phone, code := service.GetNumberForApi(queue, typ, serviceCode, price, period)
 | 
				
			||||||
 | 
							result.ActivationId = strconv.Itoa(activationId)
 | 
				
			||||||
 | 
							result.Phone = phone
 | 
				
			||||||
 | 
							result.ExpireTime = nil
 | 
				
			||||||
 | 
							result.StartTime = nil
 | 
				
			||||||
 | 
							result.EndTime = nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return strconv.Itoa(activationId), phone, code, nil
 | 
							return result, code, queue
 | 
				
			||||||
	case global.SmsPlatformTextVerified:
 | 
						case global.SmsPlatformTextVerified:
 | 
				
			||||||
		service := SmsTextVerified{Service: e.Service}
 | 
							service := SmsTextVerified{Service: e.Service}
 | 
				
			||||||
		resp, code := service.GetNumberForApi(typ, serviceCode, price, period)
 | 
							resp, code := service.GetNumberAndWakeUp(typ, serviceCode, price, period, queue)
 | 
				
			||||||
 | 
							result.ActivationId = resp.Id
 | 
				
			||||||
 | 
							result.Phone = resp.Phone
 | 
				
			||||||
 | 
							result.ExpireTime = resp.EndAt
 | 
				
			||||||
 | 
							result.StartTime = resp.StartTime
 | 
				
			||||||
 | 
							result.EndTime = resp.EndTime
 | 
				
			||||||
 | 
							result.BillingCycleId = resp.BillingCycleId
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if code != statuscode.Success {
 | 
							if code != statuscode.Success {
 | 
				
			||||||
			return "", "", code, nil
 | 
								return result, code, queue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return resp.Id, resp.Phone, code, resp.EndAt
 | 
							return result, code, queue
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		return "", "", statuscode.SmsPlatformUnavailable, nil
 | 
							return result, statuscode.SmsPlatformUnavailable, queue
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -414,13 +507,38 @@ func (e *SmsPhone) SyncCodes() error {
 | 
				
			|||||||
	if err := e.Orm.Model(models.SmsPhone{}).Where("status =1").Find(&phones).Error; err != nil {
 | 
						if err := e.Orm.Model(models.SmsPhone{}).Where("status =1").Find(&phones).Error; err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						mapData := make(map[string]*dto.SmsPlatformKeyQueueDto)
 | 
				
			||||||
 | 
						smsPlatformKeyRedis := NewSmsPlatformKeyRedis(e.Orm, e.Log)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, item := range phones {
 | 
						for _, item := range phones {
 | 
				
			||||||
		code, codeStatus := e.GetCodeManage(item.PlatformCode, item.NewActivationId, item.Type, item.UpdatedAt.Unix())
 | 
							key := fmt.Sprintf("%s:%s", item.PlatformCode, item.ApiKey)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if _, ok := mapData[key]; !ok {
 | 
				
			||||||
 | 
								apiInfo, _ := smsPlatformKeyRedis.GetApiInfo(item.PlatformCode, item.ApiKey)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if apiInfo.ApiKey != "" {
 | 
				
			||||||
 | 
									mapData[key] = &apiInfo
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, item := range phones {
 | 
				
			||||||
 | 
							apiInfo, ok := mapData[fmt.Sprintf("%s:%s", item.PlatformCode, item.ApiKey)]
 | 
				
			||||||
 | 
							if !ok {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							code, codeStatus := e.GetCodeManage(item.PlatformCode, item.NewActivationId,
 | 
				
			||||||
 | 
								item.Type, item.UserId, item.Service, item.ServiceCode, apiInfo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if code == "" {
 | 
							if code == "" {
 | 
				
			||||||
			expireTime := item.UpdatedAt.Add(time.Second * 150)
 | 
								var expireTime time.Time
 | 
				
			||||||
			if item.PlatformCode == global.SmsPlatformTextVerified && expireTime.Before(time.Now()) {
 | 
					
 | 
				
			||||||
 | 
								if item.EndTime != nil {
 | 
				
			||||||
 | 
									expireTime = *item.EndTime
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (item.Status == 3 && item.Actived == 2) || (item.PlatformCode == global.SmsPlatformTextVerified && expireTime.Before(time.Now())) {
 | 
				
			||||||
				if err := e.Orm.Model(&item).Updates(map[string]interface{}{"status": 3, "updated_at": time.Now()}).Error; err != nil {
 | 
									if err := e.Orm.Model(&item).Updates(map[string]interface{}{"status": 3, "updated_at": time.Now()}).Error; err != nil {
 | 
				
			||||||
					e.Log.Errorf("手机号一睡眠 %s", item.Phone)
 | 
										e.Log.Errorf("手机号一睡眠 %s", item.Phone)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
@ -473,106 +591,56 @@ func (e *SmsPhone) SyncCodes() error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 获取验证码
 | 
					// 获取验证码
 | 
				
			||||||
func (e *SmsPhone) GetCodeManage(platformCode string, messageId string, typ int, unixTime int64) (string, int) {
 | 
					func (e *SmsPhone) GetCodeManage(platformCode string, messageId string, typ int, userId int, smsService, serviceCode string, apiInfo *dto.SmsPlatformKeyQueueDto) (string, int) {
 | 
				
			||||||
	switch platformCode {
 | 
						switch platformCode {
 | 
				
			||||||
	case global.SmsPlatformDaisysms:
 | 
						case global.SmsPlatformDaisysms:
 | 
				
			||||||
		service := SmsDaisysms{Service: e.Service}
 | 
							service := SmsDaisysms{Service: e.Service}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return service.GetCodeForApi(messageId)
 | 
							return service.GetCodeForApi(messageId, apiInfo)
 | 
				
			||||||
	case global.SmsPlatformTextVerified:
 | 
						case global.SmsPlatformTextVerified:
 | 
				
			||||||
		service := SmsTextVerified{Service: e.Service}
 | 
							service := SmsTextVerified{Service: e.Service}
 | 
				
			||||||
		return service.GetCode(messageId, typ, unixTime)
 | 
							return service.GetCode(messageId, typ, userId, smsService, serviceCode, apiInfo)
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		return "", statuscode.SmsPlatformUnavailable
 | 
							return "", statuscode.SmsPlatformUnavailable
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 自动续期
 | 
					 | 
				
			||||||
func (e SmsServices) AutoRenewal() error {
 | 
					 | 
				
			||||||
	var datas []models.SmsPhone
 | 
					 | 
				
			||||||
	var data models.SmsPhone
 | 
					 | 
				
			||||||
	startTime := time.Now().Add(-24 * time.Hour)
 | 
					 | 
				
			||||||
	endTime := time.Now().Add(24 * time.Hour)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	smsServices := SmsServices{Service: e.Service}
 | 
					 | 
				
			||||||
	serviceMap := smsServices.GetMapAll()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	configService := SysConfig{Service: e.Service}
 | 
					 | 
				
			||||||
	mapDatas, _ := configService.GetMapByKeys([]string{"renew_number_premium_daisysms", "renew_number_premium_textverified"})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err := e.Orm.Model(&models.SmsPhone{}).Where("auto_renewal =1 and type =1 and actived =2 and expire_time >=? and expire_time <?", startTime, endTime).Find(&datas).Error; err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for _, item := range datas {
 | 
					 | 
				
			||||||
		percent := decimal.NewFromInt(1)
 | 
					 | 
				
			||||||
		price := decimal.Zero
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if service, ok := serviceMap[item.PlatformCode+"_"+item.ServiceCode]; !ok {
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			price = service.LongPrice
 | 
					 | 
				
			||||||
			switch {
 | 
					 | 
				
			||||||
			case item.PlatformCode == global.SmsPlatformDaisysms:
 | 
					 | 
				
			||||||
				p, ok := mapDatas["renew_number_premium_daisysms"]
 | 
					 | 
				
			||||||
				val, _ := decimal.NewFromString(p.ConfigValue)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				if ok && val.Cmp(decimal.Zero) > 0 {
 | 
					 | 
				
			||||||
					per := decimal.NewFromInt(100).Add(val)
 | 
					 | 
				
			||||||
					percent = per.Div(decimal.NewFromInt(100)).Truncate(2)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			case item.PlatformCode == global.SmsPlatformTextVerified:
 | 
					 | 
				
			||||||
				p, ok := mapDatas["renew_number_premium_textverified"]
 | 
					 | 
				
			||||||
				val, _ := decimal.NewFromString(p.ConfigValue)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				if ok && val.Cmp(decimal.Zero) > 0 {
 | 
					 | 
				
			||||||
					per := decimal.NewFromInt(100).Add(val)
 | 
					 | 
				
			||||||
					percent = per.Div(decimal.NewFromInt(100)).Truncate(2)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			renewLog := models.SmsRenewalLog{}
 | 
					 | 
				
			||||||
			renewLog.UserId = item.UserId
 | 
					 | 
				
			||||||
			renewLog.PhoneId = item.Id
 | 
					 | 
				
			||||||
			renewLog.Type = item.Type
 | 
					 | 
				
			||||||
			renewLog.Amount = price.Mul(percent).Truncate(2)
 | 
					 | 
				
			||||||
			renewLog.BeforeTime = *item.ExpireTime
 | 
					 | 
				
			||||||
			renewLog.Period = 30
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if err := e.Orm.Transaction(func(tx *gorm.DB) error {
 | 
					 | 
				
			||||||
				db := tx.Exec("UPDATE member_balance set balance = balance - ? where user_id = ? and balance >= ?", price, item.UserId, price)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				if db.Error != nil {
 | 
					 | 
				
			||||||
					return db.Error
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				if db.RowsAffected == 0 {
 | 
					 | 
				
			||||||
					return errors.New("余额不足")
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				if err1 := tx.Create(&renewLog).Error; err1 != nil {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					return errors.New("创建续费日志失败")
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				newExpireTime := item.ExpireTime.AddDate(0, 0, 30)
 | 
					 | 
				
			||||||
				if err1 := tx.Model(data).Where("id =?", item.Id).Update("expire_time", newExpireTime).Error; err1 != nil {
 | 
					 | 
				
			||||||
					return err1
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				return nil
 | 
					 | 
				
			||||||
			}); err != nil {
 | 
					 | 
				
			||||||
				e.Log.Errorf("自动续期失败:%s", err.Error())
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ChangeAutoRenew 修改自动续期
 | 
					// ChangeAutoRenew 修改自动续期
 | 
				
			||||||
func (e *SmsPhone) ChangeAutoRenew(req *dto.SmsPhoneChangeAutoRenewReq, userId int) int {
 | 
					func (e *SmsPhone) ChangeAutoRenew(req *dto.SmsPhoneChangeAutoRenewReq, userId int) int {
 | 
				
			||||||
	var data models.SmsPhone
 | 
						var data models.SmsPhone
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if req.AutoRenew == 1 {
 | 
				
			||||||
 | 
							smsServices := SmsServices{Service: e.Service}
 | 
				
			||||||
 | 
							balanceService := MemberBalance{Service: e.Service}
 | 
				
			||||||
 | 
							balance := balanceService.GetBalance(userId)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if err := e.Orm.Model(data).Where("id =? or activation_id =?", req.Id, req.ActivationId).First(&data).Error; err != nil {
 | 
				
			||||||
 | 
								e.Log.Errorf("修改自动续期 数据不存在 id:%d err:%s", req.Id, err.Error())
 | 
				
			||||||
 | 
								return statuscode.SmsNotExisted
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if data.ExpireTime.Before(time.Now()) {
 | 
				
			||||||
 | 
								return statuscode.SmsRentalCantRenew
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							serviceItem, err := smsServices.GetByCode(data.PlatformCode, data.ServiceCode)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								e.Log.Errorf("短信服务报错:%v", err)
 | 
				
			||||||
 | 
								return statuscode.ServerError
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							configService := SysConfig{Service: e.Service}
 | 
				
			||||||
 | 
							configKeys := []string{"renew_number_premium_daisysms", "renew_number_premium_textverified"}
 | 
				
			||||||
 | 
							premiumMap, _ := configService.GetMapByKeys(configKeys)
 | 
				
			||||||
 | 
							renewalService := SmsRenewalLog{Service: e.Service}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							price := renewalService.calculateRenewalPrice(serviceItem.LongPrice, serviceItem.PlatformCode, premiumMap)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if balance.Cmp(price) < 0 {
 | 
				
			||||||
 | 
								return statuscode.BalanceNotEnough
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if req.Id > 0 {
 | 
						if req.Id > 0 {
 | 
				
			||||||
		if err := e.Orm.Model(data).Where("id =? and user_id =?", req.Id, userId).First(&data).Error; err != nil {
 | 
							if err := e.Orm.Model(data).Where("id =? and user_id =?", req.Id, userId).First(&data).Error; err != nil {
 | 
				
			||||||
			e.Log.Errorf("修改自动续期 数据不存在 id:%d err:%s", req.Id, err.Error())
 | 
								e.Log.Errorf("修改自动续期 数据不存在 id:%d err:%s", req.Id, err.Error())
 | 
				
			||||||
@ -615,7 +683,7 @@ func (e *SmsPhone) DoChangeAutoRenewal(data models.SmsPhone, autoRenewal int) (b
 | 
				
			|||||||
	if data.Actived == 3 {
 | 
						if data.Actived == 3 {
 | 
				
			||||||
		code = http.StatusOK
 | 
							code = http.StatusOK
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		code = e.ChangeAutoRenewManage(data.PlatformCode, data.ActivationId, status)
 | 
							code = e.ChangeAutoRenewManage(data.PlatformCode, data.ApiKey, data.ActivationId, status)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if code != statuscode.Success {
 | 
							if code != statuscode.Success {
 | 
				
			||||||
			return true, statuscode.ServerError
 | 
								return true, statuscode.ServerError
 | 
				
			||||||
@ -629,16 +697,24 @@ func (e *SmsPhone) DoChangeAutoRenewal(data models.SmsPhone, autoRenewal int) (b
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ChangeAutoRenewManage 修改自动续期管理
 | 
					// ChangeAutoRenewManage 修改自动续期管理
 | 
				
			||||||
func (e *SmsPhone) ChangeAutoRenewManage(platform string, activationId string, status bool) int {
 | 
					func (e *SmsPhone) ChangeAutoRenewManage(platform, apiKey string, activationId string, status bool) int {
 | 
				
			||||||
 | 
						smsPlatformKeyRedis := NewSmsPlatformKeyRedis(e.Orm, e.Log)
 | 
				
			||||||
 | 
						apiInfo, err := smsPlatformKeyRedis.GetApiInfo(platform, apiKey)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("获取平台密钥失败: %v", err)
 | 
				
			||||||
 | 
							return statuscode.ServerError
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch platform {
 | 
						switch platform {
 | 
				
			||||||
	case global.SmsPlatformDaisysms:
 | 
						case global.SmsPlatformDaisysms:
 | 
				
			||||||
		service := SmsDaisysms{Service: e.Service}
 | 
							service := SmsDaisysms{Service: e.Service}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return service.ChangeAutoRenewForApi(activationId, status)
 | 
							return service.ChangeAutoRenewForApi(activationId, status, &apiInfo)
 | 
				
			||||||
	case global.SmsPlatformTextVerified:
 | 
						case global.SmsPlatformTextVerified:
 | 
				
			||||||
		service := SmsTextVerified{Service: e.Service}
 | 
							service := SmsTextVerified{Service: e.Service}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return service.Renew(activationId, status)
 | 
							return service.Renew(activationId, status, &apiInfo)
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		return statuscode.SmsPlatformUnavailable
 | 
							return statuscode.SmsPlatformUnavailable
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -649,12 +725,20 @@ func (e *SmsPhone) GetPage(c *dto.SmsPhoneGetPageReq, p *actions.DataPermission,
 | 
				
			|||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	var data models.SmsPhone
 | 
						var data models.SmsPhone
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = e.Orm.Model(&data).
 | 
						query := e.Orm.Model(&data).
 | 
				
			||||||
		Scopes(
 | 
							Scopes(
 | 
				
			||||||
			cDto.MakeCondition(c.GetNeedSearch()),
 | 
								cDto.MakeCondition(c.GetNeedSearch()),
 | 
				
			||||||
			cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
 | 
								cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
 | 
				
			||||||
			actions.Permission(data.TableName(), p),
 | 
								actions.Permission(data.TableName(), p),
 | 
				
			||||||
		).
 | 
							)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch c.IsActived {
 | 
				
			||||||
 | 
						case 1:
 | 
				
			||||||
 | 
							query = query.Where("expire_time > ?", time.Now())
 | 
				
			||||||
 | 
						case 2:
 | 
				
			||||||
 | 
							query = query.Where("expire_time < ?", time.Now())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						err = query.
 | 
				
			||||||
		Find(list).Limit(-1).Offset(-1).
 | 
							Find(list).Limit(-1).Offset(-1).
 | 
				
			||||||
		Count(count).Error
 | 
							Count(count).Error
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@ -767,3 +851,71 @@ func (e *SmsPhone) DeleteExpired() error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 同步手动续费id
 | 
				
			||||||
 | 
					func (e *SmsPhone) SyncBilingCycleId() error {
 | 
				
			||||||
 | 
						var logs []models.SmsPhone
 | 
				
			||||||
 | 
						logMap := map[string]models.SmsPhone{}
 | 
				
			||||||
 | 
						if err := e.Orm.Model(&models.SmsPhone{}).Where("billing_cycle_id = '' or billing_cycle_id is null").Find(&logs).Error; err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("查询手动续费id为空的记录失败 err:%v", err)
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, item := range logs {
 | 
				
			||||||
 | 
							logMap[item.ActivationId] = item
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						textVerifiedService := SmsTextVerified{Service: e.Service}
 | 
				
			||||||
 | 
						smsPlatformKeyRedis := NewSmsPlatformKeyRedis(e.Orm, e.Log)
 | 
				
			||||||
 | 
						apiInfo, err := smsPlatformKeyRedis.GetApiInfo(global.SmsPlatformTextVerified, "ZQ0swXnsaPpeGdwa3c7gT9U9I1Oh9WoDHx0amuYovvaHuqd5u6B4NBBUSUBjR")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("获取平台密钥失败: %v", err)
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err = e.syncLoop("", textVerifiedService, apiInfo, logMap)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *SmsPhone) syncLoop(nextUrl string, textVerifiedService SmsTextVerified, apiInfo dto.SmsPlatformKeyQueueDto, logMap map[string]models.SmsPhone) (dto.TextVerifiedGetRentalListResp, error) {
 | 
				
			||||||
 | 
						if len(logMap) == 0 {
 | 
				
			||||||
 | 
							return dto.TextVerifiedGetRentalListResp{}, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rentalList, err := textVerifiedService.GetRentalList(nextUrl, &apiInfo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("获取平台密钥失败: %v", err)
 | 
				
			||||||
 | 
							return dto.TextVerifiedGetRentalListResp{}, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, log := range rentalList.Data {
 | 
				
			||||||
 | 
							if log.BillingCycleId == "" {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if item, ok := logMap[log.ID]; ok {
 | 
				
			||||||
 | 
								if err1 := e.Orm.Model(&models.SmsPhone{}).Where("activation_id = ?", item.ActivationId).Update("billing_cycle_id", log.BillingCycleId).Error; err1 != nil {
 | 
				
			||||||
 | 
									e.Log.Errorf("更新手动续费id失败 err:%v", err1)
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								delete(logMap, item.ActivationId)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if rentalList.HasNext {
 | 
				
			||||||
 | 
							time.Sleep(150 * time.Millisecond)
 | 
				
			||||||
 | 
							rentalList, err = e.syncLoop(rentalList.Links.Next.Href, textVerifiedService, apiInfo, logMap)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								e.Log.Errorf("同步续费id失败 err:%v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return rentalList, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										1
									
								
								app/admin/service/sms_phone_extend.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								app/admin/service/sms_phone_extend.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					package service
 | 
				
			||||||
@ -4,6 +4,7 @@ import (
 | 
				
			|||||||
	"go-admin/app/admin/service/dto"
 | 
						"go-admin/app/admin/service/dto"
 | 
				
			||||||
	"go-admin/common/global"
 | 
						"go-admin/common/global"
 | 
				
			||||||
	"go-admin/common/statuscode"
 | 
						"go-admin/common/statuscode"
 | 
				
			||||||
 | 
						"go-admin/utils/utility"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/go-admin-team/go-admin-core/logger"
 | 
						"github.com/go-admin-team/go-admin-core/logger"
 | 
				
			||||||
@ -63,3 +64,31 @@ func TestGetPrices(t *testing.T) {
 | 
				
			|||||||
		t.Error(err)
 | 
							t.Error(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestSyncRenewalLogs(t *testing.T) {
 | 
				
			||||||
 | 
						db := initSetting()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						service := SmsPhone{}
 | 
				
			||||||
 | 
						service.Orm = db
 | 
				
			||||||
 | 
						service.Log = logger.NewHelper(logger.DefaultLogger)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := service.SyncBilingCycleId(); err != nil {
 | 
				
			||||||
 | 
							t.Error(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestManualRenewal(t *testing.T) {
 | 
				
			||||||
 | 
						db := initSetting()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						service := SmsRenewalLog{}
 | 
				
			||||||
 | 
						service.Orm = db
 | 
				
			||||||
 | 
						service.Log = logger.NewHelper(logger.DefaultLogger)
 | 
				
			||||||
 | 
						activationId := "lr_01K5DP71G06SFX84S7W99D61F9"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, err := service.ManualDeduct(&dto.ManualDeductReq{
 | 
				
			||||||
 | 
							ActivationId: activationId,
 | 
				
			||||||
 | 
							TradeOrderNo: utility.GenerateTraceID(),
 | 
				
			||||||
 | 
						}, 1); err != statuscode.Success {
 | 
				
			||||||
 | 
							t.Error(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										314
									
								
								app/admin/service/sms_platform_key.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										314
									
								
								app/admin/service/sms_platform_key.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,314 @@
 | 
				
			|||||||
 | 
					package service
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/go-admin-team/go-admin-core/sdk/service"
 | 
				
			||||||
 | 
						"gorm.io/gorm"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"go-admin/app/admin/models"
 | 
				
			||||||
 | 
						"go-admin/app/admin/service/dto"
 | 
				
			||||||
 | 
						"go-admin/common/actions"
 | 
				
			||||||
 | 
						cDto "go-admin/common/dto"
 | 
				
			||||||
 | 
						"go-admin/utils/utility"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SmsPlatformKey struct {
 | 
				
			||||||
 | 
						service.Service
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetPage 获取SmsPlatformKey列表
 | 
				
			||||||
 | 
					func (e *SmsPlatformKey) GetPage(c *dto.SmsPlatformKeyGetPageReq, p *actions.DataPermission, list *[]models.SmsPlatformKey, count *int64) error {
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
						var data models.SmsPlatformKey
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = e.Orm.Model(&data).
 | 
				
			||||||
 | 
							Scopes(
 | 
				
			||||||
 | 
								cDto.MakeCondition(c.GetNeedSearch()),
 | 
				
			||||||
 | 
								cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
 | 
				
			||||||
 | 
								actions.Permission(data.TableName(), p),
 | 
				
			||||||
 | 
							).
 | 
				
			||||||
 | 
							Find(list).Limit(-1).Offset(-1).
 | 
				
			||||||
 | 
							Count(count).Error
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("SmsPlatformKeyService GetPage error:%s \r\n", err)
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for index := range *list {
 | 
				
			||||||
 | 
							(*list)[index].ApiKey = utility.DesensitizeGeneric((*list)[index].ApiKey, 3, 3, '*')
 | 
				
			||||||
 | 
							(*list)[index].ApiSecret = utility.DesensitizeGeneric((*list)[index].ApiSecret, 3, 3, '*')
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetRandomKey 随机获取一个密钥
 | 
				
			||||||
 | 
					func (e *SmsPlatformKey) GetRandomKey(platformCode string) (*dto.SmsPlatformKeyQueueDto, error) {
 | 
				
			||||||
 | 
						redisService := NewSmsPlatformKeyRedis(e.Orm, e.Log)
 | 
				
			||||||
 | 
						return redisService.GetRandomKey(platformCode)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetRoundRobinKey 轮询获取密钥
 | 
				
			||||||
 | 
					func (e *SmsPlatformKey) GetRoundRobinKey(platformCode string) (*dto.SmsPlatformKeyQueueDto, error) {
 | 
				
			||||||
 | 
						redisService := NewSmsPlatformKeyRedis(e.Orm, e.Log)
 | 
				
			||||||
 | 
						return redisService.GetRoundRobinKey(platformCode)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetQueueLength 获取队列长度
 | 
				
			||||||
 | 
					func (e *SmsPlatformKey) GetQueueLength(platformCode string) (int64, error) {
 | 
				
			||||||
 | 
						redisService := NewSmsPlatformKeyRedis(e.Orm, e.Log)
 | 
				
			||||||
 | 
						return redisService.GetQueueLength(platformCode)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ClearPlatformQueue 清空指定平台的队列
 | 
				
			||||||
 | 
					func (e *SmsPlatformKey) ClearPlatformQueue(platformCode string) error {
 | 
				
			||||||
 | 
						redisService := NewSmsPlatformKeyRedis(e.Orm, e.Log)
 | 
				
			||||||
 | 
						return redisService.ClearPlatformQueue(platformCode)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Get 获取SmsPlatformKey对象
 | 
				
			||||||
 | 
					func (e *SmsPlatformKey) Get(d *dto.SmsPlatformKeyGetReq, p *actions.DataPermission, model *models.SmsPlatformKey) error {
 | 
				
			||||||
 | 
						var data models.SmsPlatformKey
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := e.Orm.Model(&data).
 | 
				
			||||||
 | 
							Scopes(
 | 
				
			||||||
 | 
								actions.Permission(data.TableName(), p),
 | 
				
			||||||
 | 
							).
 | 
				
			||||||
 | 
							First(model, d.GetId()).Error
 | 
				
			||||||
 | 
						if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
 | 
				
			||||||
 | 
							err = errors.New("查看对象不存在或无权查看")
 | 
				
			||||||
 | 
							e.Log.Errorf("Service GetSmsPlatformKey error:%s \r\n", err)
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("db error:%s", err)
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Insert 创建SmsPlatformKey对象
 | 
				
			||||||
 | 
					func (e *SmsPlatformKey) Insert(c *dto.SmsPlatformKeyInsertReq) error {
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
						var data models.SmsPlatformKey
 | 
				
			||||||
 | 
						var count int64
 | 
				
			||||||
 | 
						c.Generate(&data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := e.Orm.Model(&models.SmsPlatformKey{}).
 | 
				
			||||||
 | 
							Where("platform_code = ? and api_key =? and account=?", data.PlatformCode, data.ApiKey, data.Account).
 | 
				
			||||||
 | 
							Count(&count).Error; err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("SmsPlatformKeyService Insert error:%s \r\n", err)
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if count > 0 {
 | 
				
			||||||
 | 
							return errors.New("ApiKey或账号 已存在")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = e.Orm.Create(&data).Error
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("SmsPlatformKeyService Insert error:%s \r\n", err)
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := e.AddQueque(dto.SmsPlatformKeyQueueDto{
 | 
				
			||||||
 | 
							PlatformCode: data.PlatformCode,
 | 
				
			||||||
 | 
							Account:      data.Account,
 | 
				
			||||||
 | 
							ApiKey:       data.ApiKey,
 | 
				
			||||||
 | 
							ApiSecret:    data.ApiSecret,
 | 
				
			||||||
 | 
						}); err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("添加队列失败,%v", err)
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Update 修改SmsPlatformKey对象
 | 
				
			||||||
 | 
					func (e *SmsPlatformKey) Update(c *dto.SmsPlatformKeyUpdateReq, p *actions.DataPermission) error {
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
						var data = models.SmsPlatformKey{}
 | 
				
			||||||
 | 
						var count int64
 | 
				
			||||||
 | 
						e.Orm.Scopes(
 | 
				
			||||||
 | 
							actions.Permission(data.TableName(), p),
 | 
				
			||||||
 | 
						).First(&data, c.GetId())
 | 
				
			||||||
 | 
						oldKey := data.ApiKey
 | 
				
			||||||
 | 
						oldSecret := data.ApiSecret
 | 
				
			||||||
 | 
						oldStatus := data.Status
 | 
				
			||||||
 | 
						oldPlatformCode := data.PlatformCode
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err1 := e.Orm.Model(&models.SmsPlatformKey{}).
 | 
				
			||||||
 | 
							Where("platform_code = ? and api_key =? and account=? and id != ?",
 | 
				
			||||||
 | 
								data.PlatformCode, data.ApiKey, data.Account, data.Id).
 | 
				
			||||||
 | 
							Count(&count).Error; err1 != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("SmsPlatformKeyService Insert error:%s \r\n", err1)
 | 
				
			||||||
 | 
							return err1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if count > 0 {
 | 
				
			||||||
 | 
							return errors.New("ApiKey或账号 已存在")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						c.Generate(&data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 如果要将启用状态改为禁用状态,需要检查该平台是否还有其他启用的密钥
 | 
				
			||||||
 | 
						if data.Status == 2 {
 | 
				
			||||||
 | 
							var remainingCount int64
 | 
				
			||||||
 | 
							err1 := e.Orm.Model(&models.SmsPlatformKey{}).
 | 
				
			||||||
 | 
								Where("platform_code = ? AND status = ? AND id != ?", oldPlatformCode, 1, c.GetId()).
 | 
				
			||||||
 | 
								Count(&remainingCount).Error
 | 
				
			||||||
 | 
							if err1 != nil {
 | 
				
			||||||
 | 
								e.Log.Errorf("检查平台剩余启用密钥失败: %v", err1)
 | 
				
			||||||
 | 
								return errors.New("检查平台剩余启用密钥失败")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if remainingCount == 0 {
 | 
				
			||||||
 | 
								return fmt.Errorf("平台 %s 至少需要保留一个启用状态的密钥,无法禁用", oldPlatformCode)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						db := e.Orm.Save(&data)
 | 
				
			||||||
 | 
						if err = db.Error; err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("SmsPlatformKeyService Save error:%s \r\n", err)
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if db.RowsAffected == 0 {
 | 
				
			||||||
 | 
							return errors.New("无权更新该数据")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if oldStatus == 1 && data.Status == 2 {
 | 
				
			||||||
 | 
							if err := e.RemoveQueque(dto.SmsPlatformKeyQueueDto{
 | 
				
			||||||
 | 
								PlatformCode: data.PlatformCode,
 | 
				
			||||||
 | 
								Account:      data.Account,
 | 
				
			||||||
 | 
								ApiKey:       data.ApiKey,
 | 
				
			||||||
 | 
								ApiSecret:    data.ApiSecret,
 | 
				
			||||||
 | 
							}, false); err != nil {
 | 
				
			||||||
 | 
								e.Log.Errorf("删除失败,%v", err)
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else if oldStatus == 2 && data.Status == 1 {
 | 
				
			||||||
 | 
							if err := e.AddQueque(dto.SmsPlatformKeyQueueDto{
 | 
				
			||||||
 | 
								PlatformCode: data.PlatformCode,
 | 
				
			||||||
 | 
								Account:      data.Account,
 | 
				
			||||||
 | 
								ApiKey:       data.ApiKey,
 | 
				
			||||||
 | 
								ApiSecret:    data.ApiSecret,
 | 
				
			||||||
 | 
							}); err != nil {
 | 
				
			||||||
 | 
								e.Log.Errorf("添加队列失败,%v", err)
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							oldQueueData := dto.SmsPlatformKeyQueueDto{
 | 
				
			||||||
 | 
								PlatformCode: data.PlatformCode,
 | 
				
			||||||
 | 
								Account:      data.Account,
 | 
				
			||||||
 | 
								ApiKey:       oldKey,
 | 
				
			||||||
 | 
								ApiSecret:    oldSecret,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							queueData := dto.SmsPlatformKeyQueueDto{
 | 
				
			||||||
 | 
								PlatformCode: data.PlatformCode,
 | 
				
			||||||
 | 
								Account:      data.Account,
 | 
				
			||||||
 | 
								ApiKey:       data.ApiKey,
 | 
				
			||||||
 | 
								ApiSecret:    data.ApiSecret,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if err := e.Replace(oldQueueData, queueData); err != nil {
 | 
				
			||||||
 | 
								e.Log.Errorf("替换队列失败,%v", err)
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Remove 删除SmsPlatformKey
 | 
				
			||||||
 | 
					func (e *SmsPlatformKey) Remove(d *dto.SmsPlatformKeyDeleteReq, p *actions.DataPermission) error {
 | 
				
			||||||
 | 
						var data models.SmsPlatformKey
 | 
				
			||||||
 | 
						var datas []models.SmsPlatformKey
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err1 := e.Orm.Where("id in (?)", d.GetId()).Find(&datas).Error; err1 != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("Service RemoveSmsPlatformKey error:%s \r\n", err1)
 | 
				
			||||||
 | 
							return err1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var platformCodes []string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, item := range datas {
 | 
				
			||||||
 | 
							if utility.ContainsString(platformCodes, item.PlatformCode) {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							platformCodes = append(platformCodes, item.PlatformCode)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := e.Orm.Transaction(func(tx *gorm.DB) error {
 | 
				
			||||||
 | 
							db := tx.Model(&data).
 | 
				
			||||||
 | 
								Scopes(
 | 
				
			||||||
 | 
									actions.Permission(data.TableName(), p),
 | 
				
			||||||
 | 
								).Delete(&data, d.GetId())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							var count []string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if err1 := tx.Model(&data).Where("status =1").
 | 
				
			||||||
 | 
								Group("platform_code").
 | 
				
			||||||
 | 
								Select("platform_code").
 | 
				
			||||||
 | 
								Scan(&count).
 | 
				
			||||||
 | 
								Error; err1 != nil {
 | 
				
			||||||
 | 
								e.Log.Errorf("Service RemoveSmsPlatformKey error:%s \r\n", err1)
 | 
				
			||||||
 | 
								return err1
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for _, item := range platformCodes {
 | 
				
			||||||
 | 
								if !utility.ContainsString(count, item) {
 | 
				
			||||||
 | 
									return errors.New("删除失败,通道最少需要保留一个可用ApiKey")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if err := db.Error; err != nil {
 | 
				
			||||||
 | 
								e.Log.Errorf("Service RemoveSmsPlatformKey error:%s \r\n", err)
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if db.RowsAffected == 0 {
 | 
				
			||||||
 | 
								return errors.New("无权删除该数据")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, item := range datas {
 | 
				
			||||||
 | 
							queueDta := dto.SmsPlatformKeyQueueDto{
 | 
				
			||||||
 | 
								PlatformCode: item.PlatformCode,
 | 
				
			||||||
 | 
								Account:      data.Account,
 | 
				
			||||||
 | 
								ApiKey:       item.ApiKey,
 | 
				
			||||||
 | 
								ApiSecret:    item.ApiSecret,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if err := e.RemoveQueque(queueDta, true); err != nil {
 | 
				
			||||||
 | 
								e.Log.Errorf("移出队列失败,%v", err)
 | 
				
			||||||
 | 
								return errors.New("删除队列失败")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// InitQueque 初始化Redis缓存队列
 | 
				
			||||||
 | 
					func (e *SmsPlatformKey) InitQueque() error {
 | 
				
			||||||
 | 
						redisService := NewSmsPlatformKeyRedis(e.Orm, e.Log)
 | 
				
			||||||
 | 
						return redisService.InitRedisQueque()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Replace 替换Redis缓存中的密钥
 | 
				
			||||||
 | 
					func (e *SmsPlatformKey) Replace(oldEntity dto.SmsPlatformKeyQueueDto, entity dto.SmsPlatformKeyQueueDto) error {
 | 
				
			||||||
 | 
						redisService := NewSmsPlatformKeyRedis(e.Orm, e.Log)
 | 
				
			||||||
 | 
						return redisService.ReplaceRedisKey(oldEntity, entity)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RemoveQueque 从Redis缓存中移出队列
 | 
				
			||||||
 | 
					func (e *SmsPlatformKey) RemoveQueque(entity dto.SmsPlatformKeyQueueDto, shouldDel bool) error {
 | 
				
			||||||
 | 
						redisService := NewSmsPlatformKeyRedis(e.Orm, e.Log)
 | 
				
			||||||
 | 
						return redisService.RemoveRedisQueque(entity, shouldDel)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AddQueque 添加到Redis缓存队列
 | 
				
			||||||
 | 
					func (e *SmsPlatformKey) AddQueque(entity dto.SmsPlatformKeyQueueDto) error {
 | 
				
			||||||
 | 
						redisService := NewSmsPlatformKeyRedis(e.Orm, e.Log)
 | 
				
			||||||
 | 
						return redisService.AddRedisQueque(entity)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										327
									
								
								app/admin/service/sms_platform_key_redis.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										327
									
								
								app/admin/service/sms_platform_key_redis.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,327 @@
 | 
				
			|||||||
 | 
					package service
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"math/rand"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"go-admin/app/admin/models"
 | 
				
			||||||
 | 
						"go-admin/app/admin/service/dto"
 | 
				
			||||||
 | 
						"go-admin/utils/redishelper"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/bytedance/sonic"
 | 
				
			||||||
 | 
						"github.com/go-admin-team/go-admin-core/logger"
 | 
				
			||||||
 | 
						"github.com/go-admin-team/go-admin-core/sdk/service"
 | 
				
			||||||
 | 
						"gorm.io/gorm"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SmsPlatformKeyRedis Redis版本的SMS平台密钥管理服务
 | 
				
			||||||
 | 
					type SmsPlatformKeyRedis struct {
 | 
				
			||||||
 | 
						SmsPlatformKey
 | 
				
			||||||
 | 
						redisHelper *redishelper.RedisHelper
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewSmsPlatformKeyRedis 创建Redis版本的SMS平台密钥服务
 | 
				
			||||||
 | 
					func NewSmsPlatformKeyRedis(orm *gorm.DB, log logger.Logger) *SmsPlatformKeyRedis {
 | 
				
			||||||
 | 
						return &SmsPlatformKeyRedis{
 | 
				
			||||||
 | 
							SmsPlatformKey: SmsPlatformKey{
 | 
				
			||||||
 | 
								Service: service.Service{
 | 
				
			||||||
 | 
									Orm: orm,
 | 
				
			||||||
 | 
									Log: logger.NewHelper(log),
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							redisHelper: redishelper.DefaultRedis,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// getRedisKey 获取Redis键名
 | 
				
			||||||
 | 
					func (e *SmsPlatformKeyRedis) getRedisKey(platformCode string) string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("sms:platform:keys:%s", platformCode)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// getRedisIndexKey 获取Redis索引键名(用于快速查找)
 | 
				
			||||||
 | 
					func (e *SmsPlatformKeyRedis) getRedisIndexKey(platformCode, apiKey string) string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("sms:platform:index:%s:%s", platformCode, apiKey)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *SmsPlatformKeyRedis) GetApiInfo(platformCode, apiKey string) (dto.SmsPlatformKeyQueueDto, error) {
 | 
				
			||||||
 | 
						indexKey := e.getRedisIndexKey(platformCode, apiKey)
 | 
				
			||||||
 | 
						data, err := e.redisHelper.GetString(indexKey)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("获取Redis索引失败 [%s:%s]: %v", platformCode, apiKey, err)
 | 
				
			||||||
 | 
							return dto.SmsPlatformKeyQueueDto{}, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var apiInfo dto.SmsPlatformKeyQueueDto
 | 
				
			||||||
 | 
						err = sonic.Unmarshal([]byte(data), &apiInfo)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("解析Redis数据失败 [%s:%s]: %v", platformCode, apiKey, err)
 | 
				
			||||||
 | 
							return dto.SmsPlatformKeyQueueDto{}, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return apiInfo, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// InitRedisQueque 初始化Redis缓存队列
 | 
				
			||||||
 | 
					// 从数据库加载所有SMS平台密钥并存储到Redis中
 | 
				
			||||||
 | 
					func (e *SmsPlatformKeyRedis) InitRedisQueque() error {
 | 
				
			||||||
 | 
						// 查询所有启用的SMS平台密钥
 | 
				
			||||||
 | 
						var list []models.SmsPlatformKey
 | 
				
			||||||
 | 
						err := e.Orm.Where("status = ?", 1).Find(&list).Error
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("查询SMS平台密钥失败: %v", err)
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(list) == 0 {
 | 
				
			||||||
 | 
							e.Log.Warn("未找到启用的SMS平台密钥")
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 按平台分组
 | 
				
			||||||
 | 
						platformKeyMap := make(map[string][]dto.SmsPlatformKeyQueueDto)
 | 
				
			||||||
 | 
						for _, item := range list {
 | 
				
			||||||
 | 
							data := dto.SmsPlatformKeyQueueDto{
 | 
				
			||||||
 | 
								PlatformCode: item.PlatformCode,
 | 
				
			||||||
 | 
								Account:      item.Account,
 | 
				
			||||||
 | 
								ApiKey:       item.ApiKey,
 | 
				
			||||||
 | 
								ApiSecret:    item.ApiSecret,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							platformKeyMap[item.PlatformCode] = append(platformKeyMap[item.PlatformCode], data)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 将每个平台的密钥存储到Redis
 | 
				
			||||||
 | 
						for platformCode, keys := range platformKeyMap {
 | 
				
			||||||
 | 
							redisKey := e.getRedisKey(platformCode)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 清空现有数据
 | 
				
			||||||
 | 
							err := e.redisHelper.DeleteString(redisKey)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								e.Log.Errorf("清空Redis队列失败 [%s]: %v", platformCode, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 存储密钥列表
 | 
				
			||||||
 | 
							for _, key := range keys {
 | 
				
			||||||
 | 
								keyJson, _ := json.Marshal(key)
 | 
				
			||||||
 | 
								err := e.redisHelper.RPushList(redisKey, string(keyJson))
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									e.Log.Errorf("添加密钥到Redis队列失败 [%s]: %v", platformCode, err)
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// 创建索引,便于快速查找和删除
 | 
				
			||||||
 | 
								indexKey := e.getRedisIndexKey(platformCode, key.ApiKey)
 | 
				
			||||||
 | 
								err = e.redisHelper.SetString(indexKey, string(keyJson))
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									e.Log.Errorf("创建密钥索引失败 [%s:%s]: %v", platformCode, key.ApiKey, err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 设置过期时间(24小时)
 | 
				
			||||||
 | 
							err = e.redisHelper.SetKeyExpiration(redisKey, 24*time.Hour)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								e.Log.Errorf("设置Redis队列过期时间失败 [%s]: %v", platformCode, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							e.Log.Infof("平台 [%s] 初始化 %d 个密钥到Redis缓存", platformCode, len(keys))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AddRedisQueque 添加密钥到Redis缓存
 | 
				
			||||||
 | 
					func (e *SmsPlatformKeyRedis) AddRedisQueque(entity dto.SmsPlatformKeyQueueDto) error {
 | 
				
			||||||
 | 
						redisKey := e.getRedisKey(entity.PlatformCode)
 | 
				
			||||||
 | 
						indexKey := e.getRedisIndexKey(entity.PlatformCode, entity.ApiKey)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 检查是否已存在
 | 
				
			||||||
 | 
						// exists, err := e.redisHelper.GetString(indexKey)
 | 
				
			||||||
 | 
						// if err == nil && exists != "" {
 | 
				
			||||||
 | 
						// 	return errors.New("密钥已存在")
 | 
				
			||||||
 | 
						// }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 序列化密钥数据
 | 
				
			||||||
 | 
						keyJson, err := json.Marshal(entity)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("序列化密钥数据失败: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 添加到队列
 | 
				
			||||||
 | 
						err = e.redisHelper.RPushList(redisKey, string(keyJson))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("添加密钥到Redis队列失败: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 创建索引
 | 
				
			||||||
 | 
						err = e.redisHelper.SetString(indexKey, string(keyJson))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("创建密钥索引失败: %v", err)
 | 
				
			||||||
 | 
							// 索引创建失败不影响主要功能
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 设置过期时间
 | 
				
			||||||
 | 
						err = e.redisHelper.SetKeyExpiration(redisKey, 24*time.Hour)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("设置Redis队列过期时间失败: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						e.Log.Infof("成功添加密钥到Redis缓存 [%s:%s]", entity.PlatformCode, entity.ApiKey)
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RemoveRedisQueque 从Redis缓存中删除密钥
 | 
				
			||||||
 | 
					// shouldDel 是否删除索引
 | 
				
			||||||
 | 
					func (e *SmsPlatformKeyRedis) RemoveRedisQueque(entity dto.SmsPlatformKeyQueueDto, shouldDel bool) error {
 | 
				
			||||||
 | 
						redisKey := e.getRedisKey(entity.PlatformCode)
 | 
				
			||||||
 | 
						indexKey := e.getRedisIndexKey(entity.PlatformCode, entity.ApiKey)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 获取要删除的密钥数据
 | 
				
			||||||
 | 
						keyData, err := e.redisHelper.GetString(indexKey)
 | 
				
			||||||
 | 
						if err != nil || keyData == "" {
 | 
				
			||||||
 | 
							return errors.New("密钥不存在")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 从队列中删除
 | 
				
			||||||
 | 
						_, err = e.redisHelper.LRem(redisKey, keyData)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("从Redis队列删除密钥失败: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 删除索引
 | 
				
			||||||
 | 
						if shouldDel {
 | 
				
			||||||
 | 
							err = e.redisHelper.DeleteString(indexKey)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								e.Log.Errorf("删除密钥索引失败: %v", err)
 | 
				
			||||||
 | 
								// 索引删除失败不影响主要功能
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// e.Log.Infof("成功从Redis缓存删除密钥 [%s:%s]", entity.PlatformCode, entity.ApiKey)
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetRandomKey 随机获取一个密钥
 | 
				
			||||||
 | 
					func (e *SmsPlatformKeyRedis) GetRandomKey(platformCode string) (*dto.SmsPlatformKeyQueueDto, error) {
 | 
				
			||||||
 | 
						redisKey := e.getRedisKey(platformCode)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 获取所有密钥
 | 
				
			||||||
 | 
						keys, err := e.redisHelper.GetAllList(redisKey)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("获取Redis队列失败: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(keys) == 0 {
 | 
				
			||||||
 | 
							return nil, errors.New("队列为空")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 随机选择一个
 | 
				
			||||||
 | 
						rand.Seed(time.Now().UnixNano())
 | 
				
			||||||
 | 
						randomIndex := rand.Intn(len(keys))
 | 
				
			||||||
 | 
						keyJson := keys[randomIndex]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var keyDto dto.SmsPlatformKeyQueueDto
 | 
				
			||||||
 | 
						err = json.Unmarshal([]byte(keyJson), &keyDto)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("反序列化密钥数据失败: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &keyDto, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetRoundRobinKey 轮询获取密钥
 | 
				
			||||||
 | 
					func (e *SmsPlatformKeyRedis) GetRoundRobinKey(platformCode string) (*dto.SmsPlatformKeyQueueDto, error) {
 | 
				
			||||||
 | 
						redisKey := e.getRedisKey(platformCode)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 从队列头部取出一个密钥
 | 
				
			||||||
 | 
						keyJson, err := e.redisHelper.LPopList(redisKey)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("从Redis队列获取密钥失败: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if keyJson == "" {
 | 
				
			||||||
 | 
							return nil, errors.New("队列为空")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 将密钥重新放到队列尾部(实现轮询)
 | 
				
			||||||
 | 
						err = e.redisHelper.RPushList(redisKey, keyJson)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("轮询密钥回放失败: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var keyDto dto.SmsPlatformKeyQueueDto
 | 
				
			||||||
 | 
						err = json.Unmarshal([]byte(keyJson), &keyDto)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("反序列化密钥数据失败: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &keyDto, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetQueueLength 获取队列长度
 | 
				
			||||||
 | 
					func (e *SmsPlatformKeyRedis) GetQueueLength(platformCode string) (int64, error) {
 | 
				
			||||||
 | 
						redisKey := e.getRedisKey(platformCode)
 | 
				
			||||||
 | 
						keys, err := e.redisHelper.GetAllList(redisKey)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return 0, fmt.Errorf("获取Redis队列长度失败: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return int64(len(keys)), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ReplaceRedisKey 替换Redis缓存中的密钥
 | 
				
			||||||
 | 
					func (e *SmsPlatformKeyRedis) ReplaceRedisKey(oldEntity, newEntity dto.SmsPlatformKeyQueueDto) error {
 | 
				
			||||||
 | 
						// 先删除旧密钥
 | 
				
			||||||
 | 
						err := e.RemoveRedisQueque(oldEntity, true)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("删除旧密钥失败: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 再添加新密钥
 | 
				
			||||||
 | 
						err = e.AddRedisQueque(newEntity)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("添加新密钥失败: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// e.Log.Infof("成功替换Redis缓存密钥 [%s] %s -> %s", newEntity.PlatformCode, oldEntity.ApiKey, newEntity.ApiKey)
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ClearPlatformQueue 清空指定平台的队列
 | 
				
			||||||
 | 
					func (e *SmsPlatformKeyRedis) ClearPlatformQueue(platformCode string) error {
 | 
				
			||||||
 | 
						redisKey := e.getRedisKey(platformCode)
 | 
				
			||||||
 | 
						err := e.redisHelper.DeleteString(redisKey)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("清空平台队列失败: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 清空相关索引
 | 
				
			||||||
 | 
						pattern := fmt.Sprintf("sms:platform:index:%s:*", platformCode)
 | 
				
			||||||
 | 
						err = e.redisHelper.DeleteKeysByPrefix(pattern)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("清空平台索引失败: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						e.Log.Infof("成功清空平台 [%s] 的Redis队列", platformCode)
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 获取平台所有密钥
 | 
				
			||||||
 | 
					func (e *SmsPlatformKeyRedis) GetPlatformKeys(platformCode string) ([]dto.SmsPlatformKeyQueueDto, error) {
 | 
				
			||||||
 | 
						redisKey := e.getRedisKey(platformCode)
 | 
				
			||||||
 | 
						keys, err := e.redisHelper.GetAllList(redisKey)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("获取Redis队列失败: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var keyDtos []dto.SmsPlatformKeyQueueDto
 | 
				
			||||||
 | 
						for _, keyJson := range keys {
 | 
				
			||||||
 | 
							var keyDto dto.SmsPlatformKeyQueueDto
 | 
				
			||||||
 | 
							err = json.Unmarshal([]byte(keyJson), &keyDto)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, fmt.Errorf("反序列化密钥数据失败: %v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							keyDtos = append(keyDtos, keyDto)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return keyDtos, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										23
									
								
								app/admin/service/sms_platform_key_redis_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								app/admin/service/sms_platform_key_redis_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					package service
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"go-admin/common/global"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/go-admin-team/go-admin-core/logger"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestNextKey(t *testing.T) {
 | 
				
			||||||
 | 
						orm := initSetting()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						redisService := NewSmsPlatformKeyRedis(orm, logger.DefaultLogger)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						val, err := redisService.GetRoundRobinKey(global.SmsPlatformTextVerified)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Errorf("GetRoundRobinKey error:%s \r\n", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fmt.Printf("val:%s \r\n", val)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -2,6 +2,7 @@ package service
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/go-admin-team/go-admin-core/sdk/service"
 | 
						"github.com/go-admin-team/go-admin-core/sdk/service"
 | 
				
			||||||
	"gorm.io/gorm"
 | 
						"gorm.io/gorm"
 | 
				
			||||||
@ -38,7 +39,7 @@ func (e SmsReceiveLog) WebHook(req *dto.SmsReceiveWebHookReq) error {
 | 
				
			|||||||
			UserId:      phoneLog.UserId,
 | 
								UserId:      phoneLog.UserId,
 | 
				
			||||||
			Service:     phoneLog.Service,
 | 
								Service:     phoneLog.Service,
 | 
				
			||||||
			ServiceCode: phoneLog.ServiceCode,
 | 
								ServiceCode: phoneLog.ServiceCode,
 | 
				
			||||||
			MessageId:   req.MessageID,
 | 
								MessageId:   strconv.Itoa(req.MessageID),
 | 
				
			||||||
			Phone:       phoneLog.Phone,
 | 
								Phone:       phoneLog.Phone,
 | 
				
			||||||
			Code:        req.Code,
 | 
								Code:        req.Code,
 | 
				
			||||||
			Status:      2,
 | 
								Status:      2,
 | 
				
			||||||
@ -48,8 +49,16 @@ func (e SmsReceiveLog) WebHook(req *dto.SmsReceiveWebHookReq) error {
 | 
				
			|||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							smsPlatformKeyRedis := NewSmsPlatformKeyRedis(e.Orm, e.Log)
 | 
				
			||||||
 | 
							apiInfo, err := smsPlatformKeyRedis.GetApiInfo(phoneLog.PlatformCode, phoneLog.ApiKey)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								e.Log.Errorf("获取平台密钥失败, %s", err)
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		phoneService := SmsDaisysms{Service: e.Service}
 | 
							phoneService := SmsDaisysms{Service: e.Service}
 | 
				
			||||||
		if code := phoneService.setStatus(req.ActivationID); code != statuscode.Success {
 | 
					
 | 
				
			||||||
 | 
							if code := phoneService.setStatus(req.ActivationID, &apiInfo); code != statuscode.Success {
 | 
				
			||||||
			e.Log.Errorf("接受验证码回调后修改状态失败 %d", code)
 | 
								e.Log.Errorf("接受验证码回调后修改状态失败 %d", code)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
				
			|||||||
@ -4,6 +4,7 @@ import (
 | 
				
			|||||||
	"go-admin/app/admin/service/dto"
 | 
						"go-admin/app/admin/service/dto"
 | 
				
			||||||
	"go-admin/config"
 | 
						"go-admin/config"
 | 
				
			||||||
	"go-admin/utils/redishelper"
 | 
						"go-admin/utils/redishelper"
 | 
				
			||||||
 | 
						"go-admin/utils/utility"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/go-admin-team/go-admin-core/logger"
 | 
						"github.com/go-admin-team/go-admin-core/logger"
 | 
				
			||||||
@ -13,9 +14,10 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func initSetting() *gorm.DB {
 | 
					func initSetting() *gorm.DB {
 | 
				
			||||||
	dsn := "root:123456@tcp(127.0.0.1:3306)/proxy_server?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms"
 | 
						dsn := "root:123456@tcp(127.0.0.1:3306)/proxy_server_prod?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms"
 | 
				
			||||||
	db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})
 | 
						db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})
 | 
				
			||||||
	sdk.Runtime.SetDb("default", db)
 | 
						sdk.Runtime.SetDb("default", db)
 | 
				
			||||||
 | 
						// config.ExtConfig.
 | 
				
			||||||
	config.ExtConfig.TrxGridUrl = "https://api.trongrid.io"
 | 
						config.ExtConfig.TrxGridUrl = "https://api.trongrid.io"
 | 
				
			||||||
	config.ExtConfig.DaisysmsUrl = "https://daisysms.com/stubs/handler_api.php"
 | 
						config.ExtConfig.DaisysmsUrl = "https://daisysms.com/stubs/handler_api.php"
 | 
				
			||||||
	config.ExtConfig.SmsTextVerified.ApiKey = "ZQ0swXnsaPpeGdwa3c7gT9U9I1Oh9WoDHx0amuYovvaHuqd5u6B4NBBUSUBjR"
 | 
						config.ExtConfig.SmsTextVerified.ApiKey = "ZQ0swXnsaPpeGdwa3c7gT9U9I1Oh9WoDHx0amuYovvaHuqd5u6B4NBBUSUBjR"
 | 
				
			||||||
@ -24,6 +26,7 @@ func initSetting() *gorm.DB {
 | 
				
			|||||||
	redishelper.InitDefaultRedis("127.0.0.1:6379", "", 4)
 | 
						redishelper.InitDefaultRedis("127.0.0.1:6379", "", 4)
 | 
				
			||||||
	redishelper.InitLockRedisConn("127.0.0.1:6379", "", "4")
 | 
						redishelper.InitLockRedisConn("127.0.0.1:6379", "", "4")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						utility.InitSnowflake()
 | 
				
			||||||
	return db
 | 
						return db
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -2,14 +2,21 @@ package service
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/go-admin-team/go-admin-core/logger"
 | 
				
			||||||
	"github.com/go-admin-team/go-admin-core/sdk/service"
 | 
						"github.com/go-admin-team/go-admin-core/sdk/service"
 | 
				
			||||||
 | 
						"github.com/shopspring/decimal"
 | 
				
			||||||
	"gorm.io/gorm"
 | 
						"gorm.io/gorm"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"go-admin/app/admin/models"
 | 
						"go-admin/app/admin/models"
 | 
				
			||||||
	"go-admin/app/admin/service/dto"
 | 
						"go-admin/app/admin/service/dto"
 | 
				
			||||||
	"go-admin/common/actions"
 | 
						"go-admin/common/actions"
 | 
				
			||||||
	cDto "go-admin/common/dto"
 | 
						cDto "go-admin/common/dto"
 | 
				
			||||||
 | 
						"go-admin/common/global"
 | 
				
			||||||
 | 
						"go-admin/common/statuscode"
 | 
				
			||||||
 | 
						"go-admin/utils/utility"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type SmsRenewalLog struct {
 | 
					type SmsRenewalLog struct {
 | 
				
			||||||
@ -17,22 +24,45 @@ type SmsRenewalLog struct {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetPage 获取SmsRenewalLog列表
 | 
					// GetPage 获取SmsRenewalLog列表
 | 
				
			||||||
func (e *SmsRenewalLog) GetPage(c *dto.SmsRenewalLogGetPageReq, p *actions.DataPermission, list *[]models.SmsRenewalLog, count *int64) error {
 | 
					func (e *SmsRenewalLog) GetPage(c *dto.SmsRenewalLogGetPageReq, p *actions.DataPermission, list *[]dto.SmsRenewalLogResp, count *int64) error {
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	var data models.SmsRenewalLog
 | 
						var data models.SmsRenewalLog
 | 
				
			||||||
 | 
						var datas []models.SmsRenewalLog
 | 
				
			||||||
	err = e.Orm.Model(&data).
 | 
						query := e.Orm.Model(&data).
 | 
				
			||||||
 | 
							Joins("left join sys_user on sys_user.user_id = sms_renewal_log.user_id").
 | 
				
			||||||
		Scopes(
 | 
							Scopes(
 | 
				
			||||||
			cDto.MakeCondition(c.GetNeedSearch()),
 | 
								cDto.MakeCondition(c.GetNeedSearch()),
 | 
				
			||||||
			cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
 | 
								cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
 | 
				
			||||||
			actions.Permission(data.TableName(), p),
 | 
								actions.Permission(data.TableName(), p),
 | 
				
			||||||
		).
 | 
							)
 | 
				
			||||||
		Find(list).Limit(-1).Offset(-1).
 | 
					
 | 
				
			||||||
 | 
						if c.UserName != "" {
 | 
				
			||||||
 | 
							query = query.Where("sys_user.username LIKE ?", "%"+c.UserName+"%")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						err = query.Select("sms_renewal_log.*, sys_user.username").Find(&datas).Limit(-1).Offset(-1).
 | 
				
			||||||
		Count(count).Error
 | 
							Count(count).Error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		e.Log.Errorf("SmsRenewalLogService GetPage error:%s \r\n", err)
 | 
							e.Log.Errorf("SmsRenewalLogService GetPage error:%s \r\n", err)
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, v := range datas {
 | 
				
			||||||
 | 
							*list = append(*list, dto.SmsRenewalLogResp{
 | 
				
			||||||
 | 
								Id:           v.Id,
 | 
				
			||||||
 | 
								UserId:       v.UserId,
 | 
				
			||||||
 | 
								Category:     v.Category,
 | 
				
			||||||
 | 
								Phone:        v.Phone,
 | 
				
			||||||
 | 
								Amount:       v.Amount,
 | 
				
			||||||
 | 
								BeforeTime:   v.BeforeTime,
 | 
				
			||||||
 | 
								Period:       v.Period,
 | 
				
			||||||
 | 
								CreatedAt:    v.CreatedAt,
 | 
				
			||||||
 | 
								UserName:     v.Username,
 | 
				
			||||||
 | 
								TradeOrderNo: v.TradeOrderNo,
 | 
				
			||||||
 | 
								PayOrderNo:   v.PayOrderNo,
 | 
				
			||||||
 | 
								Status:       v.Status,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -107,3 +137,551 @@ func (e *SmsRenewalLog) Remove(d *dto.SmsRenewalLogDeleteReq, p *actions.DataPer
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 手动扣费
 | 
				
			||||||
 | 
					func (e *SmsRenewalLog) ManualDeduct(req *dto.ManualDeductReq, userId int) (dto.ManualDeductResp, int) {
 | 
				
			||||||
 | 
						var entity models.SmsPhone
 | 
				
			||||||
 | 
						var result dto.ManualDeductResp
 | 
				
			||||||
 | 
						phoneService := SmsPhone{}
 | 
				
			||||||
 | 
						phoneService.Orm = e.Orm
 | 
				
			||||||
 | 
						phoneService.Log = e.Log
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := e.Orm.Model(&entity).
 | 
				
			||||||
 | 
							Where("activation_id =?", req.ActivationId).
 | 
				
			||||||
 | 
							First(&entity).Error; err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("获取短信号码失败, %s", err)
 | 
				
			||||||
 | 
							return result, statuscode.SmsNotExisted
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 获取服务配置映射
 | 
				
			||||||
 | 
						serviceMap, premiumMap, err := phoneService.getRenewalConfigs()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("获取短信服务配置失败, %s", err)
 | 
				
			||||||
 | 
							return result, statuscode.ServerError
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var count int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := e.Orm.Model(&models.SmsRenewalLog{}).
 | 
				
			||||||
 | 
							Where("phone_id = ? AND status = 1", entity.Id).
 | 
				
			||||||
 | 
							Count(&count).Error; err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("查询号码续费记录失败 [PhoneID: %d]: %v", entity.Id, err)
 | 
				
			||||||
 | 
							return result, statuscode.ServerError
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if count > 0 {
 | 
				
			||||||
 | 
							return result, statuscode.SmsRenewalLogExisted
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if renewLog, err := e.processPhoneRenewal(entity, serviceMap, premiumMap, req.TradeOrderNo, false); err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("处理号码续费失败 [PhoneID: %d 续费订单号:%s]: %v", entity.Id, renewLog.PayOrderNo, err)
 | 
				
			||||||
 | 
							return result, statuscode.ServerError
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							result.ActivationId = req.ActivationId
 | 
				
			||||||
 | 
							result.TradeOrderNo = renewLog.TradeOrderNo
 | 
				
			||||||
 | 
							result.PayOrderNo = renewLog.PayOrderNo
 | 
				
			||||||
 | 
							result.BeginTime = req.BeginTime
 | 
				
			||||||
 | 
							result.EndTime = renewLog.TargetTime
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return result, statuscode.Success
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 续期详情
 | 
				
			||||||
 | 
					func (e *SmsRenewalLog) GetRenewalDetailByTradeOrderNo(req *dto.ManualDeductDetailReq, userId int) (dto.ManualDeductResp, int) {
 | 
				
			||||||
 | 
						var entity models.SmsRenewalLog
 | 
				
			||||||
 | 
						result := dto.ManualDeductResp{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := e.Orm.Model(entity).
 | 
				
			||||||
 | 
							Joins("LEFT JOIN sms_phone s on s.id = sms_renewal_log.phone_id").
 | 
				
			||||||
 | 
							Where("sms_renewal_log.trade_order_no =? and sms_renewal_log.user_id =?", req.TradeOrderNo, userId).
 | 
				
			||||||
 | 
							Select("sms_renewal_log.*, s.activation_id").
 | 
				
			||||||
 | 
							First(&entity).Error; err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("获取续费记录失败, %s", err)
 | 
				
			||||||
 | 
							return result, statuscode.SmsNotExisted
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						result.ActivationId = entity.ActivationId
 | 
				
			||||||
 | 
						result.TradeOrderNo = entity.TradeOrderNo
 | 
				
			||||||
 | 
						result.PayOrderNo = entity.PayOrderNo
 | 
				
			||||||
 | 
						result.BeginTime = entity.BeforeTime
 | 
				
			||||||
 | 
						result.EndTime = entity.TargetTime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return result, statuscode.Success
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AutoRenewal 自动续期处理
 | 
				
			||||||
 | 
					// 处理即将到期的长期号码自动续费逻辑
 | 
				
			||||||
 | 
					func (e *SmsRenewalLog) AutoRenewal() error {
 | 
				
			||||||
 | 
						if err := e.JudgeRenewalLogStatus(); err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("处理预扣费记录失败 %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 获取24小时内到期的自动续费号码
 | 
				
			||||||
 | 
						phoneService := SmsPhone{}
 | 
				
			||||||
 | 
						phoneService.Orm = e.Orm
 | 
				
			||||||
 | 
						phoneService.Log = e.Log
 | 
				
			||||||
 | 
						expiredPhones, err := e.getExpiredPhones()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 获取服务配置映射
 | 
				
			||||||
 | 
						serviceMap, premiumMap, err := phoneService.getRenewalConfigs()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 处理每个到期号码
 | 
				
			||||||
 | 
						for _, phone := range expiredPhones {
 | 
				
			||||||
 | 
							if renewLog, err := e.processPhoneRenewal(phone, serviceMap, premiumMap, "", false); err != nil {
 | 
				
			||||||
 | 
								e.Log.Errorf("处理号码续费失败 [PhoneID: %d 续费订单号:%s]: %v", phone.Id, renewLog.PayOrderNo, err)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 手动续费(处理到期号码)
 | 
				
			||||||
 | 
					func (e *SmsRenewalLog) ManualRenewal(activationIds []string) ([]string, error) {
 | 
				
			||||||
 | 
						if len(activationIds) == 0 {
 | 
				
			||||||
 | 
							return nil, errors.New("activationIds 不能为空")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var errorIds []string
 | 
				
			||||||
 | 
						var expiredPhones []models.SmsPhone
 | 
				
			||||||
 | 
						phoneService := SmsPhone{}
 | 
				
			||||||
 | 
						phoneService.Orm = e.Orm
 | 
				
			||||||
 | 
						phoneService.Log = e.Log
 | 
				
			||||||
 | 
						// 查询所有到期号码
 | 
				
			||||||
 | 
						if err := e.Orm.Model(&models.SmsPhone{}).
 | 
				
			||||||
 | 
							Where("activation_id IN ? ", activationIds). //expire_time < ? time.Now()
 | 
				
			||||||
 | 
							Find(&expiredPhones).Error; err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 获取服务配置映射
 | 
				
			||||||
 | 
						serviceMap, premiumMap, err := phoneService.getRenewalConfigs()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 处理每个到期号码
 | 
				
			||||||
 | 
						for _, phone := range expiredPhones {
 | 
				
			||||||
 | 
							if renewLog, err := e.processPhoneRenewal(phone, serviceMap, premiumMap, "", true); err != nil {
 | 
				
			||||||
 | 
								e.Log.Errorf("处理号码续费失败 [PhoneID: %d 续费订单号:%s]: %v", phone.Id, renewLog.PayOrderNo, err)
 | 
				
			||||||
 | 
								errorIds = append(errorIds, fmt.Sprintf("处理号码续费失败 [PhoneID: %d]: %v", phone.Id, err))
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return errorIds, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// getExpiredPhones 获取即将到期的自动续费号码
 | 
				
			||||||
 | 
					func (e *SmsRenewalLog) getExpiredPhones() ([]models.SmsPhone, error) {
 | 
				
			||||||
 | 
						var phones []models.SmsPhone
 | 
				
			||||||
 | 
						startTime := time.Now().Add(-24 * time.Hour)
 | 
				
			||||||
 | 
						endTime := time.Now().Add(24 * time.Hour)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						notExistsQuery := e.Orm.Model(&models.SmsRenewalLog{}).
 | 
				
			||||||
 | 
							Select("1").
 | 
				
			||||||
 | 
							Where("sms_renewal_log.phone_id = sms_phone.id AND sms_renewal_log.status in (1,2) AND status = 1")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := e.Orm.Model(&models.SmsPhone{}).
 | 
				
			||||||
 | 
							Where("auto_renewal = 1 AND type = 1 AND actived = 2 AND expire_time >= ? AND expire_time < ?", startTime, endTime).
 | 
				
			||||||
 | 
							Where("NOT EXISTS (?)", notExistsQuery).
 | 
				
			||||||
 | 
							Find(&phones).Error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return phones, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 获取预扣除的记录
 | 
				
			||||||
 | 
					func (e *SmsRenewalLog) GetLockRenewalLogs() ([]models.SmsRenewalLog, error) {
 | 
				
			||||||
 | 
						var logs []models.SmsRenewalLog
 | 
				
			||||||
 | 
						err := e.Orm.Model(&models.SmsRenewalLog{}).
 | 
				
			||||||
 | 
							Joins("left join sms_phone s on s.id = sms_renewal_log.phone_id").
 | 
				
			||||||
 | 
							Where("sms_renewal_log.status = 1").
 | 
				
			||||||
 | 
							Select("sms_renewal_log.*, s.platform_code,s.api_key,s.billing_cycle_id").
 | 
				
			||||||
 | 
							Find(&logs).Error
 | 
				
			||||||
 | 
						return logs, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 判断续费是否成功
 | 
				
			||||||
 | 
					func (e *SmsRenewalLog) JudgeRenewalLogStatus() error {
 | 
				
			||||||
 | 
						logs, err := e.GetLockRenewalLogs()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						smsPlatformKeyRedis := NewSmsPlatformKeyRedis(e.Orm, e.Log)
 | 
				
			||||||
 | 
						textVerifiedService := SmsTextVerified{Service: e.Service}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, item := range logs {
 | 
				
			||||||
 | 
							switch item.PlatformCode {
 | 
				
			||||||
 | 
							case global.SmsPlatformTextVerified:
 | 
				
			||||||
 | 
								apiInfo, err := smsPlatformKeyRedis.GetApiInfo(item.PlatformCode, item.ApiKey)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									e.Log.Errorf("获取短信平台密钥失败 [PlatformCode: %s]: %v", item.PlatformCode, err)
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								cycleInfo, err := textVerifiedService.GetBillingCycle(item.BillingCycleId, &apiInfo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									e.Log.Errorf("获取短信平台密钥失败 [PlatformCode: %s]: %v", item.PlatformCode, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if errors.Is(err, LogNotFund) {
 | 
				
			||||||
 | 
										if err := e.rollbackRenewalLog(item.Id); err != nil {
 | 
				
			||||||
 | 
											e.Log.Errorf("回滚续费记录失败 [RenewalLogID: %d]: %v", item.Id, err)
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								startTime := item.BeforeTime.UTC().Truncate(24 * time.Hour)
 | 
				
			||||||
 | 
								endTime := cycleInfo.RenewedThrough.Truncate(24 * time.Hour)
 | 
				
			||||||
 | 
								// 续费成功
 | 
				
			||||||
 | 
								if startTime.Before(endTime) {
 | 
				
			||||||
 | 
									if err := e.confirmPayment(item.PhoneId, item.Id); err != nil {
 | 
				
			||||||
 | 
										e.Log.Errorf("更新续费记录状态失败 [RenewalLogID: %d]: %v", item.Id, err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} else if item.BeforeTime.Before(time.Now()) {
 | 
				
			||||||
 | 
									if err := e.rollbackRenewalLog(item.Id); err != nil {
 | 
				
			||||||
 | 
										e.Log.Errorf("回滚续费记录失败 [RenewalLogID: %d]: %v", item.Id, err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case global.SmsPlatformDaisysms:
 | 
				
			||||||
 | 
								//没有手动续费只能 在到期前直接扣费
 | 
				
			||||||
 | 
								timeDuration := time.Since(item.BeforeTime)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if timeDuration < 24*time.Hour {
 | 
				
			||||||
 | 
									if err := e.confirmPayment(item.PhoneId, item.Id); err != nil {
 | 
				
			||||||
 | 
										e.Log.Errorf("更新续费记录状态失败 [RenewalLogID: %d]: %v", item.Id, err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								e.Log.Errorf("不支持的短信平台 %s 续费id:%d", item.PlatformCode, item.Id)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// getRenewalConfigs 获取续费相关配置
 | 
				
			||||||
 | 
					func (e *SmsPhone) getRenewalConfigs() (map[string]models.SmsServices, map[string]dto.GetSysConfigByKEYForServiceResp, error) {
 | 
				
			||||||
 | 
						smsServices := SmsServices{Service: e.Service}
 | 
				
			||||||
 | 
						serviceMap := smsServices.GetMapAll()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						configService := SysConfig{Service: e.Service}
 | 
				
			||||||
 | 
						configKeys := []string{"renew_number_premium_daisysms", "renew_number_premium_textverified"}
 | 
				
			||||||
 | 
						premiumMap, err := configService.GetMapByKeys(configKeys)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return serviceMap, premiumMap, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// processPhoneRenewal 处理单个号码的续费
 | 
				
			||||||
 | 
					// tradeOrderNo 交易订单号
 | 
				
			||||||
 | 
					func (e *SmsRenewalLog) processPhoneRenewal(phone models.SmsPhone, serviceMap map[string]models.SmsServices,
 | 
				
			||||||
 | 
						premiumMap map[string]dto.GetSysConfigByKEYForServiceResp, tradeOrderNo string, admin bool) (models.SmsRenewalLog, error) {
 | 
				
			||||||
 | 
						var renewLog models.SmsRenewalLog
 | 
				
			||||||
 | 
						smsPlatformKeyRedis := NewSmsPlatformKeyRedis(e.Orm, e.Log)
 | 
				
			||||||
 | 
						apiInfo, err := smsPlatformKeyRedis.GetApiInfo(phone.PlatformCode, phone.ApiKey)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return renewLog, fmt.Errorf("获取短信平台密钥失败 [PlatformCode: %s]: %v", phone.PlatformCode, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//续期id为空 重新获取
 | 
				
			||||||
 | 
						if phone.BillingCycleId == "" {
 | 
				
			||||||
 | 
							switch phone.PlatformCode {
 | 
				
			||||||
 | 
							case global.SmsPlatformTextVerified:
 | 
				
			||||||
 | 
								//可以手动续费
 | 
				
			||||||
 | 
								textVerifiedService := SmsTextVerified{Service: e.Service}
 | 
				
			||||||
 | 
								detail, code := textVerifiedService.GetRentalDetail(phone.ActivationId, &apiInfo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if code != statuscode.Success {
 | 
				
			||||||
 | 
									logger.Errorf("获取短信验证码续费详情失败 [ActivationID: %s]: %v", phone.ActivationId, code)
 | 
				
			||||||
 | 
									return renewLog, fmt.Errorf("获取短信验证码续费详情失败 [ActivationID: %s]: %v", phone.ActivationId, code)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								phone.BillingCycleId = detail.BillingCycleId
 | 
				
			||||||
 | 
							case global.SmsPlatformDaisysms:
 | 
				
			||||||
 | 
								//只有自动续费的 用定时服务查询状态
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 获取服务价格
 | 
				
			||||||
 | 
						service, exists := serviceMap[phone.PlatformCode+"_"+phone.ServiceCode]
 | 
				
			||||||
 | 
						if !exists {
 | 
				
			||||||
 | 
							return renewLog, errors.New("服务不存在")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 计算续费价格
 | 
				
			||||||
 | 
						renewalPrice := e.calculateRenewalPrice(service.LongPrice, phone.PlatformCode, premiumMap)
 | 
				
			||||||
 | 
						if renewalPrice.IsZero() {
 | 
				
			||||||
 | 
							return renewLog, errors.New("续费价格计算失败")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 创建续费日志
 | 
				
			||||||
 | 
						renewLog = e.createRenewalLog(phone, renewalPrice, tradeOrderNo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 执行续费事务
 | 
				
			||||||
 | 
						if err := e.executeRenewalTransaction(phone, renewalPrice, &renewLog, &apiInfo, admin); err != nil {
 | 
				
			||||||
 | 
							return renewLog, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return renewLog, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// calculateRenewalPrice 计算续费价格
 | 
				
			||||||
 | 
					func (e *SmsRenewalLog) calculateRenewalPrice(basePrice decimal.Decimal, platformCode string, premiumMap map[string]dto.GetSysConfigByKEYForServiceResp) decimal.Decimal {
 | 
				
			||||||
 | 
						percent := decimal.NewFromInt(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var configKey string
 | 
				
			||||||
 | 
						switch platformCode {
 | 
				
			||||||
 | 
						case global.SmsPlatformDaisysms:
 | 
				
			||||||
 | 
							configKey = "renew_number_premium_daisysms"
 | 
				
			||||||
 | 
						case global.SmsPlatformTextVerified:
 | 
				
			||||||
 | 
							configKey = "renew_number_premium_textverified"
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return basePrice
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if config, exists := premiumMap[configKey]; exists {
 | 
				
			||||||
 | 
							if val, err := decimal.NewFromString(config.ConfigValue); err == nil && val.Cmp(decimal.Zero) > 0 {
 | 
				
			||||||
 | 
								percent = decimal.NewFromInt(100).Add(val).Div(decimal.NewFromInt(100))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return basePrice.Mul(percent).Truncate(2)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// createRenewalLog 创建续费日志
 | 
				
			||||||
 | 
					// tradeOrderNo 交易订单号
 | 
				
			||||||
 | 
					func (e *SmsRenewalLog) createRenewalLog(phone models.SmsPhone, amount decimal.Decimal, tradeOrderNo string) models.SmsRenewalLog {
 | 
				
			||||||
 | 
						targetTime := phone.ExpireTime.AddDate(0, 0, 30)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return models.SmsRenewalLog{
 | 
				
			||||||
 | 
							UserId:       phone.UserId,
 | 
				
			||||||
 | 
							TradeOrderNo: tradeOrderNo,
 | 
				
			||||||
 | 
							PayOrderNo:   utility.GenerateTraceID(),
 | 
				
			||||||
 | 
							Phone:        phone.Phone,
 | 
				
			||||||
 | 
							Category:     2,
 | 
				
			||||||
 | 
							PhoneId:      phone.Id,
 | 
				
			||||||
 | 
							Type:         phone.Type,
 | 
				
			||||||
 | 
							Amount:       amount,
 | 
				
			||||||
 | 
							BeforeTime:   *phone.ExpireTime,
 | 
				
			||||||
 | 
							TargetTime:   &targetTime,
 | 
				
			||||||
 | 
							Period:       30,
 | 
				
			||||||
 | 
							Status:       1,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// executeRenewalTransaction 执行续费事务
 | 
				
			||||||
 | 
					func (e *SmsRenewalLog) executeRenewalTransaction(phone models.SmsPhone, price decimal.Decimal,
 | 
				
			||||||
 | 
						renewLog *models.SmsRenewalLog, apiInfo *dto.SmsPlatformKeyQueueDto, isAdmin bool) error {
 | 
				
			||||||
 | 
						err := e.Orm.Transaction(func(tx *gorm.DB) error {
 | 
				
			||||||
 | 
							// 扣除余额
 | 
				
			||||||
 | 
							result := tx.Exec("UPDATE member_balance SET balance = balance - ? WHERE user_id = ? AND balance >= ?", price, phone.UserId, price)
 | 
				
			||||||
 | 
							if result.Error != nil {
 | 
				
			||||||
 | 
								return result.Error
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if result.RowsAffected == 0 {
 | 
				
			||||||
 | 
								return errors.New("余额不足")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 创建续费日志
 | 
				
			||||||
 | 
							if err := tx.Create(renewLog).Error; err != nil {
 | 
				
			||||||
 | 
								return errors.New("创建续费日志失败")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil && err.Error() == "余额不足" && phone.ExpireTime.Before(time.Now()) {
 | 
				
			||||||
 | 
							return e.handleInsufficientBalance(phone)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//是否是管理员处理
 | 
				
			||||||
 | 
						if isAdmin {
 | 
				
			||||||
 | 
							if err1 := e.confirmPayment(phone.Id, renewLog.Id); err1 != nil {
 | 
				
			||||||
 | 
								logger.Errorf("确认扣款失败 [PhoneID: %d, RenewalLogID: %d]: %v", phone.Id, renewLog.Id, err1)
 | 
				
			||||||
 | 
								return err1
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							switch phone.PlatformCode {
 | 
				
			||||||
 | 
							case global.SmsPlatformTextVerified:
 | 
				
			||||||
 | 
								//可以手动续费
 | 
				
			||||||
 | 
								textVerifiedService := SmsTextVerified{Service: e.Service}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								code := textVerifiedService.ManualRenewal(phone.BillingCycleId, apiInfo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// 没有需要续费的号码 重新设置可自动续费之后再试一次
 | 
				
			||||||
 | 
								if code == statuscode.NothingToRenew {
 | 
				
			||||||
 | 
									if renewCode := textVerifiedService.Renew(phone.ActivationId, true, apiInfo); renewCode == statuscode.Success {
 | 
				
			||||||
 | 
										code = textVerifiedService.ManualRenewal(phone.BillingCycleId, apiInfo)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if code == statuscode.Success {
 | 
				
			||||||
 | 
									if err1 := e.confirmPayment(phone.Id, renewLog.Id); err1 != nil {
 | 
				
			||||||
 | 
										logger.Errorf("确认扣款失败 [PhoneID: %d, RenewalLogID: %d]: %v", phone.Id, renewLog.Id, err1)
 | 
				
			||||||
 | 
										return err1
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									if err1 := e.rollbackRenewalLog(renewLog.Id); err1 != nil {
 | 
				
			||||||
 | 
										logger.Errorf("回滚续费日志状态失败,续费日志ID: %d, 错误: %v", renewLog.Id, err1)
 | 
				
			||||||
 | 
										return err1
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									return fmt.Errorf("手动续费失败 [BillingCycleID: %s]: %v", phone.BillingCycleId, code)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case global.SmsPlatformDaisysms:
 | 
				
			||||||
 | 
								//只有自动续费的 用定时服务查询状态
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 确认扣款成功
 | 
				
			||||||
 | 
					func (e *SmsRenewalLog) confirmPayment(phoneId int, renewalLogId int) error {
 | 
				
			||||||
 | 
						var renewLog models.SmsRenewalLog
 | 
				
			||||||
 | 
						if err := e.Orm.Where("id = ?", renewalLogId).First(&renewLog).Error; err != nil {
 | 
				
			||||||
 | 
							if errors.Is(err, gorm.ErrRecordNotFound) {
 | 
				
			||||||
 | 
								// 事务ID不存在:这是致命错误,平台A必须有此记录才能Confirm
 | 
				
			||||||
 | 
								e.Log.Errorf("Confirm失败: 找不到续费记录ID %s 的续费日志", renewalLogId)
 | 
				
			||||||
 | 
								return errors.New("事务记录不存在,无法确认支付")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return fmt.Errorf("查询续费日志失败: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 检查幂等性
 | 
				
			||||||
 | 
						if renewLog.Status == 2 { // 状态 2: 扣费成功
 | 
				
			||||||
 | 
							// 幂等性成功:已经被 Confirm 过了,直接返回成功,不需重复操作
 | 
				
			||||||
 | 
							e.Log.Warnf("Confirm重复调用: 续费ID %s 已是成功状态", renewalLogId)
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 流程错误检查:不能对已取消或失败的事务执行 Confirm
 | 
				
			||||||
 | 
						if renewLog.Status != 1 { // 状态 1: 预扣中/冻结
 | 
				
			||||||
 | 
							// 理论上不该发生。说明平台 B 的调用时序错误。
 | 
				
			||||||
 | 
							e.Log.Errorf("Confirm失败: 续费ID %s 状态为 %d,无法执行确认支付", renewalLogId, renewLog.Status)
 | 
				
			||||||
 | 
							return errors.New("事务状态不正确,无法确认支付")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return e.Orm.Transaction(func(tx *gorm.DB) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 1. 查找并更新续费日志状态 (状态 2: 扣费成功)
 | 
				
			||||||
 | 
							result := tx.Model(&models.SmsRenewalLog{}).
 | 
				
			||||||
 | 
								Where("id = ? AND status = ?", renewalLogId, 1). // 必须是预扣中状态
 | 
				
			||||||
 | 
								Update("status", 2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if result.RowsAffected == 0 {
 | 
				
			||||||
 | 
								return errors.New("续费日志状态不正确或已处理")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 2. **更新到期时间** (只有在 Confirm 阶段才能更新)
 | 
				
			||||||
 | 
							var phone models.SmsPhone
 | 
				
			||||||
 | 
							if err := tx.Model(&models.SmsPhone{}).Where("id = ?", phoneId).First(&phone).Error; err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							var targetTime time.Time
 | 
				
			||||||
 | 
							if renewLog.TargetTime != nil {
 | 
				
			||||||
 | 
								targetTime = *renewLog.TargetTime
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								targetTime = phone.ExpireTime.AddDate(0, 0, 30)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if err := tx.Model(&phone).Update("expire_time", targetTime).Error; err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return nil // TCC Confirm 成功
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// rollbackRenewalLog 回滚续费日志状态
 | 
				
			||||||
 | 
					func (e *SmsRenewalLog) rollbackRenewalLog(renewLogId int) error {
 | 
				
			||||||
 | 
						var renewLog models.SmsRenewalLog
 | 
				
			||||||
 | 
						if err := e.Orm.Where("id = ?", renewLogId).First(&renewLog).Error; err != nil {
 | 
				
			||||||
 | 
							if errors.Is(err, gorm.ErrRecordNotFound) {
 | 
				
			||||||
 | 
								e.Log.Errorf("回滚失败: 找不到续费记录ID %d", renewLogId)
 | 
				
			||||||
 | 
								return errors.New("事务记录不存在,无法回滚")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return fmt.Errorf("查询续费日志失败: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 幂等性检查:如果已经处于回滚状态,直接返回成功
 | 
				
			||||||
 | 
						if renewLog.Status == 3 {
 | 
				
			||||||
 | 
							e.Log.Warnf("回滚重复调用: 续费ID %d 已是回滚状态 (Status=3)", renewLogId)
 | 
				
			||||||
 | 
							return nil // 幂等性成功
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 流程检查:如果已经是成功扣费状态,不能执行普通回滚
 | 
				
			||||||
 | 
						if renewLog.Status == 2 {
 | 
				
			||||||
 | 
							e.Log.Errorf("回滚失败: 续费ID %d 已是成功扣费状态 (Status=2)", renewLogId)
 | 
				
			||||||
 | 
							return errors.New("事务已确认支付,无法执行取消回滚")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return e.Orm.Transaction(func(tx *gorm.DB) error {
 | 
				
			||||||
 | 
							// 1. 更新续费日志状态 (状态 1: 预扣中/冻结)
 | 
				
			||||||
 | 
							result := tx.Model(&models.SmsRenewalLog{}).
 | 
				
			||||||
 | 
								Where("id = ? AND status = ?", renewLogId, 1). // 必须是扣费成功状态
 | 
				
			||||||
 | 
								Update("status", 3)
 | 
				
			||||||
 | 
							if result.Error != nil {
 | 
				
			||||||
 | 
								logger.Errorf("回滚续费日志状态失败,续费日志ID: %d, 错误: %v", renewLogId, result.Error)
 | 
				
			||||||
 | 
								return result.Error
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if result.RowsAffected == 0 {
 | 
				
			||||||
 | 
								logger.Errorf("回滚续费日志状态失败,续费日志ID: %d", renewLogId)
 | 
				
			||||||
 | 
								return errors.New("续费日志状态不正确或已处理")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							result = tx.Exec("UPDATE member_balance SET balance = balance + ? WHERE user_id = ? ", renewLog.Amount, renewLog.UserId)
 | 
				
			||||||
 | 
							if result.Error != nil {
 | 
				
			||||||
 | 
								logger.Errorf("回滚续费日志状态失败,续费日志ID: %d, 错误: %v", renewLogId, result.Error)
 | 
				
			||||||
 | 
								return result.Error
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if result.RowsAffected == 0 {
 | 
				
			||||||
 | 
								return errors.New("余额不足")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// handleInsufficientBalance 处理余额不足情况
 | 
				
			||||||
 | 
					func (e *SmsRenewalLog) handleInsufficientBalance(phone models.SmsPhone) error {
 | 
				
			||||||
 | 
						params := map[string]interface{}{
 | 
				
			||||||
 | 
							"auto_renewal": 2,
 | 
				
			||||||
 | 
							"remark":       "余额不足,取消自动续费",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// 取消自动续费
 | 
				
			||||||
 | 
						if err := e.Orm.Model(&models.SmsPhone{}).
 | 
				
			||||||
 | 
							Where("id = ?", phone.Id).
 | 
				
			||||||
 | 
							Updates(params).Error; err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("余额不足,取消自动续费失败: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						phoneService := SmsPhone{}
 | 
				
			||||||
 | 
						phoneService.Orm = e.Orm
 | 
				
			||||||
 | 
						phoneService.Log = e.Log
 | 
				
			||||||
 | 
						// 调用平台取消续费接口
 | 
				
			||||||
 | 
						code := phoneService.ChangeAutoRenewManage(phone.PlatformCode, phone.ApiKey, phone.ActivationId, false)
 | 
				
			||||||
 | 
						if code != statuscode.Success {
 | 
				
			||||||
 | 
							params["auto_renewal"] = 1
 | 
				
			||||||
 | 
							params["remark"] = ""
 | 
				
			||||||
 | 
							// 如果平台取消失败,恢复自动续费状态
 | 
				
			||||||
 | 
							if err := e.Orm.Model(&models.SmsPhone{}).
 | 
				
			||||||
 | 
								Where("id = ?", phone.Id).
 | 
				
			||||||
 | 
								Updates(params).Error; err != nil {
 | 
				
			||||||
 | 
								e.Log.Errorf("恢复自动续费状态失败: %v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							e.Log.Errorf("平台取消自动续费失败,状态码: %d", code)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return errors.New("余额不足")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -115,7 +115,9 @@ func (e SmsServices) GetList(resp *[]dto.SmsServicesGetListResp) error {
 | 
				
			|||||||
	dictService := SysDictData{Service: e.Service}
 | 
						dictService := SysDictData{Service: e.Service}
 | 
				
			||||||
	dictDatas, _ := dictService.GetMapByType("sms_platform")
 | 
						dictDatas, _ := dictService.GetMapByType("sms_platform")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	keys := []string{"number_premium_daisysms", "number_premium_textverified", "long_number_premium_daisysms", "long_number_premium_textverified"}
 | 
						keys := []string{"number_premium_daisysms", "number_premium_textverified",
 | 
				
			||||||
 | 
							"long_number_premium_daisysms", "long_number_premium_textverified",
 | 
				
			||||||
 | 
							"renew_number_premium_daisysms", "renew_number_premium_textverified"}
 | 
				
			||||||
	configService := SysConfig{Service: e.Service}
 | 
						configService := SysConfig{Service: e.Service}
 | 
				
			||||||
	mapConfigs, err := configService.GetWithKeyForMap(keys)
 | 
						mapConfigs, err := configService.GetWithKeyForMap(keys)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@ -131,6 +133,7 @@ func (e SmsServices) GetList(resp *[]dto.SmsServicesGetListResp) error {
 | 
				
			|||||||
			PlatformCode:   item.PlatformCode,
 | 
								PlatformCode:   item.PlatformCode,
 | 
				
			||||||
			Price:          item.Price,
 | 
								Price:          item.Price,
 | 
				
			||||||
			LongPrice:      item.LongPrice,
 | 
								LongPrice:      item.LongPrice,
 | 
				
			||||||
 | 
								RenewLongPrice: item.LongPrice,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if dict, ok := dictDatas[respItem.PlatformCode]; ok {
 | 
							if dict, ok := dictDatas[respItem.PlatformCode]; ok {
 | 
				
			||||||
@ -157,6 +160,15 @@ func (e SmsServices) GetList(resp *[]dto.SmsServicesGetListResp) error {
 | 
				
			|||||||
					respItem.LongPrice = respItem.LongPrice.Mul(decimal.NewFromInt(100).Add(premium).Div(decimal.NewFromInt(100))).Truncate(2)
 | 
										respItem.LongPrice = respItem.LongPrice.Mul(decimal.NewFromInt(100).Add(premium).Div(decimal.NewFromInt(100))).Truncate(2)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if config, ok := mapConfigs["renew_number_premium_daisysms"]; ok {
 | 
				
			||||||
 | 
									premium, err := decimal.NewFromString(config.ConfigValue)
 | 
				
			||||||
 | 
									if err != nil || premium.IsZero() {
 | 
				
			||||||
 | 
										e.Log.Errorf("浮动百分比为0,或者是转换错误 %s", config.ConfigValue)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									respItem.RenewLongPrice = respItem.RenewLongPrice.Mul(decimal.NewFromInt(100).Add(premium).Div(decimal.NewFromInt(100))).Truncate(2)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		case global.SmsPlatformTextVerified:
 | 
							case global.SmsPlatformTextVerified:
 | 
				
			||||||
			if config, ok := mapConfigs["number_premium_textverified"]; ok {
 | 
								if config, ok := mapConfigs["number_premium_textverified"]; ok {
 | 
				
			||||||
				premium, err := decimal.NewFromString(config.ConfigValue)
 | 
									premium, err := decimal.NewFromString(config.ConfigValue)
 | 
				
			||||||
@ -176,6 +188,17 @@ func (e SmsServices) GetList(resp *[]dto.SmsServicesGetListResp) error {
 | 
				
			|||||||
					respItem.LongPrice = respItem.LongPrice.Mul(decimal.NewFromInt(100).Add(premium).Div(decimal.NewFromInt(100))).Truncate(2)
 | 
										respItem.LongPrice = respItem.LongPrice.Mul(decimal.NewFromInt(100).Add(premium).Div(decimal.NewFromInt(100))).Truncate(2)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								//续期价格浮动
 | 
				
			||||||
 | 
								if config, ok := mapConfigs["renew_number_premium_textverified"]; ok {
 | 
				
			||||||
 | 
									premium, err := decimal.NewFromString(config.ConfigValue)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if err != nil || premium.IsZero() {
 | 
				
			||||||
 | 
										e.Log.Errorf("浮动百分比为0,或者是转换错误 %s", config.ConfigValue)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									respItem.RenewLongPrice = respItem.RenewLongPrice.Mul(decimal.NewFromInt(100).Add(premium).Div(decimal.NewFromInt(100))).Truncate(2)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		*resp = append(*resp, respItem)
 | 
							*resp = append(*resp, respItem)
 | 
				
			||||||
 | 
				
			|||||||
@ -29,58 +29,10 @@ type SmsTextVerified struct {
 | 
				
			|||||||
	service.Service
 | 
						service.Service
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 获取收到的验证码
 | 
					// 错误
 | 
				
			||||||
// messageId 短效或长效号码的ID
 | 
					var (
 | 
				
			||||||
// typ 0-短效 1-长效
 | 
						LogNotFund = errors.New("记录不存在")
 | 
				
			||||||
func (e SmsTextVerified) GetCode(messageId string, typ int, unixTime int64) (string, int) {
 | 
					)
 | 
				
			||||||
	reservationType := ""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if typ == 0 {
 | 
					 | 
				
			||||||
		reservationType = "verification"
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		reservationType = "renewable"
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	url := fmt.Sprintf(getSmsCode, messageId, reservationType)
 | 
					 | 
				
			||||||
	client, code := e.GetTextVerifiedAuthClient()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if code != http.StatusOK {
 | 
					 | 
				
			||||||
		e.Log.Errorf("获取授权请求失败,status %d", code)
 | 
					 | 
				
			||||||
		return "", code
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var parsedCode string
 | 
					 | 
				
			||||||
	resp := dto.TextVerifiedSmsResp{}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_, err := client.Get(url, nil, &resp)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		e.Log.Errorf("获取验证码失败, error: %v", err)
 | 
					 | 
				
			||||||
		return "", statuscode.ServerError
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if len(resp.Data) > 0 {
 | 
					 | 
				
			||||||
		for _, v := range resp.Data {
 | 
					 | 
				
			||||||
			// 2. 解析时间字符串
 | 
					 | 
				
			||||||
			// time.RFC3339Nano 是一个预定义的格式常量,
 | 
					 | 
				
			||||||
			// 它能够解析包含纳秒和时区信息的 ISO 8601 字符串。
 | 
					 | 
				
			||||||
			t, err := time.Parse(time.RFC3339Nano, v.CreatedAt)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				fmt.Println("时间解析失败:", err)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// 3. 将解析后的 time.Time 对象转换为 Unix 时间戳
 | 
					 | 
				
			||||||
			// t.Unix() 返回以秒为单位的整数时间戳
 | 
					 | 
				
			||||||
			unixTimestamp := t.Unix()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if unixTimestamp >= unixTime {
 | 
					 | 
				
			||||||
				parsedCode = v.ParsedCode
 | 
					 | 
				
			||||||
				break
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return parsedCode, http.StatusOK
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	loginUrl                = "/api/pub/v2/auth"
 | 
						loginUrl                = "/api/pub/v2/auth"
 | 
				
			||||||
@ -97,8 +49,126 @@ const (
 | 
				
			|||||||
	renewRental             = "/api/pub/v2/reservations/rental/renewable/%s/renew"  //长效续期
 | 
						renewRental             = "/api/pub/v2/reservations/rental/renewable/%s/renew"  //长效续期
 | 
				
			||||||
	updateRentalRenewStatus = "/api/pub/v2/reservations/rental/renewable/%s"        // 更改续租状态
 | 
						updateRentalRenewStatus = "/api/pub/v2/reservations/rental/renewable/%s"        // 更改续租状态
 | 
				
			||||||
	getSmsCode              = "/api/pub/v2/sms?reservationId=%s&reservationType=%s" //获取短信验证码
 | 
						getSmsCode              = "/api/pub/v2/sms?reservationId=%s&reservationType=%s" //获取短信验证码
 | 
				
			||||||
 | 
						getNumbers              = "/api/pub/v2/reservations/rental/renewable"           //获取长效号码列表 Get
 | 
				
			||||||
 | 
						manualRenewal           = "/api/pub/v2/billing-cycles/%s/renew"                 //手动续期 POST
 | 
				
			||||||
 | 
						getBillingCycles        = "/api/pub/v2/billing-cycles/%s"                       //获取计费周期详情 GET
 | 
				
			||||||
 | 
						rentalList              = "/api/pub/v2/reservations/rental/renewable"           //长效号码列表
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 获取收到的验证码
 | 
				
			||||||
 | 
					// messageId 短效或长效号码的ID
 | 
				
			||||||
 | 
					// typ 0-短效 1-长效
 | 
				
			||||||
 | 
					func (e SmsTextVerified) GetCode(messageId string, typ int, userId int, service, serviceCode string, apiInfo *dto.SmsPlatformKeyQueueDto) (string, int) {
 | 
				
			||||||
 | 
						reservationType := ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if typ == 0 {
 | 
				
			||||||
 | 
							reservationType = "verification"
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							reservationType = "renewable"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						url := fmt.Sprintf(getSmsCode, messageId, reservationType)
 | 
				
			||||||
 | 
						client, code := e.GetTextVerifiedAuthClient(apiInfo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if code != http.StatusOK {
 | 
				
			||||||
 | 
							e.Log.Errorf("获取授权请求失败,status %d", code)
 | 
				
			||||||
 | 
							return "", code
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var parsedCode string
 | 
				
			||||||
 | 
						resp := dto.TextVerifiedSmsResp{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err := client.Get(url, nil, &resp)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("获取验证码失败, error: %v", err)
 | 
				
			||||||
 | 
							return "", statuscode.ServerError
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						messageIds := []string{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(resp.Data) > 0 {
 | 
				
			||||||
 | 
							for _, v := range resp.Data {
 | 
				
			||||||
 | 
								messageIds = append(messageIds, v.Id)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(messageIds) > 0 {
 | 
				
			||||||
 | 
							receiveLogs := make([]models.SmsReceiveLog, 0)
 | 
				
			||||||
 | 
							receiveMaps := make(map[string]string, 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if err := e.Orm.Model(&models.SmsReceiveLog{}).
 | 
				
			||||||
 | 
								Where("message_id in ?", messageIds).
 | 
				
			||||||
 | 
								Select("message_id").
 | 
				
			||||||
 | 
								Find(&receiveLogs).Error; err != nil {
 | 
				
			||||||
 | 
								e.Log.Errorf("获取短信接收记录失败, error: %v", err)
 | 
				
			||||||
 | 
								return "", statuscode.ServerError
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for _, v := range receiveLogs {
 | 
				
			||||||
 | 
								receiveMaps[v.MessageId] = v.MessageId
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 查找最新验证码并存储所有新验证码
 | 
				
			||||||
 | 
							var latestSms *dto.TextVerifiedSmsItemResp
 | 
				
			||||||
 | 
							var latestTime time.Time
 | 
				
			||||||
 | 
							var newSmsData []dto.TextVerifiedSmsItemResp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 遍历所有验证码数据
 | 
				
			||||||
 | 
							for _, v := range resp.Data {
 | 
				
			||||||
 | 
								// 解析时间字符串
 | 
				
			||||||
 | 
								t, err := time.Parse(time.RFC3339Nano, v.CreatedAt)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									e.Log.Errorf("时间解析失败: %v", err)
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// 检查是否为新验证码(数据库中不存在)
 | 
				
			||||||
 | 
								if _, ok := receiveMaps[v.Id]; !ok {
 | 
				
			||||||
 | 
									newSmsData = append(newSmsData, v)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// 选择最新的验证码(包括已存在的)
 | 
				
			||||||
 | 
								if latestSms == nil || t.After(latestTime) {
 | 
				
			||||||
 | 
									latestSms = &v
 | 
				
			||||||
 | 
									latestTime = t
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 批量保存所有新验证码到数据库
 | 
				
			||||||
 | 
							if len(newSmsData) > 0 {
 | 
				
			||||||
 | 
								var receiveLogs []models.SmsReceiveLog
 | 
				
			||||||
 | 
								for _, sms := range newSmsData {
 | 
				
			||||||
 | 
									receiveLog := models.SmsReceiveLog{
 | 
				
			||||||
 | 
										UserId:      userId,
 | 
				
			||||||
 | 
										Service:     service,
 | 
				
			||||||
 | 
										ServiceCode: serviceCode,
 | 
				
			||||||
 | 
										MessageId:   sms.Id,
 | 
				
			||||||
 | 
										Phone:       "1" + sms.To,
 | 
				
			||||||
 | 
										Code:        sms.ParsedCode,
 | 
				
			||||||
 | 
										Status:      2,
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									receiveLogs = append(receiveLogs, receiveLog)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// 批量插入数据库
 | 
				
			||||||
 | 
								if err := e.Orm.Create(&receiveLogs).Error; err != nil {
 | 
				
			||||||
 | 
									e.Log.Errorf("批量创建短信接收记录失败, error: %v", err)
 | 
				
			||||||
 | 
									return "", statuscode.ServerError
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								e.Log.Infof("成功存储 %d 条新验证码记录", len(newSmsData))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// 返回最新的验证码
 | 
				
			||||||
 | 
								if latestSms != nil {
 | 
				
			||||||
 | 
									parsedCode = latestSms.ParsedCode
 | 
				
			||||||
 | 
									e.Log.Infof("返回最新验证码: %s, messageId: %s, 时间: %s", parsedCode, latestSms.Id, latestSms.CreatedAt)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return parsedCode, http.StatusOK
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var idPattern = regexp.MustCompile(`^[a-zA-Z0-9_]{28}$`)
 | 
					var idPattern = regexp.MustCompile(`^[a-zA-Z0-9_]{28}$`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 获取授权失败
 | 
					// 获取授权失败
 | 
				
			||||||
@ -106,13 +176,13 @@ var ErrUnAuth = errors.New("未授权,请检查配置的账号和密码")
 | 
				
			|||||||
var ErrOutOfStockOrUnavailable = errors.New("缺货或服务不可用")
 | 
					var ErrOutOfStockOrUnavailable = errors.New("缺货或服务不可用")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Login 登录
 | 
					// Login 登录
 | 
				
			||||||
func (e *SmsTextVerified) Login() (string, error) {
 | 
					func (e *SmsTextVerified) Login(apiInfo *dto.SmsPlatformKeyQueueDto) (string, error) {
 | 
				
			||||||
	resp := dto.TextVerifiedLoginResp{}
 | 
						resp := dto.TextVerifiedLoginResp{}
 | 
				
			||||||
	var token string
 | 
						var token string
 | 
				
			||||||
	client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.SmsTextVerified.Url, nil)
 | 
						client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.SmsTextVerified.Url, nil)
 | 
				
			||||||
	headers := map[string]string{
 | 
						headers := map[string]string{
 | 
				
			||||||
		"X-API-USERNAME": config.ExtConfig.SmsTextVerified.UserName,
 | 
							"X-API-USERNAME": apiInfo.Account,
 | 
				
			||||||
		"X-API-KEY":      config.ExtConfig.SmsTextVerified.ApiKey,
 | 
							"X-API-KEY":      apiInfo.ApiKey,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	_, err1 := client.Post(loginUrl, nil, headers, &resp)
 | 
						_, err1 := client.Post(loginUrl, nil, headers, &resp)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -130,7 +200,8 @@ func (e *SmsTextVerified) Login() (string, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if resp.ExpiresIn >= 10 {
 | 
						if resp.ExpiresIn >= 10 {
 | 
				
			||||||
		resp.ExpiresIn = resp.ExpiresIn - 10 // 提前10秒过期
 | 
							resp.ExpiresIn = resp.ExpiresIn - 10 // 提前10秒过期
 | 
				
			||||||
		if err := redishelper.DefaultRedis.SetStringExpire(global.TextVerifiedToken, token, time.Duration(resp.ExpiresIn)*time.Second); err != nil {
 | 
							key := fmt.Sprintf(global.TextVerifiedToken, apiInfo.ApiKey)
 | 
				
			||||||
 | 
							if err := redishelper.DefaultRedis.SetStringExpire(key, token, time.Duration(resp.ExpiresIn)*time.Second); err != nil {
 | 
				
			||||||
			e.Log.Errorf("TextVerified登录失败,缓存Token失败 error: %v", err)
 | 
								e.Log.Errorf("TextVerified登录失败,缓存Token失败 error: %v", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -139,15 +210,16 @@ func (e *SmsTextVerified) Login() (string, error) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 获取token
 | 
					// 获取token
 | 
				
			||||||
func (e *SmsTextVerified) GetToken() (string, error) {
 | 
					func (e *SmsTextVerified) GetToken(apiInfo *dto.SmsPlatformKeyQueueDto) (string, error) {
 | 
				
			||||||
	token, err := redishelper.DefaultRedis.GetString(global.TextVerifiedToken)
 | 
						key := fmt.Sprintf(global.TextVerifiedToken, apiInfo.ApiKey)
 | 
				
			||||||
 | 
						token, err := redishelper.DefaultRedis.GetString(key)
 | 
				
			||||||
	if err != nil && errors.Is(err, redis.Nil) {
 | 
						if err != nil && errors.Is(err, redis.Nil) {
 | 
				
			||||||
		// token不存在,重新登录获取
 | 
							// token不存在,重新登录获取
 | 
				
			||||||
		return e.Login()
 | 
							return e.Login(apiInfo)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if token == "" {
 | 
						if token == "" {
 | 
				
			||||||
		return e.Login()
 | 
							return e.Login(apiInfo)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return token, nil
 | 
						return token, nil
 | 
				
			||||||
@ -163,9 +235,9 @@ func (e *SmsTextVerified) GetAreas() ([]string, error) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 获取服务列表
 | 
					// 获取服务列表
 | 
				
			||||||
func (e *SmsTextVerified) GetServices() ([]dto.TextVerifiedServeResp, error) {
 | 
					func (e *SmsTextVerified) GetServices(apiInfo *dto.SmsPlatformKeyQueueDto) ([]dto.TextVerifiedServeResp, error) {
 | 
				
			||||||
	client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.SmsTextVerified.Url, nil)
 | 
						client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.SmsTextVerified.Url, nil)
 | 
				
			||||||
	headers, err := e.GetAuthHeader()
 | 
						headers, err := e.GetAuthHeader(apiInfo)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -182,7 +254,15 @@ func (e *SmsTextVerified) GetServices() ([]dto.TextVerifiedServeResp, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// 同步服务列表
 | 
					// 同步服务列表
 | 
				
			||||||
func (e *SmsTextVerified) SyncServices() error {
 | 
					func (e *SmsTextVerified) SyncServices() error {
 | 
				
			||||||
	services, err := e.GetServices()
 | 
						// 获取API信息
 | 
				
			||||||
 | 
						smsPlatformKeyRedis := NewSmsPlatformKeyRedis(e.Orm, e.Log)
 | 
				
			||||||
 | 
						apiInfo, err := smsPlatformKeyRedis.GetRoundRobinKey(global.SmsPlatformTextVerified)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("获取API信息失败: %v", err)
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						services, err := e.GetServices(apiInfo)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		e.Log.Errorf("获取服务列表失败: %v", err)
 | 
							e.Log.Errorf("获取服务列表失败: %v", err)
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
@ -259,17 +339,25 @@ func (e *SmsTextVerified) SyncPrices() error {
 | 
				
			|||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						smsPlatformKeyRedis := NewSmsPlatformKeyRedis(e.Orm, e.Log)
 | 
				
			||||||
 | 
						apiInfo, err := smsPlatformKeyRedis.GetRoundRobinKey(global.SmsPlatformTextVerified)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("获取API信息失败: %v", err)
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, v := range services {
 | 
						for _, v := range services {
 | 
				
			||||||
		params := map[string]interface{}{}
 | 
							params := map[string]interface{}{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		price, code := e.GetPrice(0, v.Code)
 | 
							price, code := e.GetPrice(0, v.Code, apiInfo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if code == http.StatusOK {
 | 
							if code == http.StatusOK {
 | 
				
			||||||
			params["price"] = price
 | 
								params["price"] = price
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		time.Sleep(time.Microsecond * 500) // 避免请求过快被限制
 | 
							time.Sleep(time.Microsecond * 500) // 避免请求过快被限制
 | 
				
			||||||
		longPrice, code := e.GetPrice(1, v.Code)
 | 
							longPrice, code := e.GetPrice(1, v.Code, apiInfo)
 | 
				
			||||||
		if code == http.StatusOK {
 | 
							if code == http.StatusOK {
 | 
				
			||||||
			params["long_price"] = longPrice
 | 
								params["long_price"] = longPrice
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@ -284,12 +372,36 @@ func (e *SmsTextVerified) SyncPrices() error {
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 获取号码并唤醒
 | 
				
			||||||
 | 
					func (e *SmsTextVerified) GetNumberAndWakeUp(tye int, serviceCode string, price decimal.Decimal, period int, apiInfo *dto.SmsPlatformKeyQueueDto) (dto.SmsPhoneGetPhoneResp, int) {
 | 
				
			||||||
 | 
						resp, code := e.GetNumberForApi(tye, serviceCode, price, period, apiInfo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if code != statuscode.Success {
 | 
				
			||||||
 | 
							return resp, code
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wakeUpResp, code := e.getExtraActivation(resp.Id, apiInfo, 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if code != statuscode.Success {
 | 
				
			||||||
 | 
							//唤醒失败不退出
 | 
				
			||||||
 | 
							now := time.Now()
 | 
				
			||||||
 | 
							resp.StartTime = &now
 | 
				
			||||||
 | 
							resp.EndTime = &now
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							resp.MessageId = wakeUpResp.Id
 | 
				
			||||||
 | 
							resp.StartTime = wakeUpResp.UsageWindowStart
 | 
				
			||||||
 | 
							resp.EndTime = wakeUpResp.UsageWindowEnd
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return resp, code
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 号码租赁
 | 
					// 号码租赁
 | 
				
			||||||
// getType 0-短效 1-长效
 | 
					// getType 0-短效 1-长效
 | 
				
			||||||
// service 服务code
 | 
					// service 服务code
 | 
				
			||||||
// maxPrice 最大价格
 | 
					// maxPrice 最大价格
 | 
				
			||||||
// period 时长(月=30天)
 | 
					// period 时长(月=30天)
 | 
				
			||||||
func (e SmsTextVerified) GetNumberForApi(typ int, serviceCode string, price decimal.Decimal, period int) (dto.SmsPhoneGetPhoneResp, int) {
 | 
					func (e *SmsTextVerified) GetNumberForApi(typ int, serviceCode string, price decimal.Decimal, period int, apiInfo *dto.SmsPlatformKeyQueueDto) (dto.SmsPhoneGetPhoneResp, int) {
 | 
				
			||||||
	//这个平台的所有号码都是美国号码 默认国家代码都是 +1
 | 
						//这个平台的所有号码都是美国号码 默认国家代码都是 +1
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	var createResp dto.TextVerifiedResp
 | 
						var createResp dto.TextVerifiedResp
 | 
				
			||||||
@ -297,10 +409,10 @@ func (e SmsTextVerified) GetNumberForApi(typ int, serviceCode string, price deci
 | 
				
			|||||||
	switch typ {
 | 
						switch typ {
 | 
				
			||||||
	case 0:
 | 
						case 0:
 | 
				
			||||||
		var code int
 | 
							var code int
 | 
				
			||||||
		createResp, code = e.CreateVerification(serviceCode)
 | 
							createResp, code = e.CreateVerification(serviceCode, apiInfo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if code == statuscode.Success && createResp.Href != "" {
 | 
							if code == statuscode.Success && createResp.Href != "" {
 | 
				
			||||||
			bytes, err := e.doRequest(&createResp)
 | 
								bytes, err := e.doRequest(&createResp, apiInfo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				e.Log.Errorf("短效号码 获取号码失败:%v", e)
 | 
									e.Log.Errorf("短效号码 获取号码失败:%v", e)
 | 
				
			||||||
@ -327,11 +439,11 @@ func (e SmsTextVerified) GetNumberForApi(typ int, serviceCode string, price deci
 | 
				
			|||||||
			return result, statuscode.ServerError
 | 
								return result, statuscode.ServerError
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	case 1:
 | 
						case 1:
 | 
				
			||||||
		createResp, err = e.CreateRental(serviceCode)
 | 
							createResp, err = e.CreateRental(serviceCode, apiInfo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if err == nil && createResp.Href != "" {
 | 
							if err == nil && createResp.Href != "" {
 | 
				
			||||||
			saleId := getIdByUrl(createResp.Href)
 | 
								saleId := getIdByUrl(createResp.Href)
 | 
				
			||||||
			bytes, err := e.doRequest(&createResp)
 | 
								bytes, err := e.doRequest(&createResp, apiInfo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				e.Log.Errorf("通过销售id [%s] 获取号码失败:%v", saleId, err)
 | 
									e.Log.Errorf("通过销售id [%s] 获取号码失败:%v", saleId, err)
 | 
				
			||||||
@ -358,13 +470,14 @@ func (e SmsTextVerified) GetNumberForApi(typ int, serviceCode string, price deci
 | 
				
			|||||||
					return result, statuscode.ServerError
 | 
										return result, statuscode.ServerError
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				detail, code := e.GetRentalDetail(result.Id)
 | 
									detail, code := e.GetRentalDetail(result.Id, apiInfo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if code != statuscode.Success {
 | 
									if code != statuscode.Success {
 | 
				
			||||||
					return result, code
 | 
										return result, code
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				result.Phone = "1" + detail.Number
 | 
									result.Phone = "1" + detail.Number
 | 
				
			||||||
 | 
									result.BillingCycleId = detail.BillingCycleId
 | 
				
			||||||
				endTime := resp.UpdatedAt.AddDate(0, 0, 30)
 | 
									endTime := resp.UpdatedAt.AddDate(0, 0, 30)
 | 
				
			||||||
				result.EndAt = &(endTime) // 30天后过期
 | 
									result.EndAt = &(endTime) // 30天后过期
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -394,13 +507,13 @@ func (e SmsTextVerified) GetNumberForApi(typ int, serviceCode string, price deci
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 执行查询请求
 | 
					// 执行查询请求
 | 
				
			||||||
func (e *SmsTextVerified) doRequest(resp *dto.TextVerifiedResp) ([]byte, error) {
 | 
					func (e *SmsTextVerified) doRequest(resp *dto.TextVerifiedResp, apiInfo *dto.SmsPlatformKeyQueueDto) ([]byte, error) {
 | 
				
			||||||
	bytes := []byte{}
 | 
						bytes := []byte{}
 | 
				
			||||||
	if resp.Href == "" {
 | 
						if resp.Href == "" {
 | 
				
			||||||
		return bytes, errors.New("成功请求没有返回url")
 | 
							return bytes, errors.New("成功请求没有返回url")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	header, err := e.GetAuthHeader()
 | 
						header, err := e.GetAuthHeader(apiInfo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return bytes, fmt.Errorf("获取授权失败:%v", err)
 | 
							return bytes, fmt.Errorf("获取授权失败:%v", err)
 | 
				
			||||||
@ -418,7 +531,7 @@ func (e *SmsTextVerified) doRequest(resp *dto.TextVerifiedResp) ([]byte, error)
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 长号码租赁
 | 
					// 长号码租赁
 | 
				
			||||||
func (e *SmsTextVerified) CreateRental(serviceCode string) (dto.TextVerifiedResp, error) {
 | 
					func (e *SmsTextVerified) CreateRental(serviceCode string, apiInfo *dto.SmsPlatformKeyQueueDto) (dto.TextVerifiedResp, error) {
 | 
				
			||||||
	req := dto.TextVerifiedCreateRewalReq{
 | 
						req := dto.TextVerifiedCreateRewalReq{
 | 
				
			||||||
		ServiceName: serviceCode,
 | 
							ServiceName: serviceCode,
 | 
				
			||||||
		Capability:  "sms",
 | 
							Capability:  "sms",
 | 
				
			||||||
@ -430,7 +543,7 @@ func (e *SmsTextVerified) CreateRental(serviceCode string) (dto.TextVerifiedResp
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	resp := dto.TextVerifiedResp{}
 | 
						resp := dto.TextVerifiedResp{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	client, code := e.GetTextVerifiedAuthClient()
 | 
						client, code := e.GetTextVerifiedAuthClient(apiInfo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if code != statuscode.Success {
 | 
						if code != statuscode.Success {
 | 
				
			||||||
		e.Log.Errorf("获取头信息失败 error: %d", code)
 | 
							e.Log.Errorf("获取头信息失败 error: %d", code)
 | 
				
			||||||
@ -458,10 +571,10 @@ func (e *SmsTextVerified) CreateRental(serviceCode string) (dto.TextVerifiedResp
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 获取长效号码详情
 | 
					// 获取长效号码详情
 | 
				
			||||||
func (e *SmsTextVerified) GetRentalDetail(id string) (dto.VerificationRentalDetailResp, int) {
 | 
					func (e *SmsTextVerified) GetRentalDetail(id string, apiInfo *dto.SmsPlatformKeyQueueDto) (dto.VerificationRentalDetailResp, int) {
 | 
				
			||||||
	result := dto.VerificationRentalDetailResp{}
 | 
						result := dto.VerificationRentalDetailResp{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	client, code := e.GetTextVerifiedAuthClient()
 | 
						client, code := e.GetTextVerifiedAuthClient(apiInfo)
 | 
				
			||||||
	if code != statuscode.Success {
 | 
						if code != statuscode.Success {
 | 
				
			||||||
		return result, code
 | 
							return result, code
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -477,12 +590,13 @@ func (e *SmsTextVerified) GetRentalDetail(id string) (dto.VerificationRentalDeta
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 唤醒号码
 | 
					// 唤醒号码
 | 
				
			||||||
// returns activationId,messageId, code
 | 
					// returns activationId,messageId,startTime(可用开始时间),endTime(可用结束时间), code,
 | 
				
			||||||
func (e SmsTextVerified) getExtraActivation(id string) (string, string, int) {
 | 
					func (e SmsTextVerified) getExtraActivation(id string, apiInfo *dto.SmsPlatformKeyQueueDto, retry int) (dto.TextVerifiedWakeUpResp, int) {
 | 
				
			||||||
	client, code := e.GetTextVerifiedAuthClient()
 | 
						client, code := e.GetTextVerifiedAuthClient(apiInfo)
 | 
				
			||||||
 | 
						result := dto.TextVerifiedWakeUpResp{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if code != statuscode.Success {
 | 
						if code != statuscode.Success {
 | 
				
			||||||
		return "", "", code
 | 
							return result, code
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	req := dto.TextVerifiedWakeUpReq{
 | 
						req := dto.TextVerifiedWakeUpReq{
 | 
				
			||||||
@ -496,50 +610,59 @@ func (e SmsTextVerified) getExtraActivation(id string) (string, string, int) {
 | 
				
			|||||||
	status, err := client.Post(wakeUp, req, headers, &resp)
 | 
						status, err := client.Post(wakeUp, req, headers, &resp)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if strings.Contains(err.Error(), "Line is currently busy. Please try this line again later") && retry < 3 {
 | 
				
			||||||
 | 
								time.Sleep(300 * time.Millisecond)
 | 
				
			||||||
 | 
								return e.getExtraActivation(id, apiInfo, retry+1)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
			e.Log.Errorf("唤醒号码失败 id:%s error: %v", id, err)
 | 
								e.Log.Errorf("唤醒号码失败 id:%s error: %v", id, err)
 | 
				
			||||||
		return "", "", statuscode.ServerError
 | 
								return result, statuscode.ServerError
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	} else if status == http.StatusCreated || status == http.StatusOK {
 | 
						} else if status == http.StatusCreated || status == http.StatusOK {
 | 
				
			||||||
		if resp.Method != "" && resp.Href != "" {
 | 
							if resp.Method != "" && resp.Href != "" {
 | 
				
			||||||
			bytes, err := e.doRequest(&resp)
 | 
								bytes, err := e.doRequest(&resp, apiInfo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				e.Log.Errorf("唤醒号码失败 id:%s error: %v", id, err)
 | 
									e.Log.Errorf("唤醒号码失败 id:%s error: %v", id, err)
 | 
				
			||||||
				return "", "", statuscode.ServerError
 | 
									return result, statuscode.ServerError
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			detailResp := dto.TextVerifiedWakeUpResp{}
 | 
								detailResp := dto.TextVerifiedWakeUpResp{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if err := sonic.Unmarshal(bytes, &detailResp); err != nil {
 | 
								if err := sonic.Unmarshal(bytes, &detailResp); err != nil {
 | 
				
			||||||
				e.Log.Errorf("唤醒号码反序列化失败 id:%s error: %v", id, err)
 | 
									e.Log.Errorf("唤醒号码反序列化失败 id:%s error: %v", id, err)
 | 
				
			||||||
				return "", "", statuscode.ServerError
 | 
									return result, statuscode.ServerError
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			return detailResp.ReservationId, detailResp.Id, statuscode.Success
 | 
								result.Id = detailResp.Id
 | 
				
			||||||
 | 
								result.ReservationId = detailResp.ReservationId
 | 
				
			||||||
 | 
								result.UsageWindowEnd = detailResp.UsageWindowEnd
 | 
				
			||||||
 | 
								result.UsageWindowStart = detailResp.UsageWindowStart
 | 
				
			||||||
 | 
								return result, statuscode.Success
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			e.Log.Errorf("唤醒号码失败 id:%s error: %v", id, err)
 | 
								e.Log.Errorf("唤醒号码失败 id:%s error: %v", id, err)
 | 
				
			||||||
			return "", "", statuscode.ServerError
 | 
								return result, statuscode.ServerError
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return "", "", statuscode.ServerError
 | 
						return result, statuscode.ServerError
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 获取价格
 | 
					// 获取价格
 | 
				
			||||||
// getType 0-短效 1-长效
 | 
					// getType 0-短效 1-长效
 | 
				
			||||||
// returns decimal.Decimal(单价), int(状态code)
 | 
					// returns decimal.Decimal(单价), int(状态code)
 | 
				
			||||||
func (e *SmsTextVerified) GetPrice(typ int, serviceName string) (decimal.Decimal, int) {
 | 
					func (e *SmsTextVerified) GetPrice(typ int, serviceName string, apiInfo *dto.SmsPlatformKeyQueueDto) (decimal.Decimal, int) {
 | 
				
			||||||
	switch typ {
 | 
						switch typ {
 | 
				
			||||||
	case 1:
 | 
						case 1:
 | 
				
			||||||
		return e.GetRentalPrice(serviceName)
 | 
							return e.GetRentalPrice(serviceName, apiInfo)
 | 
				
			||||||
	case 0:
 | 
						case 0:
 | 
				
			||||||
		return e.GetVerificationPrice(serviceName)
 | 
							return e.GetVerificationPrice(serviceName, apiInfo)
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		return decimal.Zero, statuscode.SmsInvalidType
 | 
							return decimal.Zero, statuscode.SmsInvalidType
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 获取长租价格
 | 
					// 获取长租价格
 | 
				
			||||||
func (e *SmsTextVerified) GetRentalPrice(serviceName string) (decimal.Decimal, int) {
 | 
					func (e *SmsTextVerified) GetRentalPrice(serviceName string, apiInfo *dto.SmsPlatformKeyQueueDto) (decimal.Decimal, int) {
 | 
				
			||||||
	req := dto.TextVerifiedPriceReq{
 | 
						req := dto.TextVerifiedPriceReq{
 | 
				
			||||||
		ServiceName: serviceName,
 | 
							ServiceName: serviceName,
 | 
				
			||||||
		Capability:  "sms",
 | 
							Capability:  "sms",
 | 
				
			||||||
@ -548,7 +671,7 @@ func (e *SmsTextVerified) GetRentalPrice(serviceName string) (decimal.Decimal, i
 | 
				
			|||||||
		Duration:    "thirtyDay",
 | 
							Duration:    "thirtyDay",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	client, code := e.GetTextVerifiedAuthClient()
 | 
						client, code := e.GetTextVerifiedAuthClient(apiInfo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if code != statuscode.Success {
 | 
						if code != statuscode.Success {
 | 
				
			||||||
		e.Log.Errorf("获取授权请求失败,status %d", code)
 | 
							e.Log.Errorf("获取授权请求失败,status %d", code)
 | 
				
			||||||
@ -571,7 +694,7 @@ func (e *SmsTextVerified) GetRentalPrice(serviceName string) (decimal.Decimal, i
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 获取单次验证码价格
 | 
					// 获取单次验证码价格
 | 
				
			||||||
func (e *SmsTextVerified) GetVerificationPrice(sericeName string) (decimal.Decimal, int) {
 | 
					func (e *SmsTextVerified) GetVerificationPrice(sericeName string, apiInfo *dto.SmsPlatformKeyQueueDto) (decimal.Decimal, int) {
 | 
				
			||||||
	params := map[string]interface{}{
 | 
						params := map[string]interface{}{
 | 
				
			||||||
		"serviceName": sericeName,
 | 
							"serviceName": sericeName,
 | 
				
			||||||
		"areaCode":    false,
 | 
							"areaCode":    false,
 | 
				
			||||||
@ -580,7 +703,7 @@ func (e *SmsTextVerified) GetVerificationPrice(sericeName string) (decimal.Decim
 | 
				
			|||||||
		"numberType":  "mobile",
 | 
							"numberType":  "mobile",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	client, code := e.GetTextVerifiedAuthClient()
 | 
						client, code := e.GetTextVerifiedAuthClient(apiInfo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if code != statuscode.Success {
 | 
						if code != statuscode.Success {
 | 
				
			||||||
		e.Log.Errorf("获取授权请求失败,status %d", code)
 | 
							e.Log.Errorf("获取授权请求失败,status %d", code)
 | 
				
			||||||
@ -602,14 +725,14 @@ func (e *SmsTextVerified) GetVerificationPrice(sericeName string) (decimal.Decim
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 单次接收
 | 
					// 单次接收
 | 
				
			||||||
func (e *SmsTextVerified) CreateVerification(serviceCode string) (dto.TextVerifiedResp, int) {
 | 
					func (e *SmsTextVerified) CreateVerification(serviceCode string, apiInfo *dto.SmsPlatformKeyQueueDto) (dto.TextVerifiedResp, int) {
 | 
				
			||||||
	req := dto.TextVerifiedCreateRewalReq{
 | 
						req := dto.TextVerifiedCreateRewalReq{
 | 
				
			||||||
		ServiceName: serviceCode,
 | 
							ServiceName: serviceCode,
 | 
				
			||||||
		Capability:  "sms",
 | 
							Capability:  "sms",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	resp := dto.TextVerifiedResp{}
 | 
						resp := dto.TextVerifiedResp{}
 | 
				
			||||||
	client, code := e.GetTextVerifiedAuthClient()
 | 
						client, code := e.GetTextVerifiedAuthClient(apiInfo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if code != statuscode.Success {
 | 
						if code != statuscode.Success {
 | 
				
			||||||
		return resp, code
 | 
							return resp, code
 | 
				
			||||||
@ -634,8 +757,8 @@ func (e *SmsTextVerified) CreateVerification(serviceCode string) (dto.TextVerifi
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// 取消验证码
 | 
					// 取消验证码
 | 
				
			||||||
// typ 0-短效 1-长效
 | 
					// typ 0-短效 1-长效
 | 
				
			||||||
func (e SmsTextVerified) CancelRental(id string, typ int) int {
 | 
					func (e SmsTextVerified) CancelRental(id string, typ int, apiInfo *dto.SmsPlatformKeyQueueDto) int {
 | 
				
			||||||
	client, code := e.GetTextVerifiedAuthClient()
 | 
						client, code := e.GetTextVerifiedAuthClient(apiInfo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if code != statuscode.Success {
 | 
						if code != statuscode.Success {
 | 
				
			||||||
		return code
 | 
							return code
 | 
				
			||||||
@ -670,8 +793,8 @@ func (e SmsTextVerified) CancelRental(id string, typ int) int {
 | 
				
			|||||||
	return statuscode.Success
 | 
						return statuscode.Success
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (e *SmsTextVerified) GetTextVerifiedAuthClient() (*httphelper.HTTPClient, int) {
 | 
					func (e *SmsTextVerified) GetTextVerifiedAuthClient(apiInfo *dto.SmsPlatformKeyQueueDto) (*httphelper.HTTPClient, int) {
 | 
				
			||||||
	header, err := e.GetAuthHeader()
 | 
						header, err := e.GetAuthHeader(apiInfo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		e.Log.Errorf("取消验证码获取token失败 error: %v", err)
 | 
							e.Log.Errorf("取消验证码获取token失败 error: %v", err)
 | 
				
			||||||
@ -794,10 +917,10 @@ func (e *SmsTextVerified) TextVerifiedWebHook(req *dto.TextVerifiedWebHookReq) e
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// 续期
 | 
					// 续期
 | 
				
			||||||
// typ 0-短效 1-长效
 | 
					// typ 0-短效 1-长效
 | 
				
			||||||
func (e *SmsTextVerified) Renew(activationId string, status bool) int {
 | 
					func (e *SmsTextVerified) Renew(activationId string, status bool, apiInfo *dto.SmsPlatformKeyQueueDto) int {
 | 
				
			||||||
	url := fmt.Sprintf(updateRentalRenewStatus, activationId)
 | 
						url := fmt.Sprintf(updateRentalRenewStatus, activationId)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	client, code := e.GetTextVerifiedAuthClient()
 | 
						client, code := e.GetTextVerifiedAuthClient(apiInfo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if code != statuscode.Success {
 | 
						if code != statuscode.Success {
 | 
				
			||||||
		e.Log.Errorf("获取长效续期失败 %d", code)
 | 
							e.Log.Errorf("获取长效续期失败 %d", code)
 | 
				
			||||||
@ -826,9 +949,201 @@ func (e *SmsTextVerified) Renew(activationId string, status bool) int {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 手动续期
 | 
				
			||||||
 | 
					// 续费id
 | 
				
			||||||
 | 
					func (e *SmsTextVerified) ManualRenewal(bilingCycleId string, apiInfo *dto.SmsPlatformKeyQueueDto) int {
 | 
				
			||||||
 | 
						url := fmt.Sprintf(manualRenewal, bilingCycleId)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client, code := e.GetTextVerifiedAuthClient(apiInfo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if code != statuscode.Success {
 | 
				
			||||||
 | 
							e.Log.Errorf("获取长效续期失败 %d", code)
 | 
				
			||||||
 | 
							return statuscode.ServerError
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						headers := map[string]string{
 | 
				
			||||||
 | 
							"Content-Type": "application/json",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						mapData := dto.TextVerifiedManualRenewalResp{}
 | 
				
			||||||
 | 
						statuCode, err := client.Post(url, nil, headers, &mapData)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("修改长效续期状态失败 %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if statuCode >= 200 && statuCode < 300 {
 | 
				
			||||||
 | 
							return statuscode.Success
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							e.Log.Errorf("修改长效续期状态失败 %d", statuCode)
 | 
				
			||||||
 | 
							// 没有需要续费的号码 重新设置可自动续费之后再试一次
 | 
				
			||||||
 | 
							if statuCode == 400 && strings.Contains(err.Error(), "Nothing to renew") {
 | 
				
			||||||
 | 
								return statuscode.NothingToRenew
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return statuscode.ServerError
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 重新初始化短信记录
 | 
				
			||||||
 | 
					func (e *SmsTextVerified) InitSmsLogs() error {
 | 
				
			||||||
 | 
						// 获取平台密钥信息
 | 
				
			||||||
 | 
						smsPlatformKeyRedis := NewSmsPlatformKeyRedis(e.Orm, e.Log)
 | 
				
			||||||
 | 
						apiInfo, err := smsPlatformKeyRedis.GetApiInfo("textverified", "")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("获取平台密钥失败: %v", err)
 | 
				
			||||||
 | 
							return fmt.Errorf("获取平台密钥失败: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						phoneLogs, err := e.GetUserByNumber("textverified")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("获取系统内号码记录失败 %v", err)
 | 
				
			||||||
 | 
							return fmt.Errorf("获取系统内号码记录失败 %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(phoneLogs) > 0 {
 | 
				
			||||||
 | 
							for _, v := range phoneLogs {
 | 
				
			||||||
 | 
								e.GetCode(v.ActivationId, v.Type, v.UserId, v.Service, v.ServiceCode, &apiInfo)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 根据电话号码获取系统内记录
 | 
				
			||||||
 | 
					func (e *SmsTextVerified) GetUserByNumber(platformCode string) ([]models.SmsPhone, error) {
 | 
				
			||||||
 | 
						var result []models.SmsPhone
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := e.Orm.Model(&models.SmsPhone{}).Where("platform_code = ?", platformCode).Find(&result).Error; err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return result, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 获取平台下 所有账号的获取记录
 | 
				
			||||||
 | 
					// map[string][]dto.VerificationDTO key:account
 | 
				
			||||||
 | 
					func (e *SmsTextVerified) GetAllPlatformNumbers() (map[string][]dto.VerificationDTO, error) {
 | 
				
			||||||
 | 
						// 获取平台密钥信息
 | 
				
			||||||
 | 
						smsPlatformKeyRedis := NewSmsPlatformKeyRedis(e.Orm, e.Log)
 | 
				
			||||||
 | 
						platformKeys, err := smsPlatformKeyRedis.GetPlatformKeys(global.SmsPlatformTextVerified)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("获取平台密钥失败: %v", err)
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("获取平台密钥失败: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						allNumbers := make(map[string][]dto.VerificationDTO)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, v := range platformKeys {
 | 
				
			||||||
 | 
							numbersw, err1 := e.GetAllNumbers(&v)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if err1 != nil {
 | 
				
			||||||
 | 
								e.Log.Errorf("获取平台密钥失败: %v", err1)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							allNumbers[v.Account] = numbersw
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return allNumbers, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *SmsTextVerified) GetAllNumbers(apiInfo *dto.SmsPlatformKeyQueueDto) ([]dto.VerificationDTO, error) {
 | 
				
			||||||
 | 
						phones, err := e.GetNumbers(apiInfo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("获取长效号码列表失败 %v", err)
 | 
				
			||||||
 | 
							return phones, fmt.Errorf("获取长效号码列表失败 %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return phones, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 获取号码
 | 
				
			||||||
 | 
					func (e *SmsTextVerified) GetNumbers(apiInfo *dto.SmsPlatformKeyQueueDto) ([]dto.VerificationDTO, error) {
 | 
				
			||||||
 | 
						client, code := e.GetTextVerifiedAuthClient(apiInfo)
 | 
				
			||||||
 | 
						result := make([]dto.VerificationDTO, 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if code != statuscode.Success {
 | 
				
			||||||
 | 
							e.Log.Errorf("获取长效号码列表失败 %d", code)
 | 
				
			||||||
 | 
							return result, fmt.Errorf("获取长效号码列表失败 %d", code)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp := dto.TextVerifiedGetNumbersResp{}
 | 
				
			||||||
 | 
						statusCode, err := client.Get(getNumbers, map[string]string{}, &resp)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("获取长效号码列表失败 %v", err)
 | 
				
			||||||
 | 
							return result, fmt.Errorf("获取长效号码列表失败 %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if statusCode == http.StatusOK {
 | 
				
			||||||
 | 
							// 添加第一页数据
 | 
				
			||||||
 | 
							for _, item := range resp.Data {
 | 
				
			||||||
 | 
								item.Number = "1" + item.Number
 | 
				
			||||||
 | 
								item.ApiKey = apiInfo.ApiKey
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								result = append(result, item)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 处理分页数据
 | 
				
			||||||
 | 
							if resp.HasNext {
 | 
				
			||||||
 | 
								paginatedData, err := e.fetchPaginatedData(resp.Links.Next, apiInfo)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return result, err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								result = append(result, paginatedData...)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return result, nil
 | 
				
			||||||
 | 
						} else if statusCode == http.StatusUnauthorized {
 | 
				
			||||||
 | 
							return e.GetNumbers(apiInfo)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							e.Log.Errorf("获取长效号码列表失败 %d", statusCode)
 | 
				
			||||||
 | 
							return result, fmt.Errorf("获取长效号码列表失败 %d", statusCode)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 获取授权header头
 | 
					// 获取授权header头
 | 
				
			||||||
func (e *SmsTextVerified) GetAuthHeader() (map[string]string, error) {
 | 
					// fetchPaginatedData 获取分页数据的通用方法
 | 
				
			||||||
	token, err := e.GetToken()
 | 
					// 处理TextVerified API的分页响应,自动获取所有页面的数据
 | 
				
			||||||
 | 
					func (e *SmsTextVerified) fetchPaginatedData(nextLink *dto.TextVerifiedResp, apiInfo *dto.SmsPlatformKeyQueueDto) ([]dto.VerificationDTO, error) {
 | 
				
			||||||
 | 
						var result []dto.VerificationDTO
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for nextLink != nil {
 | 
				
			||||||
 | 
							nextData, err := e.doRequest(nextLink, apiInfo)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								e.Log.Errorf("获取分页数据失败: %v", err)
 | 
				
			||||||
 | 
								return result, fmt.Errorf("获取分页数据失败: %v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							nextResp := dto.TextVerifiedGetNumbersResp{}
 | 
				
			||||||
 | 
							err = sonic.Unmarshal(nextData, &nextResp)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								e.Log.Errorf("解析分页数据失败: %v", err)
 | 
				
			||||||
 | 
								return result, fmt.Errorf("解析分页数据失败: %v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for _, item := range nextResp.Data {
 | 
				
			||||||
 | 
								item.Number = "1" + item.Number
 | 
				
			||||||
 | 
								item.ApiKey = apiInfo.ApiKey
 | 
				
			||||||
 | 
								result = append(result, item)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 检查是否还有下一页
 | 
				
			||||||
 | 
							if nextResp.HasNext {
 | 
				
			||||||
 | 
								nextLink = nextResp.Links.Next
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								nextLink = nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return result, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *SmsTextVerified) GetAuthHeader(apiInfo *dto.SmsPlatformKeyQueueDto) (map[string]string, error) {
 | 
				
			||||||
 | 
						token, err := e.GetToken(apiInfo)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -853,3 +1168,70 @@ func getIdByUrl(URLString string) string {
 | 
				
			|||||||
func isValidID(s string) bool {
 | 
					func isValidID(s string) bool {
 | 
				
			||||||
	return idPattern.MatchString(s)
 | 
						return idPattern.MatchString(s)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 获取长效租赁号码列表
 | 
				
			||||||
 | 
					// next 分页url
 | 
				
			||||||
 | 
					func (e *SmsTextVerified) GetRentalList(next string, apiInfo *dto.SmsPlatformKeyQueueDto) (dto.TextVerifiedGetRentalListResp, error) {
 | 
				
			||||||
 | 
						var result dto.TextVerifiedGetRentalListResp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client, code := e.GetTextVerifiedAuthClient(apiInfo)
 | 
				
			||||||
 | 
						if code != statuscode.Success {
 | 
				
			||||||
 | 
							e.Log.Errorf("获取长效号码列表失败 %d", code)
 | 
				
			||||||
 | 
							return result, fmt.Errorf("获取长效号码列表失败 %d", code)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if next == "" {
 | 
				
			||||||
 | 
							next = rentalList
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							client.BaseURL = ""
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp := dto.TextVerifiedGetRentalListResp{}
 | 
				
			||||||
 | 
						statusCode, err := client.Get(next, map[string]string{}, &resp)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("获取长效号码列表失败 %v", err)
 | 
				
			||||||
 | 
							return result, fmt.Errorf("获取长效号码列表失败 %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if statusCode == http.StatusOK {
 | 
				
			||||||
 | 
							result = resp
 | 
				
			||||||
 | 
							return result, nil
 | 
				
			||||||
 | 
						} else if statusCode == http.StatusUnauthorized {
 | 
				
			||||||
 | 
							return e.GetRentalList(next, apiInfo)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							e.Log.Errorf("获取长效号码列表失败 %d", statusCode)
 | 
				
			||||||
 | 
							return result, fmt.Errorf("获取长效号码列表失败 %d", statusCode)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 获取续费周期详情
 | 
				
			||||||
 | 
					func (e *SmsTextVerified) GetBillingCycle(billingCycleId string, apiInfo *dto.SmsPlatformKeyQueueDto) (dto.TextVerifiedGetBillingCycleResp, error) {
 | 
				
			||||||
 | 
						var result dto.TextVerifiedGetBillingCycleResp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client, code := e.GetTextVerifiedAuthClient(apiInfo)
 | 
				
			||||||
 | 
						if code != statuscode.Success {
 | 
				
			||||||
 | 
							e.Log.Errorf("获取计费周期详情失败 %d", code)
 | 
				
			||||||
 | 
							return result, fmt.Errorf("获取计费周期详情失败 %d", code)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						next := fmt.Sprintf(getBillingCycles, billingCycleId)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp := dto.TextVerifiedGetBillingCycleResp{}
 | 
				
			||||||
 | 
						statusCode, err := client.Get(next, map[string]string{}, &resp)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("获取计费周期详情失败 %v", err)
 | 
				
			||||||
 | 
							return result, fmt.Errorf("获取计费周期详情失败 %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if statusCode >= http.StatusOK && statusCode < http.StatusMultipleChoices {
 | 
				
			||||||
 | 
							result = resp
 | 
				
			||||||
 | 
							return result, nil
 | 
				
			||||||
 | 
						} else if statusCode == http.StatusUnauthorized {
 | 
				
			||||||
 | 
							return e.GetBillingCycle(billingCycleId, apiInfo)
 | 
				
			||||||
 | 
						} else if statusCode == http.StatusNotFound {
 | 
				
			||||||
 | 
							return result, LogNotFund
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							e.Log.Errorf("获取计费周期详情失败 %d", statusCode)
 | 
				
			||||||
 | 
							return result, fmt.Errorf("获取计费周期详情失败 %d", statusCode)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										224
									
								
								app/admin/service/sms_text_verified_enhanced.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										224
									
								
								app/admin/service/sms_text_verified_enhanced.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,224 @@
 | 
				
			|||||||
 | 
					package service
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"go-admin/app/admin/service/dto"
 | 
				
			||||||
 | 
						"go-admin/common/global"
 | 
				
			||||||
 | 
						"go-admin/common/statuscode"
 | 
				
			||||||
 | 
						"go-admin/config"
 | 
				
			||||||
 | 
						"go-admin/utils/httphelper"
 | 
				
			||||||
 | 
						"go-admin/utils/redishelper"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/go-admin-team/go-admin-core/sdk/service"
 | 
				
			||||||
 | 
						"github.com/go-redis/redis/v8"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SmsTextVerifiedEnhanced 增强版TextVerified服务,支持token自动刷新
 | 
				
			||||||
 | 
					type SmsTextVerifiedEnhanced struct {
 | 
				
			||||||
 | 
						service.Service
 | 
				
			||||||
 | 
						maxRetries int // 最大重试次数
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewSmsTextVerifiedEnhanced 创建增强版TextVerified服务实例
 | 
				
			||||||
 | 
					func NewSmsTextVerifiedEnhanced() *SmsTextVerifiedEnhanced {
 | 
				
			||||||
 | 
						return &SmsTextVerifiedEnhanced{
 | 
				
			||||||
 | 
							maxRetries: 2, // 默认最大重试2次
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AuthenticatedRequest 带自动token刷新的HTTP请求包装器
 | 
				
			||||||
 | 
					// 当遇到401未授权错误时,会自动刷新token并重试请求
 | 
				
			||||||
 | 
					func (e *SmsTextVerifiedEnhanced) AuthenticatedRequest(
 | 
				
			||||||
 | 
						apiInfo *dto.SmsPlatformKeyQueueDto,
 | 
				
			||||||
 | 
						requestFunc func(client *httphelper.HTTPClient) (int, error),
 | 
				
			||||||
 | 
					) (int, error) {
 | 
				
			||||||
 | 
						var lastErr error
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						// 尝试执行请求,包含重试逻辑
 | 
				
			||||||
 | 
						for attempt := 0; attempt <= e.maxRetries; attempt++ {
 | 
				
			||||||
 | 
							// 获取认证客户端
 | 
				
			||||||
 | 
							client, code := e.GetTextVerifiedAuthClient(apiInfo)
 | 
				
			||||||
 | 
							if code != statuscode.Success {
 | 
				
			||||||
 | 
								e.Log.Errorf("获取授权客户端失败,状态码: %d", code)
 | 
				
			||||||
 | 
								return code, fmt.Errorf("获取授权客户端失败,状态码: %d", code)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 执行请求
 | 
				
			||||||
 | 
							status, err := requestFunc(client)
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							// 如果请求成功,直接返回
 | 
				
			||||||
 | 
							if err == nil {
 | 
				
			||||||
 | 
								return status, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 检查是否为401未授权错误
 | 
				
			||||||
 | 
							if e.isUnauthorizedError(status, err) {
 | 
				
			||||||
 | 
								e.Log.Warnf("检测到token过期或未授权错误 (尝试 %d/%d): %v", attempt+1, e.maxRetries+1, err)
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								// 如果不是最后一次尝试,清除token缓存并重试
 | 
				
			||||||
 | 
								if attempt < e.maxRetries {
 | 
				
			||||||
 | 
									if clearErr := e.clearTokenCache(apiInfo); clearErr != nil {
 | 
				
			||||||
 | 
										e.Log.Errorf("清除token缓存失败: %v", clearErr)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									e.Log.Infof("正在重试请求... (尝试 %d/%d)", attempt+2, e.maxRetries+1)
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 如果不是401错误或已达到最大重试次数,记录错误并退出循环
 | 
				
			||||||
 | 
							lastErr = err
 | 
				
			||||||
 | 
							break
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 返回最后一次的错误
 | 
				
			||||||
 | 
						return statuscode.ServerError, fmt.Errorf("请求失败,已重试%d次: %v", e.maxRetries, lastErr)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// isUnauthorizedError 检查错误是否为401未授权错误
 | 
				
			||||||
 | 
					func (e *SmsTextVerifiedEnhanced) isUnauthorizedError(status int, err error) bool {
 | 
				
			||||||
 | 
						if status == http.StatusUnauthorized {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							errorMsg := strings.ToLower(err.Error())
 | 
				
			||||||
 | 
							// 检查常见的未授权错误消息
 | 
				
			||||||
 | 
							return strings.Contains(errorMsg, "unauthorized") ||
 | 
				
			||||||
 | 
								strings.Contains(errorMsg, "401") ||
 | 
				
			||||||
 | 
								strings.Contains(errorMsg, "invalid token") ||
 | 
				
			||||||
 | 
								strings.Contains(errorMsg, "token expired") ||
 | 
				
			||||||
 | 
								strings.Contains(errorMsg, "authentication failed")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// clearTokenCache 清除Redis中的token缓存
 | 
				
			||||||
 | 
					func (e *SmsTextVerifiedEnhanced) clearTokenCache(apiInfo *dto.SmsPlatformKeyQueueDto) error {
 | 
				
			||||||
 | 
						key := fmt.Sprintf(global.TextVerifiedToken, apiInfo.ApiKey)
 | 
				
			||||||
 | 
						err := redishelper.DefaultRedis.DeleteString(key)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("删除token缓存失败: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						e.Log.Infof("已清除token缓存: %s", key)
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetTextVerifiedAuthClient 获取认证的HTTP客户端
 | 
				
			||||||
 | 
					// 这个方法与原版相同,但可以在这里添加额外的逻辑
 | 
				
			||||||
 | 
					func (e *SmsTextVerifiedEnhanced) GetTextVerifiedAuthClient(apiInfo *dto.SmsPlatformKeyQueueDto) (*httphelper.HTTPClient, int) {
 | 
				
			||||||
 | 
						header, err := e.GetAuthHeader(apiInfo)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("获取授权头失败: %v", err)
 | 
				
			||||||
 | 
							return nil, statuscode.ServerError
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.SmsTextVerified.Url, header)
 | 
				
			||||||
 | 
						return client, statuscode.Success
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetAuthHeader 获取认证头
 | 
				
			||||||
 | 
					func (e *SmsTextVerifiedEnhanced) GetAuthHeader(apiInfo *dto.SmsPlatformKeyQueueDto) (map[string]string, error) {
 | 
				
			||||||
 | 
						token, err := e.GetToken(apiInfo)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						headers := map[string]string{
 | 
				
			||||||
 | 
							"Authorization": "Bearer " + token,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return headers, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetToken 获取token(与原版相同的逻辑)
 | 
				
			||||||
 | 
					func (e *SmsTextVerifiedEnhanced) GetToken(apiInfo *dto.SmsPlatformKeyQueueDto) (string, error) {
 | 
				
			||||||
 | 
						key := fmt.Sprintf(global.TextVerifiedToken, apiInfo.ApiKey)
 | 
				
			||||||
 | 
						token, err := redishelper.DefaultRedis.GetString(key)
 | 
				
			||||||
 | 
						if err != nil && errors.Is(err, redis.Nil) {
 | 
				
			||||||
 | 
							// token不存在,重新登录获取
 | 
				
			||||||
 | 
							return e.Login(apiInfo)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if token == "" {
 | 
				
			||||||
 | 
							return e.Login(apiInfo)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return token, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Login 登录获取token(与原版相同的逻辑)
 | 
				
			||||||
 | 
					func (e *SmsTextVerifiedEnhanced) Login(apiInfo *dto.SmsPlatformKeyQueueDto) (string, error) {
 | 
				
			||||||
 | 
						resp := dto.TextVerifiedLoginResp{}
 | 
				
			||||||
 | 
						var token string
 | 
				
			||||||
 | 
						client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.SmsTextVerified.Url, nil)
 | 
				
			||||||
 | 
						headers := map[string]string{
 | 
				
			||||||
 | 
							"X-API-USERNAME": apiInfo.ApiSecret,
 | 
				
			||||||
 | 
							"X-API-KEY":      apiInfo.ApiKey,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, err1 := client.Post("/api/pub/v2/auth", nil, headers, &resp)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err1 != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("TextVerified登录失败 error: %v", err1)
 | 
				
			||||||
 | 
							return "", err1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if resp.Token == "" {
 | 
				
			||||||
 | 
							e.Log.Errorf("TextVerified登录失败,返回的Token为空")
 | 
				
			||||||
 | 
							return "", errors.New("TextVerified登录失败,返回的Token为空")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						token = resp.Token
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if resp.ExpiresIn >= 10 {
 | 
				
			||||||
 | 
							resp.ExpiresIn = resp.ExpiresIn - 10 // 提前10秒过期
 | 
				
			||||||
 | 
							key := fmt.Sprintf(global.TextVerifiedToken, apiInfo.ApiKey)
 | 
				
			||||||
 | 
							if err := redishelper.DefaultRedis.SetStringExpire(key, token, time.Duration(resp.ExpiresIn)*time.Second); err != nil {
 | 
				
			||||||
 | 
								e.Log.Errorf("TextVerified登录失败,缓存Token失败 error: %v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return token, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 使用示例:增强版的GetCode方法
 | 
				
			||||||
 | 
					func (e *SmsTextVerifiedEnhanced) GetCodeEnhanced(messageId string, typ int, userId int, service, serviceCode string, apiInfo *dto.SmsPlatformKeyQueueDto) (string, int) {
 | 
				
			||||||
 | 
						reservationType := ""
 | 
				
			||||||
 | 
						if typ == 0 {
 | 
				
			||||||
 | 
							reservationType = "verification"
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							reservationType = "renewable"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						url := fmt.Sprintf("/api/pub/v2/sms?reservationId=%s&reservationType=%s", messageId, reservationType)
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						var parsedCode string
 | 
				
			||||||
 | 
						var finalStatus int
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						// 使用增强的请求方法
 | 
				
			||||||
 | 
						status, err := e.AuthenticatedRequest(apiInfo, func(client *httphelper.HTTPClient) (int, error) {
 | 
				
			||||||
 | 
							resp := dto.TextVerifiedSmsResp{}
 | 
				
			||||||
 | 
							status, err := client.Get(url, nil, &resp)
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return status, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							// 处理响应数据...
 | 
				
			||||||
 | 
							if len(resp.Data) > 0 {
 | 
				
			||||||
 | 
								// 这里可以添加原有的验证码处理逻辑
 | 
				
			||||||
 | 
								parsedCode = resp.Data[0].ParsedCode
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							return http.StatusOK, nil
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e.Log.Errorf("获取验证码失败: %v", err)
 | 
				
			||||||
 | 
							finalStatus = statuscode.ServerError
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							finalStatus = status
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						return parsedCode, finalStatus
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,6 +1,7 @@
 | 
				
			|||||||
package service
 | 
					package service
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"go-admin/common/global"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/go-admin-team/go-admin-core/logger"
 | 
						"github.com/go-admin-team/go-admin-core/logger"
 | 
				
			||||||
@ -13,7 +14,15 @@ func TestSmsTextVerifiedLogin(t *testing.T) {
 | 
				
			|||||||
	s.Orm = db
 | 
						s.Orm = db
 | 
				
			||||||
	s.Log = logger.NewHelper(logger.DefaultLogger)
 | 
						s.Log = logger.NewHelper(logger.DefaultLogger)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	token, err := s.Login()
 | 
						smsPlatformKeyRedis := NewSmsPlatformKeyRedis(s.Orm, s.Log)
 | 
				
			||||||
 | 
						apiInfo, err := smsPlatformKeyRedis.GetRoundRobinKey(global.SmsPlatformTextVerified)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							s.Log.Errorf("获取API信息失败: %v", err)
 | 
				
			||||||
 | 
							t.Error("获取API信息失败", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						token, err := s.Login(apiInfo)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Errorf("Login failed: %v", err)
 | 
							t.Errorf("Login failed: %v", err)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
@ -27,9 +36,16 @@ func TestSmsTextVerifiedGetServices(t *testing.T) {
 | 
				
			|||||||
	s := SmsTextVerified{}
 | 
						s := SmsTextVerified{}
 | 
				
			||||||
	s.Orm = db
 | 
						s.Orm = db
 | 
				
			||||||
	s.Log = logger.NewHelper(logger.DefaultLogger)
 | 
						s.Log = logger.NewHelper(logger.DefaultLogger)
 | 
				
			||||||
 | 
						smsPlatformKeyRedis := NewSmsPlatformKeyRedis(s.Orm, s.Log)
 | 
				
			||||||
 | 
						apiInfo, err := smsPlatformKeyRedis.GetRoundRobinKey(global.SmsPlatformTextVerified)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							s.Log.Errorf("获取API信息失败: %v", err)
 | 
				
			||||||
 | 
							t.Error("获取API信息失败", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Now, test GetServices with the valid token
 | 
						// Now, test GetServices with the valid token
 | 
				
			||||||
	servicesResp, err := s.GetServices()
 | 
						servicesResp, err := s.GetServices(apiInfo)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Errorf("GetServices failed: %v", err)
 | 
							t.Errorf("GetServices failed: %v", err)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
@ -51,3 +67,18 @@ func TestSyncTextVerifiedServices(t *testing.T) {
 | 
				
			|||||||
		t.Log("SyncTextVerifiedServices succeeded")
 | 
							t.Log("SyncTextVerifiedServices succeeded")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestInitSmsLogs(t *testing.T) {
 | 
				
			||||||
 | 
						db := initSetting()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						s := SmsTextVerified{}
 | 
				
			||||||
 | 
						s.Orm = db
 | 
				
			||||||
 | 
						s.Log = logger.NewHelper(logger.DefaultLogger)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := s.InitSmsLogs()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Errorf("InitSmsLogs failed: %v", err)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							t.Log("InitSmsLogs succeeded")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -37,11 +37,11 @@ func (j RenewalJob) Exec(args interface{}) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// 定时短信续期任务
 | 
					// 定时短信续期任务
 | 
				
			||||||
func (j SmsRenewalJob) Exec(args interface{}) error {
 | 
					func (j SmsRenewalJob) Exec(args interface{}) error {
 | 
				
			||||||
	smsService := service.SmsServices{}
 | 
						smsPhoneService := service.SmsRenewalLog{}
 | 
				
			||||||
	smsService.Orm = GetDb()
 | 
						smsPhoneService.Orm = GetDb()
 | 
				
			||||||
	smsService.Log = logger.NewHelper(logger.DefaultLogger)
 | 
						smsPhoneService.Log = logger.NewHelper(logger.DefaultLogger)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return smsService.AutoRenewal()
 | 
						return smsPhoneService.AutoRenewal()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 过期任务
 | 
					// 过期任务
 | 
				
			||||||
 | 
				
			|||||||
@ -23,7 +23,7 @@ func TestTrxJob(t *testing.T) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func initSetting() {
 | 
					func initSetting() {
 | 
				
			||||||
	dsn := "root:123456@tcp(127.0.0.1:3306)/proxy_server?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms"
 | 
						dsn := "root:123456@tcp(127.0.0.1:3306)/proxy_server_prod?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms"
 | 
				
			||||||
	db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})
 | 
						db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})
 | 
				
			||||||
	sdk.Runtime.SetDb("default", db)
 | 
						sdk.Runtime.SetDb("default", db)
 | 
				
			||||||
	config.ExtConfig.TrxGridUrl = "https://api.trongrid.io"
 | 
						config.ExtConfig.TrxGridUrl = "https://api.trongrid.io"
 | 
				
			||||||
 | 
				
			|||||||
@ -223,4 +223,12 @@ func initBusinesses() {
 | 
				
			|||||||
	memberApiService.Log = cliProxyService.Log
 | 
						memberApiService.Log = cliProxyService.Log
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	memberApiService.InitApis()
 | 
						memberApiService.InitApis()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 创建SMS平台密钥服务实例并初始化Redis缓存
 | 
				
			||||||
 | 
						smsPlatformKey := service.SmsPlatformKey{}
 | 
				
			||||||
 | 
						smsPlatformKey.Orm = cliProxyService.Orm
 | 
				
			||||||
 | 
						smsPlatformKey.Log = cliProxyService.Log
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 初始化Redis缓存队列
 | 
				
			||||||
 | 
						smsPlatformKey.InitQueque()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										
											BIN
										
									
								
								cmd/proxy-server
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								cmd/proxy-server
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							@ -1,6 +1,6 @@
 | 
				
			|||||||
package global
 | 
					package global
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	// TextVerified Token
 | 
						// TextVerified Token {apiKey}
 | 
				
			||||||
	TextVerifiedToken = "TextVerifiedToken"
 | 
						TextVerifiedToken = "TextVerifiedToken:%s"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										6
									
								
								common/rediskey/sms-platform-key.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								common/rediskey/sms-platform-key.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					package rediskey
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						//平台key
 | 
				
			||||||
 | 
						SmsPlatformKey = "sms_platform_key:%s"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
@ -41,6 +41,8 @@ var StatusCodeZh = map[int]string{
 | 
				
			|||||||
	SmsInvalidType:              "短信验证码_无效类型",
 | 
						SmsInvalidType:              "短信验证码_无效类型",
 | 
				
			||||||
	SmsOutOfStockOrUnavailable:  "短信验证码_缺货或服务不可用",
 | 
						SmsOutOfStockOrUnavailable:  "短信验证码_缺货或服务不可用",
 | 
				
			||||||
	SmsRentalRefundNotPermitted: "短信验证码_租赁退款不允许",
 | 
						SmsRentalRefundNotPermitted: "短信验证码_租赁退款不允许",
 | 
				
			||||||
 | 
						SmsRentalCantRenew:          "短信验证码_无法续期",
 | 
				
			||||||
 | 
						SmsRenewalLogExisted:        "短信验证码_重复续期",
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var StatusCodeEn = map[int]string{
 | 
					var StatusCodeEn = map[int]string{
 | 
				
			||||||
@ -74,6 +76,8 @@ var StatusCodeEn = map[int]string{
 | 
				
			|||||||
	SmsInvalidType:              "sms type invalid",
 | 
						SmsInvalidType:              "sms type invalid",
 | 
				
			||||||
	SmsOutOfStockOrUnavailable:  "sms out of stock or unavailable",
 | 
						SmsOutOfStockOrUnavailable:  "sms out of stock or unavailable",
 | 
				
			||||||
	SmsRentalRefundNotPermitted: "sms rental refund not permitted",
 | 
						SmsRentalRefundNotPermitted: "sms rental refund not permitted",
 | 
				
			||||||
 | 
						SmsRentalCantRenew:          "sms rental can not renewal",
 | 
				
			||||||
 | 
						SmsRenewalLogExisted:        "sms renewal log existed",
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetMsg 获取状态码对应的消息
 | 
					// GetMsg 获取状态码对应的消息
 | 
				
			||||||
@ -107,7 +111,8 @@ const (
 | 
				
			|||||||
	AccountOrPasswordError = 10003
 | 
						AccountOrPasswordError = 10003
 | 
				
			||||||
	//密码不一致
 | 
						//密码不一致
 | 
				
			||||||
	PassWordNotMatch = 10004
 | 
						PassWordNotMatch = 10004
 | 
				
			||||||
 | 
						// 没有需要续费的号码 重新设置可自动续费之后再试一次
 | 
				
			||||||
 | 
						NothingToRenew = 11005
 | 
				
			||||||
	//
 | 
						//
 | 
				
			||||||
	ServerError = 500
 | 
						ServerError = 500
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -161,4 +166,8 @@ const (
 | 
				
			|||||||
	SmsOutOfStockOrUnavailable = 20024
 | 
						SmsOutOfStockOrUnavailable = 20024
 | 
				
			||||||
	// 短信-租赁退款不允许
 | 
						// 短信-租赁退款不允许
 | 
				
			||||||
	SmsRentalRefundNotPermitted = 20025
 | 
						SmsRentalRefundNotPermitted = 20025
 | 
				
			||||||
 | 
						// 短信-无法续期
 | 
				
			||||||
 | 
						SmsRentalCantRenew = 20026
 | 
				
			||||||
 | 
						// 短信-重复续期
 | 
				
			||||||
 | 
						SmsRenewalLogExisted = 20028
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										221
									
								
								utils/utility/crypto_helper.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										221
									
								
								utils/utility/crypto_helper.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,221 @@
 | 
				
			|||||||
 | 
					package utility
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"crypto/aes"
 | 
				
			||||||
 | 
						"crypto/cipher"
 | 
				
			||||||
 | 
						"crypto/rand"
 | 
				
			||||||
 | 
						"encoding/base64"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 加密key
 | 
				
			||||||
 | 
					var CryptoKey = "ProxyServer@#(123321)!Keycrypto"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CryptoHelper 加密帮助类
 | 
				
			||||||
 | 
					type CryptoHelper struct {
 | 
				
			||||||
 | 
						key []byte
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewCryptoHelper 创建新的加密帮助实例
 | 
				
			||||||
 | 
					// key: 32字节的加密密钥,如果长度不足会自动填充,超出会截断
 | 
				
			||||||
 | 
					func NewCryptoHelper(key string) *CryptoHelper {
 | 
				
			||||||
 | 
						// 确保密钥长度为32字节(AES-256)
 | 
				
			||||||
 | 
						keyBytes := make([]byte, 32)
 | 
				
			||||||
 | 
						copy(keyBytes, []byte(key))
 | 
				
			||||||
 | 
						return &CryptoHelper{
 | 
				
			||||||
 | 
							key: keyBytes,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Encrypt 加密字符串
 | 
				
			||||||
 | 
					// plaintext: 要加密的明文
 | 
				
			||||||
 | 
					// 返回: base64编码的密文和错误信息
 | 
				
			||||||
 | 
					func (c *CryptoHelper) Encrypt(plaintext string) (string, error) {
 | 
				
			||||||
 | 
						if plaintext == "" {
 | 
				
			||||||
 | 
							return "", errors.New("plaintext cannot be empty")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 创建AES cipher
 | 
				
			||||||
 | 
						block, err := aes.NewCipher(c.key)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", fmt.Errorf("failed to create cipher: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 使用GCM模式
 | 
				
			||||||
 | 
						gcm, err := cipher.NewGCM(block)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", fmt.Errorf("failed to create GCM: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 生成随机nonce
 | 
				
			||||||
 | 
						nonce := make([]byte, gcm.NonceSize())
 | 
				
			||||||
 | 
						if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
 | 
				
			||||||
 | 
							return "", fmt.Errorf("failed to generate nonce: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 加密数据
 | 
				
			||||||
 | 
						ciphertext := gcm.Seal(nonce, nonce, []byte(plaintext), nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 返回base64编码的结果
 | 
				
			||||||
 | 
						return base64.StdEncoding.EncodeToString(ciphertext), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Decrypt 解密字符串
 | 
				
			||||||
 | 
					// ciphertext: base64编码的密文
 | 
				
			||||||
 | 
					// 返回: 解密后的明文和错误信息
 | 
				
			||||||
 | 
					func (c *CryptoHelper) Decrypt(ciphertext string) (string, error) {
 | 
				
			||||||
 | 
						if ciphertext == "" {
 | 
				
			||||||
 | 
							return "", errors.New("ciphertext cannot be empty")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// base64解码
 | 
				
			||||||
 | 
						data, err := base64.StdEncoding.DecodeString(ciphertext)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", fmt.Errorf("failed to decode base64: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 创建AES cipher
 | 
				
			||||||
 | 
						block, err := aes.NewCipher(c.key)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", fmt.Errorf("failed to create cipher: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 使用GCM模式
 | 
				
			||||||
 | 
						gcm, err := cipher.NewGCM(block)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", fmt.Errorf("failed to create GCM: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 检查数据长度
 | 
				
			||||||
 | 
						nonceSize := gcm.NonceSize()
 | 
				
			||||||
 | 
						if len(data) < nonceSize {
 | 
				
			||||||
 | 
							return "", errors.New("ciphertext too short")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 提取nonce和密文
 | 
				
			||||||
 | 
						nonce, cipherData := data[:nonceSize], data[nonceSize:]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 解密数据
 | 
				
			||||||
 | 
						plaintext, err := gcm.Open(nil, nonce, cipherData, nil)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", fmt.Errorf("failed to decrypt: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return string(plaintext), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// EncryptBytes 加密字节数组
 | 
				
			||||||
 | 
					// data: 要加密的字节数组
 | 
				
			||||||
 | 
					// 返回: 加密后的字节数组和错误信息
 | 
				
			||||||
 | 
					func (c *CryptoHelper) EncryptBytes(data []byte) ([]byte, error) {
 | 
				
			||||||
 | 
						if len(data) == 0 {
 | 
				
			||||||
 | 
							return nil, errors.New("data cannot be empty")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 创建AES cipher
 | 
				
			||||||
 | 
						block, err := aes.NewCipher(c.key)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("failed to create cipher: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 使用GCM模式
 | 
				
			||||||
 | 
						gcm, err := cipher.NewGCM(block)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("failed to create GCM: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 生成随机nonce
 | 
				
			||||||
 | 
						nonce := make([]byte, gcm.NonceSize())
 | 
				
			||||||
 | 
						if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("failed to generate nonce: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 加密数据
 | 
				
			||||||
 | 
						ciphertext := gcm.Seal(nonce, nonce, data, nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ciphertext, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DecryptBytes 解密字节数组
 | 
				
			||||||
 | 
					// ciphertext: 加密后的字节数组
 | 
				
			||||||
 | 
					// 返回: 解密后的字节数组和错误信息
 | 
				
			||||||
 | 
					func (c *CryptoHelper) DecryptBytes(ciphertext []byte) ([]byte, error) {
 | 
				
			||||||
 | 
						if len(ciphertext) == 0 {
 | 
				
			||||||
 | 
							return nil, errors.New("ciphertext cannot be empty")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 创建AES cipher
 | 
				
			||||||
 | 
						block, err := aes.NewCipher(c.key)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("failed to create cipher: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 使用GCM模式
 | 
				
			||||||
 | 
						gcm, err := cipher.NewGCM(block)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("failed to create GCM: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 检查数据长度
 | 
				
			||||||
 | 
						nonceSize := gcm.NonceSize()
 | 
				
			||||||
 | 
						if len(ciphertext) < nonceSize {
 | 
				
			||||||
 | 
							return nil, errors.New("ciphertext too short")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 提取nonce和密文
 | 
				
			||||||
 | 
						nonce, cipherData := ciphertext[:nonceSize], ciphertext[nonceSize:]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 解密数据
 | 
				
			||||||
 | 
						plaintext, err := gcm.Open(nil, nonce, cipherData, nil)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("failed to decrypt: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return plaintext, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GenerateKey 生成随机密钥
 | 
				
			||||||
 | 
					// 返回: 32字节的随机密钥字符串
 | 
				
			||||||
 | 
					func GenerateKey() (string, error) {
 | 
				
			||||||
 | 
						key := make([]byte, 32)
 | 
				
			||||||
 | 
						if _, err := io.ReadFull(rand.Reader, key); err != nil {
 | 
				
			||||||
 | 
							return "", fmt.Errorf("failed to generate key: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return base64.StdEncoding.EncodeToString(key), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// QuickEncrypt 快速加密函数(使用默认密钥)
 | 
				
			||||||
 | 
					// plaintext: 要加密的明文
 | 
				
			||||||
 | 
					// key: 加密密钥
 | 
				
			||||||
 | 
					// 返回: base64编码的密文和错误信息
 | 
				
			||||||
 | 
					func QuickEncrypt(plaintext, key string) (string, error) {
 | 
				
			||||||
 | 
						crypto := NewCryptoHelper(key)
 | 
				
			||||||
 | 
						return crypto.Encrypt(plaintext)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// QuickDecrypt2 快速解密函数(使用默认密钥)
 | 
				
			||||||
 | 
					// ciphertext: base64编码的密文
 | 
				
			||||||
 | 
					// 返回: 解密后的明文和错误信息
 | 
				
			||||||
 | 
					func QuickDecrypt2(ciphertext string) (string, error) {
 | 
				
			||||||
 | 
						crypto := NewCryptoHelper(CryptoKey)
 | 
				
			||||||
 | 
						return crypto.Decrypt(ciphertext)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// QuickEncrypt2 快速加密函数(使用默认密钥)
 | 
				
			||||||
 | 
					// plaintext: 要加密的明文
 | 
				
			||||||
 | 
					// 返回: base64编码的密文和错误信息
 | 
				
			||||||
 | 
					func QuickEncrypt2(plaintext string) (string, error) {
 | 
				
			||||||
 | 
						crypto := NewCryptoHelper(CryptoKey)
 | 
				
			||||||
 | 
						return crypto.Encrypt(plaintext)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// QuickDecrypt 快速解密函数(使用默认密钥)
 | 
				
			||||||
 | 
					// ciphertext: base64编码的密文
 | 
				
			||||||
 | 
					// key: 解密密钥
 | 
				
			||||||
 | 
					// 返回: 解密后的明文和错误信息
 | 
				
			||||||
 | 
					func QuickDecrypt(ciphertext, key string) (string, error) {
 | 
				
			||||||
 | 
						crypto := NewCryptoHelper(key)
 | 
				
			||||||
 | 
						return crypto.Decrypt(ciphertext)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										371
									
								
								utils/utility/generic_queue.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										371
									
								
								utils/utility/generic_queue.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,371 @@
 | 
				
			|||||||
 | 
					package utility
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GenericQueue 通用循环队列
 | 
				
			||||||
 | 
					// 支持存储任意类型的数据,实现负载均衡和容错
 | 
				
			||||||
 | 
					type GenericQueue[T any] struct {
 | 
				
			||||||
 | 
						data     []T               // 存储数据的数组
 | 
				
			||||||
 | 
						current  int               // 当前指针位置
 | 
				
			||||||
 | 
						size     int               // 队列中的元素数量
 | 
				
			||||||
 | 
						capacity int               // 队列容量
 | 
				
			||||||
 | 
						mutex    sync.RWMutex      // 读写锁,保证线程安全
 | 
				
			||||||
 | 
						lastUsed map[int]time.Time // 记录每个位置的最后使用时间
 | 
				
			||||||
 | 
						cooldown time.Duration     // 冷却时间,避免频繁使用同一个元素
 | 
				
			||||||
 | 
						comparer func(T, T) bool   // 比较函数,用于检查重复元素
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						// QuequeMap 全局队列映射表,用于管理多个命名队列
 | 
				
			||||||
 | 
						// 使用interface{}类型以支持不同泛型类型的队列
 | 
				
			||||||
 | 
						QuequeMap = make(map[string]interface{})
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewGenericQueue 创建新的通用循环队列
 | 
				
			||||||
 | 
					// capacity: 队列容量
 | 
				
			||||||
 | 
					// comparer: 比较函数,用于检查重复元素(可选)
 | 
				
			||||||
 | 
					// cooldown: 元素使用冷却时间(可选,默认0表示无冷却)
 | 
				
			||||||
 | 
					func NewGenericQueue[T any](capacity int, comparer func(T, T) bool, cooldown ...time.Duration) *GenericQueue[T] {
 | 
				
			||||||
 | 
						cd := time.Duration(0)
 | 
				
			||||||
 | 
						if len(cooldown) > 0 {
 | 
				
			||||||
 | 
							cd = cooldown[0]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &GenericQueue[T]{
 | 
				
			||||||
 | 
							data:     make([]T, capacity),
 | 
				
			||||||
 | 
							capacity: capacity,
 | 
				
			||||||
 | 
							lastUsed: make(map[int]time.Time),
 | 
				
			||||||
 | 
							cooldown: cd,
 | 
				
			||||||
 | 
							comparer: comparer,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Add 添加元素到队列
 | 
				
			||||||
 | 
					// item: 要添加的元素
 | 
				
			||||||
 | 
					// 返回: 是否添加成功
 | 
				
			||||||
 | 
					func (q *GenericQueue[T]) Add(item T) bool {
 | 
				
			||||||
 | 
						q.mutex.Lock()
 | 
				
			||||||
 | 
						defer q.mutex.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if q.size >= q.capacity {
 | 
				
			||||||
 | 
							return false // 队列已满
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 如果提供了比较函数,检查是否已存在相同的元素
 | 
				
			||||||
 | 
						if q.comparer != nil {
 | 
				
			||||||
 | 
							for i := 0; i < q.size; i++ {
 | 
				
			||||||
 | 
								if q.comparer(q.data[i], item) {
 | 
				
			||||||
 | 
									return false // 已存在
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						q.data[q.size] = item
 | 
				
			||||||
 | 
						q.size++
 | 
				
			||||||
 | 
						return true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Remove 从队列中移除指定的元素
 | 
				
			||||||
 | 
					// item: 要移除的元素
 | 
				
			||||||
 | 
					// 返回: 是否移除成功
 | 
				
			||||||
 | 
					func (q *GenericQueue[T]) Remove(item T) bool {
 | 
				
			||||||
 | 
						q.mutex.Lock()
 | 
				
			||||||
 | 
						defer q.mutex.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if q.comparer == nil {
 | 
				
			||||||
 | 
							return false // 没有比较函数无法移除
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i := 0; i < q.size; i++ {
 | 
				
			||||||
 | 
							if q.comparer(q.data[i], item) {
 | 
				
			||||||
 | 
								// 将后面的元素前移
 | 
				
			||||||
 | 
								for j := i; j < q.size-1; j++ {
 | 
				
			||||||
 | 
									q.data[j] = q.data[j+1]
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								q.size--
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// 调整current指针
 | 
				
			||||||
 | 
								if q.current >= q.size && q.size > 0 {
 | 
				
			||||||
 | 
									q.current = 0
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// 清理lastUsed记录
 | 
				
			||||||
 | 
								delete(q.lastUsed, i)
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetNext 获取下一个可用的元素(轮询方式)
 | 
				
			||||||
 | 
					// 返回: 元素和是否获取成功
 | 
				
			||||||
 | 
					func (q *GenericQueue[T]) GetNext() (T, bool) {
 | 
				
			||||||
 | 
						q.mutex.Lock()
 | 
				
			||||||
 | 
						defer q.mutex.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var zero T
 | 
				
			||||||
 | 
						if q.size == 0 {
 | 
				
			||||||
 | 
							return zero, false // 队列为空
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 如果没有设置冷却时间,直接返回下一个元素
 | 
				
			||||||
 | 
						if q.cooldown == 0 {
 | 
				
			||||||
 | 
							item := q.data[q.current]
 | 
				
			||||||
 | 
							q.lastUsed[q.current] = time.Now()
 | 
				
			||||||
 | 
							q.current = (q.current + 1) % q.size
 | 
				
			||||||
 | 
							return item, true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 寻找可用的元素(考虑冷却时间)
 | 
				
			||||||
 | 
						startPos := q.current
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							lastUsed, exists := q.lastUsed[q.current]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 如果元素从未使用过,或者已过冷却时间
 | 
				
			||||||
 | 
							if !exists || time.Since(lastUsed) >= q.cooldown {
 | 
				
			||||||
 | 
								item := q.data[q.current]
 | 
				
			||||||
 | 
								q.lastUsed[q.current] = time.Now()
 | 
				
			||||||
 | 
								q.current = (q.current + 1) % q.size
 | 
				
			||||||
 | 
								return item, true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							q.current = (q.current + 1) % q.size
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 如果遍历了一圈都没找到可用的元素
 | 
				
			||||||
 | 
							if q.current == startPos {
 | 
				
			||||||
 | 
								// 返回当前元素,忽略冷却时间
 | 
				
			||||||
 | 
								item := q.data[q.current]
 | 
				
			||||||
 | 
								q.lastUsed[q.current] = time.Now()
 | 
				
			||||||
 | 
								q.current = (q.current + 1) % q.size
 | 
				
			||||||
 | 
								return item, true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetRandom 随机获取一个元素
 | 
				
			||||||
 | 
					// 返回: 元素和是否获取成功
 | 
				
			||||||
 | 
					func (q *GenericQueue[T]) GetRandom() (T, bool) {
 | 
				
			||||||
 | 
						q.mutex.RLock()
 | 
				
			||||||
 | 
						defer q.mutex.RUnlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var zero T
 | 
				
			||||||
 | 
						if q.size == 0 {
 | 
				
			||||||
 | 
							return zero, false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 使用当前时间作为随机种子
 | 
				
			||||||
 | 
						index := int(time.Now().UnixNano()) % q.size
 | 
				
			||||||
 | 
						item := q.data[index]
 | 
				
			||||||
 | 
						q.lastUsed[index] = time.Now()
 | 
				
			||||||
 | 
						return item, true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetAll 获取所有元素的副本
 | 
				
			||||||
 | 
					// 返回: 元素切片
 | 
				
			||||||
 | 
					func (q *GenericQueue[T]) GetAll() []T {
 | 
				
			||||||
 | 
						q.mutex.RLock()
 | 
				
			||||||
 | 
						defer q.mutex.RUnlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						items := make([]T, q.size)
 | 
				
			||||||
 | 
						copy(items, q.data[:q.size])
 | 
				
			||||||
 | 
						return items
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Size 获取队列中的元素数量
 | 
				
			||||||
 | 
					// 返回: 元素数量
 | 
				
			||||||
 | 
					func (q *GenericQueue[T]) Size() int {
 | 
				
			||||||
 | 
						q.mutex.RLock()
 | 
				
			||||||
 | 
						defer q.mutex.RUnlock()
 | 
				
			||||||
 | 
						return q.size
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IsEmpty 检查队列是否为空
 | 
				
			||||||
 | 
					// 返回: 是否为空
 | 
				
			||||||
 | 
					func (q *GenericQueue[T]) IsEmpty() bool {
 | 
				
			||||||
 | 
						q.mutex.RLock()
 | 
				
			||||||
 | 
						defer q.mutex.RUnlock()
 | 
				
			||||||
 | 
						return q.size == 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IsFull 检查队列是否已满
 | 
				
			||||||
 | 
					// 返回: 是否已满
 | 
				
			||||||
 | 
					func (q *GenericQueue[T]) IsFull() bool {
 | 
				
			||||||
 | 
						q.mutex.RLock()
 | 
				
			||||||
 | 
						defer q.mutex.RUnlock()
 | 
				
			||||||
 | 
						return q.size >= q.capacity
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Clear 清空队列
 | 
				
			||||||
 | 
					func (q *GenericQueue[T]) Clear() {
 | 
				
			||||||
 | 
						q.mutex.Lock()
 | 
				
			||||||
 | 
						defer q.mutex.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						q.size = 0
 | 
				
			||||||
 | 
						q.current = 0
 | 
				
			||||||
 | 
						q.lastUsed = make(map[int]time.Time)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SetCooldown 设置元素使用冷却时间
 | 
				
			||||||
 | 
					// cooldown: 冷却时间
 | 
				
			||||||
 | 
					func (q *GenericQueue[T]) SetCooldown(cooldown time.Duration) {
 | 
				
			||||||
 | 
						q.mutex.Lock()
 | 
				
			||||||
 | 
						defer q.mutex.Unlock()
 | 
				
			||||||
 | 
						q.cooldown = cooldown
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetUsageInfo 获取元素使用信息
 | 
				
			||||||
 | 
					// 返回: 位置使用时间映射
 | 
				
			||||||
 | 
					func (q *GenericQueue[T]) GetUsageInfo() map[int]time.Time {
 | 
				
			||||||
 | 
						q.mutex.RLock()
 | 
				
			||||||
 | 
						defer q.mutex.RUnlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						usage := make(map[int]time.Time)
 | 
				
			||||||
 | 
						for k, v := range q.lastUsed {
 | 
				
			||||||
 | 
							usage[k] = v
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return usage
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// BatchAdd 批量添加元素
 | 
				
			||||||
 | 
					// items: 要添加的元素切片
 | 
				
			||||||
 | 
					// 返回: 成功添加的数量
 | 
				
			||||||
 | 
					func (q *GenericQueue[T]) BatchAdd(items []T) int {
 | 
				
			||||||
 | 
						count := 0
 | 
				
			||||||
 | 
						for _, item := range items {
 | 
				
			||||||
 | 
							if q.Add(item) {
 | 
				
			||||||
 | 
								count++
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return count
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Replace 替换所有元素
 | 
				
			||||||
 | 
					// items: 新的元素切片
 | 
				
			||||||
 | 
					// 返回: 是否替换成功
 | 
				
			||||||
 | 
					func (q *GenericQueue[T]) Replace(items []T) bool {
 | 
				
			||||||
 | 
						if len(items) > q.capacity {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						q.mutex.Lock()
 | 
				
			||||||
 | 
						defer q.mutex.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						q.size = 0
 | 
				
			||||||
 | 
						q.current = 0
 | 
				
			||||||
 | 
						q.lastUsed = make(map[int]time.Time)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, item := range items {
 | 
				
			||||||
 | 
							q.data[q.size] = item
 | 
				
			||||||
 | 
							q.size++
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ReplaceItem 替换指定的单个元素
 | 
				
			||||||
 | 
					// oldItem: 要被替换的元素
 | 
				
			||||||
 | 
					// newItem: 新的元素
 | 
				
			||||||
 | 
					// 返回: 是否替换成功
 | 
				
			||||||
 | 
					func (q *GenericQueue[T]) ReplaceItem(oldItem, newItem T) bool {
 | 
				
			||||||
 | 
						q.mutex.Lock()
 | 
				
			||||||
 | 
						defer q.mutex.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if q.comparer == nil {
 | 
				
			||||||
 | 
							return false // 没有比较函数无法查找元素
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i := 0; i < q.size; i++ {
 | 
				
			||||||
 | 
							if q.comparer(q.data[i], oldItem) {
 | 
				
			||||||
 | 
								q.data[i] = newItem
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false // 未找到要替换的元素
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Enqueue 入队操作(队列尾部添加元素)
 | 
				
			||||||
 | 
					// item: 要添加的元素
 | 
				
			||||||
 | 
					// 返回: 是否添加成功
 | 
				
			||||||
 | 
					func (q *GenericQueue[T]) Enqueue(item T) bool {
 | 
				
			||||||
 | 
						return q.Add(item)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Dequeue 出队操作(从队列头部移除并返回元素)
 | 
				
			||||||
 | 
					// 返回: 元素和是否成功
 | 
				
			||||||
 | 
					func (q *GenericQueue[T]) Dequeue() (T, bool) {
 | 
				
			||||||
 | 
						q.mutex.Lock()
 | 
				
			||||||
 | 
						defer q.mutex.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var zero T
 | 
				
			||||||
 | 
						if q.size == 0 {
 | 
				
			||||||
 | 
							return zero, false // 队列为空
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 获取队列头部元素
 | 
				
			||||||
 | 
						item := q.data[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 将后面的元素前移
 | 
				
			||||||
 | 
						for i := 0; i < q.size-1; i++ {
 | 
				
			||||||
 | 
							q.data[i] = q.data[i+1]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						q.size--
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 调整current指针
 | 
				
			||||||
 | 
						if q.current > 0 {
 | 
				
			||||||
 | 
							q.current--
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if q.current >= q.size && q.size > 0 {
 | 
				
			||||||
 | 
							q.current = 0
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 重新映射lastUsed(因为索引发生了变化)
 | 
				
			||||||
 | 
						newLastUsed := make(map[int]time.Time)
 | 
				
			||||||
 | 
						for index, lastTime := range q.lastUsed {
 | 
				
			||||||
 | 
							if index > 0 {
 | 
				
			||||||
 | 
								newLastUsed[index-1] = lastTime
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						q.lastUsed = newLastUsed
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return item, true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Peek 查看队列头部元素(不移除)
 | 
				
			||||||
 | 
					// 返回: 元素和是否成功
 | 
				
			||||||
 | 
					func (q *GenericQueue[T]) Peek() (T, bool) {
 | 
				
			||||||
 | 
						q.mutex.RLock()
 | 
				
			||||||
 | 
						defer q.mutex.RUnlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var zero T
 | 
				
			||||||
 | 
						if q.size == 0 {
 | 
				
			||||||
 | 
							return zero, false // 队列为空
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return q.data[0], true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PeekLast 查看队列尾部元素(不移除)
 | 
				
			||||||
 | 
					// 返回: 元素和是否成功
 | 
				
			||||||
 | 
					func (q *GenericQueue[T]) PeekLast() (T, bool) {
 | 
				
			||||||
 | 
						q.mutex.RLock()
 | 
				
			||||||
 | 
						defer q.mutex.RUnlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var zero T
 | 
				
			||||||
 | 
						if q.size == 0 {
 | 
				
			||||||
 | 
							return zero, false // 队列为空
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return q.data[q.size-1], true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ApiKeyInfo API密钥信息结构体
 | 
				
			||||||
 | 
					type ApiKeyInfo struct {
 | 
				
			||||||
 | 
						Key      string            `json:"key"`      // API密钥
 | 
				
			||||||
 | 
						Name     string            `json:"name"`     // 密钥名称
 | 
				
			||||||
 | 
						Weight   int               `json:"weight"`   // 权重
 | 
				
			||||||
 | 
						Enabled  bool              `json:"enabled"`  // 是否启用
 | 
				
			||||||
 | 
						Metadata map[string]string `json:"metadata"` // 元数据
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user