1、新接textverified
This commit is contained in:
		| @ -13,6 +13,7 @@ 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" | 	"go-admin/common/statuscode" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @ -194,7 +195,16 @@ func (e SmsPhone) Delete(c *gin.Context) { | |||||||
| 	e.OK(req.GetId(), "删除成功") | 	e.OK(req.GetId(), "删除成功") | ||||||
| } | } | ||||||
|  |  | ||||||
| // 租赁号码 | // GetNumber | ||||||
|  | // @Summary 获取号码 | ||||||
|  | // @Description 获取号码 | ||||||
|  | // @Tags 短信手机号 | ||||||
|  | // @Accept application/json | ||||||
|  | // @Product application/json | ||||||
|  | // @Param data body dto.GetNumberReq true "data" | ||||||
|  | // @Success 200 {object} response.Response{data=decimal.Decimal} "获取号码" | ||||||
|  | // @Router /api/v1/sms-phone/get-number [post] | ||||||
|  | // @Security Bearer | ||||||
| func (e SmsPhone) GetNumber(c *gin.Context) { | func (e SmsPhone) GetNumber(c *gin.Context) { | ||||||
| 	req := dto.GetNumberReq{} | 	req := dto.GetNumberReq{} | ||||||
| 	s := service.SmsPhone{} | 	s := service.SmsPhone{} | ||||||
| @ -211,36 +221,38 @@ func (e SmsPhone) GetNumber(c *gin.Context) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if err := req.Validate(); err != nil { | 	if err1 := req.Validate(); err1 != nil { | ||||||
| 		e.Logger.Errorf("租赁号码失败,%s", err.Error()) | 		e.Logger.Errorf("租赁号码失败,%s", err1.Error()) | ||||||
| 		e.Error(500, err, "服务器错误请联系管理员") | 		e.Error(500, err, "服务器错误请联系管理员") | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	var balance decimal.Decimal |  | ||||||
| 	code := statuscode.Success | 	code := statuscode.Success | ||||||
|  | 	lang := "zh" | ||||||
| 	userId := user.GetUserId(c) | 	userId := user.GetUserId(c) | ||||||
| 	smsService, err := servicesService.GetByCode(req.ServiceCode) | 	smsService, err := servicesService.GetByCode(req.PlatformCode, req.ServiceCode) | ||||||
|  |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		code = statuscode.SmsServiceUnavailable | 		code = statuscode.SmsServiceUnavailable | ||||||
| 	} else if smsService.Status == 2 { | 	} else if smsService.Status == 2 { | ||||||
| 		code = statuscode.SmsServiceUnavailable | 		code = statuscode.SmsServiceUnavailable | ||||||
| 	} else { | 	} | ||||||
|  |  | ||||||
|  | 	var balance decimal.Decimal | ||||||
|  | 	if code == statuscode.Success { | ||||||
| 		balance, code = s.GetNumber(&req, userId) | 		balance, code = s.GetNumber(&req, userId) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if code != statuscode.Success { | 	if code != statuscode.Success { | ||||||
| 		if code == statuscode.SmsServiceUnavailable { | 		if code == statuscode.SmsServiceUnavailable { | ||||||
| 			e.Error(code, nil, statuscode.GetMsg(code, "zh", smsService.Name)) | 			e.Error(code, nil, statuscode.GetMsg(code, lang, smsService.Name)) | ||||||
| 		} else { | 		} else { | ||||||
| 			e.Error(code, nil, statuscode.GetMsg(code, "zh")) | 			e.Error(code, nil, statuscode.GetMsg(code, lang)) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	e.OK(balance, "租赁号码成功") | 	e.OK(balance, "获取号码成功") | ||||||
| } | } | ||||||
|  |  | ||||||
| // 获取验证码 | // 获取验证码 | ||||||
| @ -327,7 +339,7 @@ func (e SmsPhone) WeakUp(c *gin.Context) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	userId := user.GetUserId(c) | 	userId := user.GetUserId(c) | ||||||
| 	code := s.WeakUp(&req, userId) | 	_, code := s.WeakUp(&req, userId, false) | ||||||
|  |  | ||||||
| 	if code != statuscode.Success { | 	if code != statuscode.Success { | ||||||
| 		e.Error(code, nil, statuscode.GetMsg(code, "zh")) | 		e.Error(code, nil, statuscode.GetMsg(code, "zh")) | ||||||
| @ -449,3 +461,289 @@ func (e SmsPhone) CleanMyPhone(c *gin.Context) { | |||||||
|  |  | ||||||
| 	e.OK(nil, "清理成功") | 	e.OK(nil, "清理成功") | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // OpenGetNumber | ||||||
|  | // @Summary 开放接口-获取号码 | ||||||
|  | // @Description 开放接口-获取号码 | ||||||
|  | // @Tags 短信手机号 | ||||||
|  | // @Accept application/json | ||||||
|  | // @Product application/json | ||||||
|  | // @Param data body dto.GetNumberReq true "data" | ||||||
|  | // @Success 200 {object} response.Response{data=decimal.Decimal} "获取号码" | ||||||
|  | // @Router /api/v1/open/sms-phone/get-number [post] | ||||||
|  | func (e SmsPhone) OpenGetNumber(c *gin.Context) { | ||||||
|  | 	req := dto.GetNumberReq{} | ||||||
|  | 	s := service.SmsPhone{} | ||||||
|  | 	servicesService := service.SmsServices{} | ||||||
|  | 	err := e.MakeContext(c). | ||||||
|  | 		MakeOrm(). | ||||||
|  | 		Bind(&req). | ||||||
|  | 		MakeService(&s.Service). | ||||||
|  | 		MakeService(&servicesService.Service). | ||||||
|  | 		Errors | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Logger.Error(err) | ||||||
|  | 		e.Error(500, err, err.Error()) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err1 := req.Validate(); err1 != nil { | ||||||
|  | 		e.Logger.Errorf("租赁号码失败,%s", err1.Error()) | ||||||
|  | 		e.Error(500, err, "服务器错误请联系管理员") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	lang := "zh" | ||||||
|  | 	code := statuscode.Success | ||||||
|  | 	userId, err := middleware.GetUserIdByApiKey(c) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		var errCode int | ||||||
|  |  | ||||||
|  | 		switch err { | ||||||
|  | 		case middleware.ErrApiUnActived: | ||||||
|  | 			errCode = statuscode.ApiUnActived | ||||||
|  | 		case middleware.ErrNoAccount: | ||||||
|  | 			errCode = statuscode.AccountNotFound | ||||||
|  | 		default: | ||||||
|  | 			e.Logger.Errorf("获取API用户id失败 %v", err) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		e.Error(errCode, nil, statuscode.GetMsg(errCode, lang)) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	smsService, err := servicesService.GetByCode(req.PlatformCode, req.ServiceCode) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		code = statuscode.SmsServiceUnavailable | ||||||
|  | 	} else if smsService.Status == 2 { | ||||||
|  | 		code = statuscode.SmsServiceUnavailable | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var resp dto.OpenGetNumberResp | ||||||
|  | 	if code == statuscode.Success { | ||||||
|  | 		resp, code = s.OpenGetNumber(&req, userId) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if code != statuscode.Success { | ||||||
|  | 		if code == statuscode.SmsServiceUnavailable { | ||||||
|  | 			e.Error(code, nil, statuscode.GetMsg(code, lang, smsService.Name)) | ||||||
|  | 		} else { | ||||||
|  | 			e.Error(code, nil, statuscode.GetMsg(code, lang)) | ||||||
|  | 		} | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	e.OK(resp, "获取号码成功") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // OpenGetCodeByActivationId 开放api-获取验证码 | ||||||
|  | // @Summary 开放接口-获取验证码 | ||||||
|  | // @Description 开放接口-获取验证码 | ||||||
|  | // @Tags 短信号码 | ||||||
|  | // @Accept application/json | ||||||
|  | // @Product application/json | ||||||
|  | // @Param data body dto.GetCodeReq true "body" | ||||||
|  | // @Success 200 {object} response.Response{data=dto.GetCodeResp} "成功" | ||||||
|  | // @Router /api/v1/open/sms-phone/get-code [post] | ||||||
|  | func (e SmsPhone) OpenGetCodeByActivationId(c *gin.Context) { | ||||||
|  | 	req := dto.GetCodeReq{} | ||||||
|  | 	s := service.SmsPhone{} | ||||||
|  | 	err := e.MakeContext(c). | ||||||
|  | 		MakeOrm(). | ||||||
|  | 		Bind(&req). | ||||||
|  | 		MakeService(&s.Service). | ||||||
|  | 		Errors | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Logger.Error(err) | ||||||
|  | 		e.Error(500, err, err.Error()) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	lang := "zh" | ||||||
|  | 	userId, err := middleware.GetUserIdByApiKey(c) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		var errCode int | ||||||
|  |  | ||||||
|  | 		switch err { | ||||||
|  | 		case middleware.ErrApiUnActived: | ||||||
|  | 			errCode = statuscode.ApiUnActived | ||||||
|  | 		case middleware.ErrNoAccount: | ||||||
|  | 			errCode = statuscode.AccountNotFound | ||||||
|  | 		default: | ||||||
|  | 			e.Logger.Errorf("获取API用户id失败 %v", err) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		e.Error(errCode, nil, statuscode.GetMsg(errCode, lang)) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	codeData, code := s.GetCodeByActivationId(&req, userId) | ||||||
|  |  | ||||||
|  | 	if code != statuscode.Success { | ||||||
|  | 		e.Error(code, nil, statuscode.GetMsg(code, lang)) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	e.OK(codeData, "获取验证码成功") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // OpenCancel 开放api-取消号码 | ||||||
|  | // @Summary 开放接口-取消号码 | ||||||
|  | // @Description 开放接口-取消号码 | ||||||
|  | // @Tags 短信号码 | ||||||
|  | // @Accept application/json | ||||||
|  | // @Product application/json | ||||||
|  | // @Param data body dto.SmsPhoneCancelNumberReq true "body" | ||||||
|  | // @Success 200 {object} response.Response "成功" | ||||||
|  | // @Router /api/v1/open/sms-phone/cancel [post] | ||||||
|  | func (e SmsPhone) OpenCancel(c *gin.Context) { | ||||||
|  | 	req := dto.SmsPhoneCancelNumberReq{} | ||||||
|  | 	s := service.SmsPhone{} | ||||||
|  | 	err := e.MakeContext(c). | ||||||
|  | 		MakeOrm(). | ||||||
|  | 		Bind(&req). | ||||||
|  | 		MakeService(&s.Service). | ||||||
|  | 		Errors | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Logger.Error(err) | ||||||
|  | 		e.Error(500, err, err.Error()) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	lang := "zh" | ||||||
|  | 	userId, err := middleware.GetUserIdByApiKey(c) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		var errCode int | ||||||
|  |  | ||||||
|  | 		switch err { | ||||||
|  | 		case middleware.ErrApiUnActived: | ||||||
|  | 			errCode = statuscode.ApiUnActived | ||||||
|  | 		case middleware.ErrNoAccount: | ||||||
|  | 			errCode = statuscode.AccountNotFound | ||||||
|  | 		default: | ||||||
|  | 			e.Logger.Errorf("获取API用户id失败 %v", err) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		e.Error(errCode, nil, statuscode.GetMsg(errCode, lang)) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	code := s.CancelNumber(&req, userId) | ||||||
|  |  | ||||||
|  | 	if code != statuscode.Success { | ||||||
|  | 		e.Error(code, nil, statuscode.GetMsg(code, lang)) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	e.OK(nil, "取消号码成功") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // OpenAutoRenewal 开放api-修改续费状态 | ||||||
|  | // @Summary 开放接口-修改自动续费状态 | ||||||
|  | // @Description 开放接口-修改自动续费状态 | ||||||
|  | // @Tags 短信号码 | ||||||
|  | // @Accept application/json | ||||||
|  | // @Product application/json | ||||||
|  | // @Param data body dto.SmsPhoneChangeAutoRenewReq true "body" | ||||||
|  | // @Success 200 {object} response.Response "成功" | ||||||
|  | // @Router /api/v1/open/sms-phone/auto-renewal [post] | ||||||
|  | func (e SmsPhone) OpenAutoRenewal(c *gin.Context) { | ||||||
|  | 	req := dto.SmsPhoneChangeAutoRenewReq{} | ||||||
|  | 	s := service.SmsPhone{} | ||||||
|  | 	err := e.MakeContext(c). | ||||||
|  | 		MakeOrm(). | ||||||
|  | 		Bind(&req). | ||||||
|  | 		MakeService(&s.Service). | ||||||
|  | 		Errors | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Logger.Error(err) | ||||||
|  | 		e.Error(500, err, err.Error()) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	lang := "zh" | ||||||
|  | 	userId, err := middleware.GetUserIdByApiKey(c) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		var errCode int | ||||||
|  |  | ||||||
|  | 		switch err { | ||||||
|  | 		case middleware.ErrApiUnActived: | ||||||
|  | 			errCode = statuscode.ApiUnActived | ||||||
|  | 		case middleware.ErrNoAccount: | ||||||
|  | 			errCode = statuscode.AccountNotFound | ||||||
|  | 		default: | ||||||
|  | 			e.Logger.Errorf("获取API用户id失败 %v", err) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		e.Error(errCode, nil, statuscode.GetMsg(errCode, lang)) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	code := s.ChangeAutoRenew(&req, userId) | ||||||
|  |  | ||||||
|  | 	if code != statuscode.Success { | ||||||
|  | 		e.Error(code, nil, statuscode.GetMsg(code, lang)) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	e.OK(nil, "修改自动续费状态成功") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // OpenApi 获取服务列表 | ||||||
|  | func (e SmsPhone) OpenGetServices(c *gin.Context) { | ||||||
|  | 	s := service.SmsServices{} | ||||||
|  | 	err := e.MakeContext(c). | ||||||
|  | 		MakeOrm(). | ||||||
|  | 		MakeService(&s.Service). | ||||||
|  | 		Errors | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Logger.Error(err) | ||||||
|  | 		e.Error(500, err, err.Error()) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	resp := []dto.SmsServicesGetListResp{} | ||||||
|  | 	err = s.GetList(&resp) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Logger.Error(err) | ||||||
|  | 		e.Error(500, err, statuscode.GetMsg(statuscode.ServerError, "en")) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	e.OK(resp, "success") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 开放接口-唤醒号码 | ||||||
|  | func (e SmsPhone) OpenWeakUp(c *gin.Context) { | ||||||
|  | 	req := dto.WeakUpReq{} | ||||||
|  | 	s := service.SmsPhone{} | ||||||
|  |  | ||||||
|  | 	err := e.MakeContext(c). | ||||||
|  | 		MakeOrm(). | ||||||
|  | 		Bind(&req). | ||||||
|  | 		MakeService(&s.Service). | ||||||
|  | 		Errors | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Logger.Error(err) | ||||||
|  | 		e.Error(500, err, err.Error()) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	lang := "en" | ||||||
|  | 	userId, err := middleware.GetUserIdByApiKey(c) | ||||||
|  |  | ||||||
|  | 	resp, code := s.WeakUp(&req, userId, false) | ||||||
|  |  | ||||||
|  | 	if code != statuscode.Success { | ||||||
|  | 		e.Error(code, nil, statuscode.GetMsg(code, lang)) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	e.OK(resp, "success") | ||||||
|  | } | ||||||
|  | |||||||
| @ -1,7 +1,14 @@ | |||||||
| package apis | package apis | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"crypto/hmac" | ||||||
|  | 	"crypto/sha512" | ||||||
|  | 	"encoding/base64" | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"io" | ||||||
|  | 	"net/http" | ||||||
|  | 	"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" | ||||||
| @ -193,7 +200,7 @@ func (e SmsReceiveLog) Delete(c *gin.Context) { | |||||||
| 	e.OK(req.GetId(), "删除成功") | 	e.OK(req.GetId(), "删除成功") | ||||||
| } | } | ||||||
|  |  | ||||||
| // WebHook 接收短信发送记录回调 | // daisysms WebHook 接收短信发送记录回调 | ||||||
| func (e SmsReceiveLog) WebHook(c *gin.Context) { | func (e SmsReceiveLog) WebHook(c *gin.Context) { | ||||||
| 	req := dto.SmsReceiveWebHookReq{} | 	req := dto.SmsReceiveWebHookReq{} | ||||||
| 	s := service.SmsReceiveLog{} | 	s := service.SmsReceiveLog{} | ||||||
| @ -217,3 +224,63 @@ func (e SmsReceiveLog) WebHook(c *gin.Context) { | |||||||
|  |  | ||||||
| 	e.OK(nil, "接收短信发送记录回调成功") | 	e.OK(nil, "接收短信发送记录回调成功") | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // textVerified WebHook 接收短信发送记录回调 | ||||||
|  | func (e SmsReceiveLog) TextVerifiedWebHook(c *gin.Context) { | ||||||
|  | 	// 1. 提前读取原始 body | ||||||
|  | 	bodyBytes, err := io.ReadAll(c.Request.Body) | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Logger.Error("Failed to read body:", err) | ||||||
|  | 		e.Error(500, err, "Failed to read request body") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 2. 验签 | ||||||
|  | 	signatureHeader := c.GetHeader("X-Webhook-Signature") | ||||||
|  | 	if !verifyWebhookSignature(signatureHeader, bodyBytes, "whsec_UMjFrzkK4YrlOb0AyOFDZAJJ7VWChOVOhYAGKE0e5oKw") { | ||||||
|  | 		e.Logger.Warn("Invalid webhook signature") | ||||||
|  | 		e.Error(401, fmt.Errorf("invalid signature"), "Invalid signature") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 3. 重置 body 给 Bind 用 | ||||||
|  | 	c.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) | ||||||
|  |  | ||||||
|  | 	req := dto.TextVerifiedWebHookReq{} | ||||||
|  | 	s := service.SmsTextVerified{} | ||||||
|  |  | ||||||
|  | 	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 | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	err = s.TextVerifiedWebHook(&req) | ||||||
|  | 	if err != nil { | ||||||
|  | 		c.JSON(500, gin.H{}) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	c.Status(http.StatusOK) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 验签方法 | ||||||
|  | func verifyWebhookSignature(header string, body []byte, secret string) bool { | ||||||
|  | 	const prefix = "HMAC-SHA512=" | ||||||
|  | 	if !strings.HasPrefix(header, prefix) { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	sigBase64 := strings.TrimPrefix(header, prefix) | ||||||
|  |  | ||||||
|  | 	mac := hmac.New(sha512.New, []byte(secret)) | ||||||
|  | 	mac.Write(body) | ||||||
|  | 	expectedMAC := mac.Sum(nil) | ||||||
|  | 	expectedBase64 := base64.StdEncoding.EncodeToString(expectedMAC) | ||||||
|  |  | ||||||
|  | 	return hmac.Equal([]byte(sigBase64), []byte(expectedBase64)) | ||||||
|  | } | ||||||
|  | |||||||
| @ -226,6 +226,12 @@ func (e SmsServices) GetPrice(c *gin.Context) { | |||||||
| 		e.Error(500, err, err.Error()) | 		e.Error(500, err, err.Error()) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if req.Valid() != nil { | ||||||
|  | 		e.Error(500, req.Valid(), req.Valid().Error()) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	price, err := s.GetPrice(&req) | 	price, err := s.GetPrice(&req) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		e.Logger.Errorf("获取价格失败,\r\n失败信息 %s", err.Error()) | 		e.Logger.Errorf("获取价格失败,\r\n失败信息 %s", err.Error()) | ||||||
| @ -234,3 +240,25 @@ func (e SmsServices) GetPrice(c *gin.Context) { | |||||||
| 	} | 	} | ||||||
| 	e.OK(price, "获取价格成功") | 	e.OK(price, "获取价格成功") | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // 获取翻译平台 | ||||||
|  | func (e SmsServices) GetPlatform(c *gin.Context) { | ||||||
|  | 	s := service.SmsServices{} | ||||||
|  | 	err := e.MakeContext(c). | ||||||
|  | 		MakeOrm(). | ||||||
|  | 		MakeService(&s.Service). | ||||||
|  | 		Errors | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Logger.Error(err) | ||||||
|  | 		e.Error(500, err, err.Error()) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	list := make([]dto.SmsPlatformGetListResp, 0) | ||||||
|  | 	err = s.GetPlatform(&list) | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Logger.Errorf("获取翻译平台失败,\r\n失败信息 %s", err.Error()) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	e.OK(list, "获取翻译平台成功") | ||||||
|  | } | ||||||
|  | |||||||
| @ -492,3 +492,61 @@ func (e SysUser) GetInfo(c *gin.Context) { | |||||||
| 	mp["code"] = 200 | 	mp["code"] = 200 | ||||||
| 	e.OK(mp, "") | 	e.OK(mp, "") | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (e SysUser) GetApiInfo(c *gin.Context) { | ||||||
|  | 	s := service.MemberApi{} | ||||||
|  | 	err := e.MakeContext(c). | ||||||
|  | 		MakeOrm(). | ||||||
|  | 		MakeService(&s.Service). | ||||||
|  | 		Errors | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Logger.Error(err) | ||||||
|  | 		e.Error(500, err, err.Error()) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	userId := user.GetUserId(c) | ||||||
|  | 	memberData := dto.MemberApiResp{} | ||||||
|  | 	err = s.GetByUserId(userId, &memberData) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Logger.Error(err) | ||||||
|  | 		e.Error(500, err, err.Error()) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	e.OK(memberData, "") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 修改Api状态 | ||||||
|  | func (e SysUser) ChangeApiStatus(c *gin.Context) { | ||||||
|  | 	req := dto.MemberApiChangeStatusReq{} | ||||||
|  | 	s := service.MemberApi{} | ||||||
|  | 	err := e.MakeContext(c). | ||||||
|  | 		MakeOrm(). | ||||||
|  | 		MakeService(&s.Service). | ||||||
|  | 		Bind(&req). | ||||||
|  | 		Errors | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Logger.Error(err) | ||||||
|  | 		e.Error(500, err, err.Error()) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := req.Validate(); err != nil { | ||||||
|  | 		e.Error(500, err, err.Error()) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	userId := user.GetUserId(c) | ||||||
|  | 	err = s.ChangeStatus(req.Id, userId, req.Status) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Logger.Errorf("修改api状态失败") | ||||||
|  |  | ||||||
|  | 		e.Error(500, err, err.Error()) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	e.OK(nil, "") | ||||||
|  | } | ||||||
|  | |||||||
							
								
								
									
										13
									
								
								app/admin/models/member_api.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								app/admin/models/member_api.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | |||||||
|  | package models | ||||||
|  |  | ||||||
|  | import "go-admin/common/models" | ||||||
|  |  | ||||||
|  | type MemberApi struct { | ||||||
|  | 	models.Model | ||||||
|  |  | ||||||
|  | 	UserId int    `json:"userId" gorm:"type:bigint;comment:用户id"` | ||||||
|  | 	ApiKey string `json:"api" gorm:"type:varchar(255);comment:api_key"` | ||||||
|  | 	Status int    `json:"status" gorm:"type:tinyint;comment:状态 1-启用 2-禁用"` | ||||||
|  | 	models.ModelTime | ||||||
|  | 	models.ControlBy | ||||||
|  | } | ||||||
| @ -10,15 +10,16 @@ import ( | |||||||
| type SmsPhone struct { | type SmsPhone struct { | ||||||
| 	models.Model | 	models.Model | ||||||
|  |  | ||||||
|  | 	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"` | ||||||
| 	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-长效"` | ||||||
| 	Period          int             `json:"period" gorm:"type:int;comment:时长(月)"` | 	Period          int             `json:"period" gorm:"type:int;comment:时长(月)"` | ||||||
| 	Phone           string          `json:"phone" gorm:"type:varchar(30);comment:号码"` | 	Phone           string          `json:"phone" gorm:"type:varchar(30);comment:号码"` | ||||||
| 	ActivationId    int             `json:"activationId" gorm:"type:int;comment:激活码id"` | 	ActivationId    string          `json:"activationId" gorm:"type:varchar(50);comment:激活码id"` | ||||||
| 	NewActivationId int             `json:"newActivationId" gorm:"type:int;comment:新激活码id 每次获取验证码会刷新"` | 	NewActivationId string          `json:"newActivationId" gorm:"type:varchar(50);comment:新激活码id 每次获取验证码会刷新"` | ||||||
| 	MessageId       int             `json:"messageId" gorm:"type:int;comment:短信模板id"` | 	MessageId       string          `json:"messageId" gorm:"type:varchar(50);comment:短信模板id"` | ||||||
| 	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:过期时间"` | ||||||
|  | |||||||
| @ -2,15 +2,21 @@ package models | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"go-admin/common/models" | 	"go-admin/common/models" | ||||||
|  |  | ||||||
|  | 	"github.com/shopspring/decimal" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type SmsServices struct { | type SmsServices struct { | ||||||
| 	models.Model | 	models.Model | ||||||
|  |  | ||||||
| 	Name              string `json:"name" gorm:"type:varchar(255);comment:服务名称"` | 	PlatformCode      string          `json:"platformCode" gorm:"type:varchar(20);comment:平台编码"` | ||||||
| 	Code              string `json:"code" gorm:"type:varchar(50);comment:编码"` | 	Name              string          `json:"name" gorm:"type:varchar(255);comment:服务名称"` | ||||||
| 	ExpirationMinutes int    `json:"expirationMinutes" gorm:"type:int;comment:过期时间(分钟)"` | 	Code              string          `json:"code" gorm:"type:varchar(50);comment:编码"` | ||||||
| 	Status            int    `json:"status" gorm:"type:tinyint;comment:状态 1-启用 2-禁用"` | 	ExpirationMinutes int             `json:"expirationMinutes" gorm:"type:int;comment:过期时间(分钟)"` | ||||||
|  | 	Status            int             `json:"status" gorm:"type:tinyint;comment:状态 1-启用 2-禁用"` | ||||||
|  | 	Price             decimal.Decimal `json:"price" gorm:"type:decimal(10,2);comment:价格"` | ||||||
|  | 	LongPrice         decimal.Decimal `json:"longPrice" gorm:"type:decimal(10,2);comment:长号码价格"` | ||||||
|  | 	Icon              string          `json:"icon" gorm:"type:varchar(255);comment:图标"` | ||||||
| 	models.ModelTime | 	models.ModelTime | ||||||
| 	models.ControlBy | 	models.ControlBy | ||||||
| } | } | ||||||
|  | |||||||
| @ -29,6 +29,16 @@ func registerSmsPhoneRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddl | |||||||
| 		r1.PUT("auto-renewal", api.ChangeAutoRenew)                //修改自动续费状态 | 		r1.PUT("auto-renewal", api.ChangeAutoRenew)                //修改自动续费状态 | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	r2 := v1.Group("/sms").Use(middleware.FrontedAuth) | ||||||
|  | 	{ | ||||||
|  | 		r2.POST("getNumber", api.OpenGetNumber)                        // 开放接口 租赁号码 | ||||||
|  | 		r2.GET("getCodeByActivationId", api.OpenGetCodeByActivationId) //开放接口-获取验证码 | ||||||
|  | 		r2.PUT("cancel", api.OpenCancel)                               //开放接口-取消号码 | ||||||
|  | 		r2.PUT("auto-renewal", api.OpenAutoRenewal)                    //开放接口-修改自动续费 | ||||||
|  | 		r2.GET("services", api.OpenGetServices)                        //开放接口-获取服务列表 | ||||||
|  | 		r2.POST("weak-up", api.OpenWeakUp)                             //开放接口-唤醒号码 | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	r := v1.Group("/sms-phone").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole()) | 	r := v1.Group("/sms-phone").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole()) | ||||||
| 	{ | 	{ | ||||||
| 		r.GET("", actions.PermissionAction(), api.GetPage) | 		r.GET("", actions.PermissionAction(), api.GetPage) | ||||||
|  | |||||||
| @ -27,6 +27,7 @@ func registerSmsReceiveLogRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWT | |||||||
|  |  | ||||||
| 	r1 := v1.Group("sms-receive-log") | 	r1 := v1.Group("sms-receive-log") | ||||||
| 	{ | 	{ | ||||||
| 		r1.POST("webhook", api.WebHook) //接收短信发送记录回调 | 		r1.POST("webhook", api.WebHook)                           //接收短信发送记录回调 | ||||||
|  | 		r1.POST("text-verified-webhook", api.TextVerifiedWebHook) //接收TextVerified短信发送记录回调 | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -27,7 +27,8 @@ func registerSmsServicesRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMi | |||||||
|  |  | ||||||
| 	r1 := v1.Group("/sms-services").Use(authMiddleware.MiddlewareFunc()) | 	r1 := v1.Group("/sms-services").Use(authMiddleware.MiddlewareFunc()) | ||||||
| 	{ | 	{ | ||||||
| 		r1.GET("list", api.GetList)   //获取服务列表 | 		r1.GET("/platform", api.GetPlatform) //获取平台列表 | ||||||
| 		r1.GET("price", api.GetPrice) //获取服务价格 | 		r1.GET("/list", api.GetList)         //获取服务列表 | ||||||
|  | 		r1.GET("/price", api.GetPrice)       //获取服务价格 | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,11 +1,12 @@ | |||||||
| package router | package router | ||||||
|  |  | ||||||
| import ( | 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/app/admin/apis" | ||||||
| 	"go-admin/common/actions" | 	"go-admin/common/actions" | ||||||
| 	"go-admin/common/middleware" | 	"go-admin/common/middleware" | ||||||
|  |  | ||||||
|  | 	"github.com/gin-gonic/gin" | ||||||
|  | 	jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func init() { | func init() { | ||||||
| @ -35,5 +36,7 @@ func registerSysUserRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddle | |||||||
| 	v1auth := v1.Group("").Use(authMiddleware.MiddlewareFunc()) | 	v1auth := v1.Group("").Use(authMiddleware.MiddlewareFunc()) | ||||||
| 	{ | 	{ | ||||||
| 		v1auth.GET("/getinfo", api.GetInfo) | 		v1auth.GET("/getinfo", api.GetInfo) | ||||||
|  | 		v1auth.GET("/api-info", api.GetApiInfo) | ||||||
|  | 		v1auth.PUT("/api-info", api.ChangeApiStatus) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -44,7 +44,7 @@ func (e *CliProxyService) GetTrafficInfo() ([]dto.CliProxyTraffics, error) { | |||||||
| 		client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.CliproxyUrl, headers) | 		client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.CliproxyUrl, headers) | ||||||
| 		api := "/json/traffic_coutry.json" | 		api := "/json/traffic_coutry.json" | ||||||
|  |  | ||||||
| 		err := client.Get(api, nil, &resp) | 		_, err := client.Get(api, nil, &resp) | ||||||
|  |  | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return nil, err | 			return nil, err | ||||||
|  | |||||||
							
								
								
									
										30
									
								
								app/admin/service/dto/member_api.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								app/admin/service/dto/member_api.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | |||||||
|  | package dto | ||||||
|  |  | ||||||
|  | import "errors" | ||||||
|  |  | ||||||
|  | type MemberApiInsertReq struct { | ||||||
|  | 	UserId int `json:"userId" gorm:"type:bigint;comment:用户id"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type MemberApiResp struct { | ||||||
|  | 	Id     int    `json:"id"` | ||||||
|  | 	UserId int    `json:"userId" gorm:"type:bigint;comment:用户id"` | ||||||
|  | 	ApiKey string `json:"api" gorm:"type:varchar(255);comment:apiKey"` | ||||||
|  | 	Status int    `json:"status" gorm:"type:tinyint;comment:状态 1-启用 2-禁用"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type MemberApiChangeStatusReq struct { | ||||||
|  | 	Id     int `json:"id"` | ||||||
|  | 	Status int `json:"status" comment:"1-启用 2-禁用"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *MemberApiChangeStatusReq) Validate() error { | ||||||
|  | 	if e.Id <= 0 { | ||||||
|  | 		return errors.New("id不能为空") | ||||||
|  | 	} | ||||||
|  | 	if e.Status != 1 && e.Status != 2 { | ||||||
|  | 		return errors.New("状态不正确") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
| @ -5,11 +5,15 @@ import ( | |||||||
| 	"go-admin/app/admin/models" | 	"go-admin/app/admin/models" | ||||||
| 	"go-admin/common/dto" | 	"go-admin/common/dto" | ||||||
| 	common "go-admin/common/models" | 	common "go-admin/common/models" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/shopspring/decimal" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type SmsPhoneGetPageReq struct { | 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"` | ||||||
| 	ServiceCode    string `form:"serviceCode"  search:"type:exact;column:service_code;table:sms_phone" comment:"服务code"` | 	ServiceCode    string `form:"serviceCode"  search:"type:exact;column:service_code;table:sms_phone" comment:"服务code"` | ||||||
| 	Type           int    `form:"type"  search:"-" comment:"类型 0-短效 1-长效"` | 	Type           int    `form:"type"  search:"-" comment:"类型 0-短效 1-长效"` | ||||||
| 	SmsPhoneOrder | 	SmsPhoneOrder | ||||||
| @ -109,9 +113,10 @@ func (s *SmsPhoneDeleteReq) GetId() interface{} { | |||||||
| } | } | ||||||
|  |  | ||||||
| type GetNumberReq struct { | type GetNumberReq struct { | ||||||
| 	Type        int    `json:"type" form:"type" comment:"类型 0-短效 1-长效"` | 	PlatformCode string `json:"platformCode" form:"platformCode" commment:"平台code"` | ||||||
| 	ServiceCode string `json:"serviceCode" form:"serviceCode" comment:"服务code"` | 	Type         int    `json:"type" form:"type" comment:"类型 0-短效 1-长效"` | ||||||
| 	Period      int    `json:"period" form:"period" comment:"时长(月)"` | 	ServiceCode  string `json:"serviceCode" form:"serviceCode" comment:"服务code"` | ||||||
|  | 	Period       int    `json:"period" form:"period" comment:"时长(月)"` | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *GetNumberReq) Validate() error { | func (s *GetNumberReq) Validate() error { | ||||||
| @ -131,21 +136,26 @@ func (s *GetNumberReq) Validate() error { | |||||||
| } | } | ||||||
|  |  | ||||||
| type GetCodeReq struct { | type GetCodeReq struct { | ||||||
| 	ActivationIds []int `form:"activationIds"` | 	ActivationIds []string `form:"activationIds"` | ||||||
| } | } | ||||||
|  |  | ||||||
| type GetCodeResp struct { | type GetCodeResp struct { | ||||||
| 	ActivationId int    `json:"activationId" comment:"激活码id"` | 	ActivationId string `json:"activationId" comment:"激活码id"` | ||||||
| 	Code         string `json:"code" comment:"验证码"` | 	Code         string `json:"code" comment:"验证码"` | ||||||
| 	Status       int    `json:"status" comment:"状态 1-等待验证码 2-已获取"` | 	Status       int    `json:"status" comment:"状态 1-等待验证码 2-已获取"` | ||||||
| } | } | ||||||
|  |  | ||||||
| type WeakUpReq struct { | type WeakUpReq struct { | ||||||
| 	ActivationId int `json:"activationId" comment:"激活码id"` | 	ActivationId string `json:"activationId" comment:"激活码id"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type WeakUpResp struct { | ||||||
|  | 	ActivationId string `json:"activationId" comment:"激活id"` | ||||||
|  | 	MessageId    string `json:"messageId" commment:"消息id"` | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *WeakUpReq) Validate() error { | func (s *WeakUpReq) Validate() error { | ||||||
| 	if s.ActivationId <= 0 { | 	if s.ActivationId == "" { | ||||||
| 		return errors.New("激活码id不能为空") | 		return errors.New("激活码id不能为空") | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @ -165,14 +175,42 @@ func (s *DeleteMyNumberReq) Validate() error { | |||||||
| } | } | ||||||
|  |  | ||||||
| type SmsPhoneCancelNumberReq struct { | type SmsPhoneCancelNumberReq struct { | ||||||
| 	Id int `json:"id" comment:"短信号码id"` | 	Id           int    `json:"id" comment:"短信号码id"` | ||||||
|  | 	ActivationId string `json:"activationId" comment:"短信id"` | ||||||
| } | } | ||||||
|  |  | ||||||
| type SmsPhoneChangeAutoRenewReq struct { | type SmsPhoneChangeAutoRenewReq struct { | ||||||
| 	Id        int `json:"id" comment:"短信号码id"` | 	Id           int    `json:"id" comment:"短信号码id"` | ||||||
| 	AutoRenew int `json:"autoRenew" comment:"是否自动续费 1-不自动续费 2-自动续费"` | 	ActivationId string `json:"activationId" comment:"激活码id id或activationId "` | ||||||
|  | 	AutoRenew    int    `json:"autoRenew" comment:"是否自动续费 1-不自动续费 2-自动续费"` | ||||||
| } | } | ||||||
|  |  | ||||||
| type SmsPhoneCleanMyPhoneReq struct { | type SmsPhoneCleanMyPhoneReq struct { | ||||||
| 	Type int `json:"type" comment:"类型 0-短效 1-长效"` | 	Type int `json:"type" comment:"类型 0-短效 1-长效"` | ||||||
| } | } | ||||||
|  |  | ||||||
|  | type SmsPhoneGetPhoneResp struct { | ||||||
|  | 	Phone string     `json:"phone"` | ||||||
|  | 	EndAt *time.Time `json:"endTime"` | ||||||
|  | 	Id    string     `json:"id"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type DaisysmsPriceResp struct { | ||||||
|  | 	Key        string `json:"key"` | ||||||
|  | 	Name       string `json:"name"` | ||||||
|  | 	Count      int    `json:"count"` | ||||||
|  | 	Cost       string `json:"cost" comment:"单价"` | ||||||
|  | 	Repeatable bool   `json:"repeatable"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type OpenGetNumberResp struct { | ||||||
|  | 	Blance       decimal.Decimal `json:"balance"` | ||||||
|  | 	Number       string          `json:"number"` | ||||||
|  | 	ActivationId string          `json:"activationId"` | ||||||
|  | 	ExpireTime   *time.Time      `json:"expireTime"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type OpenWakeUpReq struct { | ||||||
|  | 	PlatformCode string `json:"platformCode" comment:"短信平台"` | ||||||
|  | 	ActivationId string `json:"activationId" comment:"平台id"` | ||||||
|  | } | ||||||
|  | |||||||
| @ -1,13 +1,19 @@ | |||||||
| package dto | package dto | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"errors" | ||||||
| 	"go-admin/app/admin/models" | 	"go-admin/app/admin/models" | ||||||
| 	"go-admin/common/dto" | 	"go-admin/common/dto" | ||||||
| 	common "go-admin/common/models" | 	common "go-admin/common/models" | ||||||
|  |  | ||||||
|  | 	"github.com/shopspring/decimal" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type SmsServicesGetPageReq struct { | type SmsServicesGetPageReq struct { | ||||||
| 	dto.Pagination `search:"-"` | 	dto.Pagination `search:"-"` | ||||||
|  | 	PlatformCode   string `form:"platformCode"  search:"type:exact;column:platform_code;table:sms_services" comment:"平台编码"` | ||||||
|  | 	Name           string `form:"name"  search:"type:contains;column:name;table:sms_services" comment:"服务名称"` | ||||||
|  | 	Status         int    `form:"status"  search:"-"` | ||||||
| 	SmsServicesOrder | 	SmsServicesOrder | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -27,9 +33,14 @@ func (m *SmsServicesGetPageReq) GetNeedSearch() interface{} { | |||||||
| } | } | ||||||
|  |  | ||||||
| type SmsServicesInsertReq struct { | type SmsServicesInsertReq struct { | ||||||
| 	Id   int    `json:"-" comment:""` // | 	Id                int             `json:"-" comment:""` // | ||||||
| 	Name string `json:"name" comment:"服务名称"` | 	Name              string          `json:"name" comment:"服务名称"` | ||||||
| 	Code string `json:"code" comment:"编码"` | 	Code              string          `json:"code" comment:"编码"` | ||||||
|  | 	PlatformCode      string          `json:"platformCode" comment:"平台编码"` | ||||||
|  | 	Status            int             `json:"status" comment:"状态 1-启用 2-禁"` | ||||||
|  | 	Price             decimal.Decimal `json:"price" comment:"价格"` | ||||||
|  | 	LongPrice         decimal.Decimal `json:"longPrice" comment:"长号码价格"` | ||||||
|  | 	ExpirationMinutes int             `json:"expirationMinutes" comment:"过期时间(分钟)"` | ||||||
| 	common.ControlBy | 	common.ControlBy | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -39,6 +50,11 @@ func (s *SmsServicesInsertReq) Generate(model *models.SmsServices) { | |||||||
| 	} | 	} | ||||||
| 	model.Name = s.Name | 	model.Name = s.Name | ||||||
| 	model.Code = s.Code | 	model.Code = s.Code | ||||||
|  | 	model.PlatformCode = s.PlatformCode | ||||||
|  | 	model.Status = s.Status | ||||||
|  | 	model.Price = s.Price | ||||||
|  | 	model.LongPrice = s.LongPrice | ||||||
|  | 	model.ExpirationMinutes = s.ExpirationMinutes | ||||||
| 	model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的 | 	model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的 | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -47,9 +63,14 @@ func (s *SmsServicesInsertReq) GetId() interface{} { | |||||||
| } | } | ||||||
|  |  | ||||||
| type SmsServicesUpdateReq struct { | type SmsServicesUpdateReq struct { | ||||||
| 	Id   int    `uri:"id" comment:""` // | 	Id                int             `uri:"id" comment:""` // | ||||||
| 	Name string `json:"name" comment:"服务名称"` | 	Name              string          `json:"name" comment:"服务名称"` | ||||||
| 	Code string `json:"code" comment:"编码"` | 	Code              string          `json:"code" comment:"编码"` | ||||||
|  | 	PlatformCode      string          `json:"platformCode" comment:"平台编码"` | ||||||
|  | 	Status            int             `json:"status" comment:"状态 1-启用 2-禁"` | ||||||
|  | 	Price             decimal.Decimal `json:"price" comment:"价格"` | ||||||
|  | 	LongPrice         decimal.Decimal `json:"longPrice" comment:"长号码价格"` | ||||||
|  | 	ExpirationMinutes int             `json:"expirationMinutes" comment:"过期时间(分钟)"` | ||||||
| 	common.ControlBy | 	common.ControlBy | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -59,6 +80,11 @@ func (s *SmsServicesUpdateReq) Generate(model *models.SmsServices) { | |||||||
| 	} | 	} | ||||||
| 	model.Name = s.Name | 	model.Name = s.Name | ||||||
| 	model.Code = s.Code | 	model.Code = s.Code | ||||||
|  | 	model.PlatformCode = s.PlatformCode | ||||||
|  | 	model.Status = s.Status | ||||||
|  | 	model.Price = s.Price | ||||||
|  | 	model.LongPrice = s.LongPrice | ||||||
|  | 	model.ExpirationMinutes = s.ExpirationMinutes | ||||||
| 	model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的 | 	model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的 | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -85,11 +111,38 @@ func (s *SmsServicesDeleteReq) GetId() interface{} { | |||||||
| } | } | ||||||
|  |  | ||||||
| type SmsServicesGetListResp struct { | type SmsServicesGetListResp struct { | ||||||
| 	Name   string `json:"name" comment:"服务名称"` | 	PlatformCode string          `json:"platformCode" comment:"平台编码"` | ||||||
| 	Code   string `json:"code" comment:"编码"` | 	PlatformName string          `json:"platformName" comment:"平台名称"` | ||||||
| 	Status int    `json:"status" comment:"状态"` | 	Code         string          `json:"code" comment:"编码"` | ||||||
|  | 	Name         string          `json:"name" comment:"服务名称"` | ||||||
|  | 	Status       int             `json:"status" comment:"状态"` | ||||||
|  | 	Price        decimal.Decimal `json:"price" comment:"价格"` | ||||||
|  | 	LongPrice    decimal.Decimal `json:"longPrice" comment:"长号码价格"` | ||||||
| } | } | ||||||
|  |  | ||||||
| type SmsGetPriceReq struct { | type SmsGetPriceReq struct { | ||||||
| 	Type int `json:"type" form:"type" comment:"类型"` | 	PlatformCode string `json:"platformCode" form:"platformCode" comment:"平台code"` | ||||||
|  | 	ServiceCode  string `json:"serviceCode" form:"serviceCode" comment:"服务code"` | ||||||
|  | 	Type         int    `json:"type" form:"type" comment:"类型"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *SmsGetPriceReq) Valid() error { | ||||||
|  | 	if e.PlatformCode == "" { | ||||||
|  | 		return errors.New("平台code不能为空") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if e.ServiceCode == "" { | ||||||
|  | 		return errors.New("服务code不能为空") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if e.Type < 0 || e.Type > 1 { | ||||||
|  | 		return errors.New("类型错误") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type SmsPlatformGetListResp struct { | ||||||
|  | 	Label string `json:"label" comment:"平台名称"` | ||||||
|  | 	Value string `json:"value" comment:"平台code"` | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										214
									
								
								app/admin/service/dto/text_verified.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								app/admin/service/dto/text_verified.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,214 @@ | |||||||
|  | package dto | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/shopspring/decimal" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // 登录相应 | ||||||
|  | type TextVerifiedLoginResp struct { | ||||||
|  | 	Token     string `json:"token"` | ||||||
|  | 	ExpiresIn int    `json:"expiresIn"` | ||||||
|  | 	ExpiresAt string `json:"expiresAt"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 获取区号响应 | ||||||
|  | type TextVerifiedAreaResp struct { | ||||||
|  | 	AreaCode string `json:"areaCode"` | ||||||
|  | 	State    string `json:"state"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 服务列表 | ||||||
|  | type TextVerifiedServeResp struct { | ||||||
|  | 	ServiceName string `json:"serviceName"` | ||||||
|  | 	Capability  string `json:"capability"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 创建租赁 | ||||||
|  | type TextVerifiedCreateRewalReq struct { | ||||||
|  | 	AllowBackOrderReservations bool     `json:"allowBackOrderReservations" comment:"如果设置为 true,当请求的租赁商品缺货时,将创建租赁订单"` | ||||||
|  | 	AlwaysOn                   bool     `json:"alwaysOn" comment:"如果设置为 true,则有库存时将分配一条不需要唤醒的线路"` | ||||||
|  | 	AreaCodeSelectOption       []string `json:"areaCodeSelectOption" comment:"可选的区号数组"` | ||||||
|  | 	Duration                   string   `json:"duration" comment:"枚举 oneDay┃threeDay┃sevenDay┃fourteenDay┃thirtyDay┃ninetyDay┃oneYear"` | ||||||
|  | 	IsRenewable                bool     `json:"isRenewable" comment:"是否可再生"` | ||||||
|  | 	NumberType                 string   `json:"numberType" comment:"枚举 mobile┃voip┃landline"` | ||||||
|  | 	BillingCycleIdToAssignTo   string   `json:"billingCycleIdToAssignTo"` | ||||||
|  | 	ServiceName                string   `json:"serviceName"` | ||||||
|  | 	Capability                 string   `json:"capability" comment:"枚举 sms┃call┃smsAndCall"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 回调请求 | ||||||
|  | type TextVerifiedWebHookReq struct { | ||||||
|  | 	Attempt    int                    `json:"attempt" comment:"尝试次数"` | ||||||
|  | 	OccurredAt string                 `json:"occurredAt" comment:"发生时间"` | ||||||
|  | 	Event      string                 `json:"event" comment:"事件类型 v2.rental.billingcycle.renewed | v2.rental.billingcycle.expired | v2.sms.received | v2.rental.backorder.fulfilled | v2.reservation.created"` | ||||||
|  | 	Id         string                 `json:"id" comment:"唯一标识"` | ||||||
|  | 	Data       map[string]interface{} `json:"data" comment:"数据"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 续费成功回调请求 | ||||||
|  | type TextVerifiedWebHookRenewedReq struct { | ||||||
|  | 	BillingCycleId string `json:"billingCycleId" comment:"续费的计费周期ID"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 计费周期过期回调请求 - 当您帐户的计费周期到期时触发。 | ||||||
|  | type TextVerifiedWebHookBillingCycleExpiredReq struct { | ||||||
|  | 	BillingCycleId string `json:"billingCycleId" comment:"过期的计费周期ID"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 收到短信回调请求 - 当收到短信并将其分配到您的帐户时触发。 | ||||||
|  | type TextVerifiedWebHookSmsReceivedReq struct { | ||||||
|  | 	From          string `json:"from" comment:"发送号码"` | ||||||
|  | 	To            string `json:"to" comment:"接收号码"` | ||||||
|  | 	CreatedAt     string `json:"createdAt" comment:"创建时间"` | ||||||
|  | 	SmsContent    string `json:"smsContent" comment:"短信内容"` | ||||||
|  | 	ParsedCode    string `json:"parsedCode" comment:"解析后的验证码"` | ||||||
|  | 	Encrypted     bool   `json:"encrypted" comment:"短信内容是否静态加密 "` | ||||||
|  | 	ReservationId string `json:"reservationId" comment:"此短信被分配到的预订的 ID。"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 订单预定完成时触发 | ||||||
|  | type TextVerifiedWebHookFulfilledReq struct { | ||||||
|  | 	BackOrderId string `json:"backOrderId" comment:"订单预定ID"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 预定订单创建回调 | ||||||
|  | type TextVerifiedWebHookReservationCreatedReq struct { | ||||||
|  | 	Id   string `json:"id" comment:"订单ID"` | ||||||
|  | 	Type string `json:"type" comment:"订单类型 verifications | rentals"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 唤醒请求 | ||||||
|  | type TextVerifiedWakeUpReq struct { | ||||||
|  | 	RevervationId string `json:"reservationId"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 唤醒响应 | ||||||
|  | type TextVerifiedWakeUpResp struct { | ||||||
|  | 	Id               string     `json:"id"` | ||||||
|  | 	UsageWindowStart *time.Time `json:"usageWindowStart"` | ||||||
|  | 	UsageWindowEnd   *time.Time `json:"usageWindowEnd"` | ||||||
|  | 	IsScheduled      bool       `json:"isScheduled"` | ||||||
|  | 	ReservationId    string     `json:"reservationId" comment:"预定id"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 请求统一响应 | ||||||
|  | type TextVerifiedResp struct { | ||||||
|  | 	Method string `json:"method"` | ||||||
|  | 	Href   string `json:"href"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type TextVerifiedCreateRentalReservationDto struct { | ||||||
|  | 	ID              string           `json:"id" comment:"号码id"` | ||||||
|  | 	Link            TextVerifiedResp `json:"link"` | ||||||
|  | 	ReservationType string           `json:"reservationType"` | ||||||
|  | 	ServiceName     string           `json:"serviceName"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 长效号码响应 | ||||||
|  | type SaleResponse struct { | ||||||
|  | 	CreatedAt             time.Time                                `json:"createdAt"` | ||||||
|  | 	ID                    string                                   `json:"id" comment:"销售id"` | ||||||
|  | 	BackOrderReservations []interface{}                            `json:"backOrderReservations"` // 由于数组为空,使用 interface{} | ||||||
|  | 	Reservations          []TextVerifiedCreateRentalReservationDto `json:"reservations" comment:"号码信息"` | ||||||
|  | 	State                 string                                   `json:"state"` | ||||||
|  | 	Total                 float64                                  `json:"total"` | ||||||
|  | 	UpdatedAt             time.Time                                `json:"updatedAt"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 单次验证码创建后查询 | ||||||
|  | type VerificationDTO struct { | ||||||
|  | 	Number      string           `json:"number"` | ||||||
|  | 	SMS         TextVerifiedResp `json:"sms"` | ||||||
|  | 	Calls       TextVerifiedResp `json:"calls"` | ||||||
|  | 	CreatedAt   time.Time        `json:"createdAt"` | ||||||
|  | 	EndsAt      time.Time        `json:"endsAt"` | ||||||
|  | 	ID          string           `json:"id"` | ||||||
|  | 	Cancel      Action           `json:"cancel"` | ||||||
|  | 	Reactivate  Action           `json:"reactivate"` | ||||||
|  | 	Report      Action           `json:"report"` | ||||||
|  | 	Reuse       ReuseInfo        `json:"reuse"` | ||||||
|  | 	Sale        TextVerifiedResp `json:"sale"` | ||||||
|  | 	ServiceName string           `json:"serviceName"` | ||||||
|  | 	// State 表示验证或服务的当前状态。 | ||||||
|  | 	// 状态是以下枚举值之一,代表了从创建到完成或取消的生命周期: | ||||||
|  | 	// * 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"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 长效详情 | ||||||
|  | type VerificationRentalDetailResp struct { | ||||||
|  | 	Number      string           `json:"number"` | ||||||
|  | 	SMS         TextVerifiedResp `json:"sms"` | ||||||
|  | 	Calls       TextVerifiedResp `json:"calls"` | ||||||
|  | 	CreatedAt   time.Time        `json:"createdAt"` | ||||||
|  | 	ID          string           `json:"id"` | ||||||
|  | 	Sale        TextVerifiedResp `json:"sale"` | ||||||
|  | 	SaleId      string           `json:"saleId"` | ||||||
|  | 	ServiceName string           `json:"serviceName"` | ||||||
|  | 	State       string           `json:"state"` | ||||||
|  | 	AlwaysOn    bool             `json:"alwaysOn"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 包含可执行标志 + 链接 | ||||||
|  | type Action struct { | ||||||
|  | 	CanCancel     bool             `json:"canCancel,omitempty"` | ||||||
|  | 	CanReactivate bool             `json:"canReactivate,omitempty"` | ||||||
|  | 	CanReport     bool             `json:"canReport,omitempty"` | ||||||
|  | 	Link          TextVerifiedResp `json:"link"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // reuse 特殊结构 | ||||||
|  | type ReuseInfo struct { | ||||||
|  | 	Link          TextVerifiedResp `json:"link"` | ||||||
|  | 	ReusableUntil *time.Time       `json:"reusableUntil"` // null -> 指针可为空 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type TextVerifiedPriceReq struct { | ||||||
|  | 	ServiceName string `json:"serviceName" comment:"服务code"` | ||||||
|  | 	AreaCode    bool   `json:"areaCode" comment:"限制区域code"` | ||||||
|  | 	NumberType  string `json:"numberType" comment:"mobile | voip | landline"` | ||||||
|  | 	Capability  string `json:"capability" comment:"sms | mms | voice"` | ||||||
|  | 	AlwaysOn    bool   `json:"alwaysOn"` | ||||||
|  | 	IsRenewable bool   `json:"isRenewable" comment:"是否可再生"` | ||||||
|  | 	Duration    string `json:"duration" comment:"oneDay┃threeDay┃sevenDay┃fourteenDay┃thirtyDay┃ninetyDay┃oneYear"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type TextVerifiedPriceResp struct { | ||||||
|  | 	Price       decimal.Decimal `json:"price"` | ||||||
|  | 	ServiceName string          `json:"serviceName"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type TextVerifiedSmsResp struct { | ||||||
|  | 	Data []TextVerifiedSmsItemResp `json:"data"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type TextVerifiedSmsItemResp struct { | ||||||
|  | 	Id         string `json:"id"` | ||||||
|  | 	From       string `json:"from"` | ||||||
|  | 	To         string `json:"to"` | ||||||
|  | 	CreatedAt  string `json:"createdAt"` | ||||||
|  | 	SmsContent string `json:"smsContent"` | ||||||
|  | 	ParsedCode string `json:"parsedCode"` | ||||||
|  | 	Encrypted  bool   `json:"encrypted"` | ||||||
|  | } | ||||||
							
								
								
									
										110
									
								
								app/admin/service/member_api.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								app/admin/service/member_api.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,110 @@ | |||||||
|  | package service | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"go-admin/app/admin/models" | ||||||
|  | 	"go-admin/app/admin/service/dto" | ||||||
|  | 	"go-admin/common/rediskey" | ||||||
|  | 	"go-admin/utils/redishelper" | ||||||
|  | 	"go-admin/utils/utility" | ||||||
|  |  | ||||||
|  | 	"github.com/bytedance/sonic" | ||||||
|  | 	"github.com/go-admin-team/go-admin-core/sdk/service" | ||||||
|  | 	"github.com/jinzhu/copier" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type MemberApi struct { | ||||||
|  | 	service.Service | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e MemberApi) ChangeStatus(id, userId int, status int) error { | ||||||
|  | 	var api models.MemberApi | ||||||
|  |  | ||||||
|  | 	if err := e.Orm.Model(&api).Where("id =? and user_id =?", id, userId).Update("status", status).Error; err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetByUserId 通过用户ID获取用户信息 | ||||||
|  | func (e *MemberApi) GetByUserId(userId int, memberData *dto.MemberApiResp) error { | ||||||
|  | 	var entity models.MemberApi | ||||||
|  |  | ||||||
|  | 	if err := e.Orm.Model(&models.MemberApi{}).Where("user_id = ?", userId).First(&entity).Error; err != nil { | ||||||
|  | 		e.Log.Errorf("通过用户ID获取用户信息失败, %s", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	copier.Copy(memberData, &entity) | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 初始化用户API | ||||||
|  | func (e *MemberApi) InitApis() error { | ||||||
|  | 	var entitys []models.SysUser | ||||||
|  | 	var memberApis []models.MemberApi | ||||||
|  |  | ||||||
|  | 	if err := e.Orm.Model(&models.SysUser{}). | ||||||
|  | 		Joins("LEFT JOIN member_api ON member_api.user_id = sys_user.user_id"). | ||||||
|  | 		Where("member_api.id is null").Find(&entitys).Error; err != nil { | ||||||
|  | 		e.Log.Errorf("初始化 查询用户API失败, %s", err) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var data models.MemberApi | ||||||
|  | 	for _, entity := range entitys { | ||||||
|  | 		e.CreateApi(entity.UserId, &data, 0) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := e.Orm.Model(&models.MemberApi{}).Find(&memberApis).Error; err != nil { | ||||||
|  | 		e.Log.Errorf("初始化 查询用户API失败, %s", err) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, item := range memberApis { | ||||||
|  | 		key := fmt.Sprintf(rediskey.MemberApiKey, item.ApiKey) | ||||||
|  | 		val, err := sonic.MarshalString(item) | ||||||
|  |  | ||||||
|  | 		if err != nil { | ||||||
|  | 			e.Log.Errorf("用户api 初始化失败") | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if err := redishelper.DefaultRedis.SetString(key, val); err != nil { | ||||||
|  | 			e.Log.Errorf("用户api 初始化失败") | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 创建api | ||||||
|  | func (e *MemberApi) CreateApi(userId int, user *models.MemberApi, retryCount int) error { | ||||||
|  | 	key, err := utility.GenerateBase62Key(32) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Log.Errorf("生成API Key失败, %s", err) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	apiEntity := models.MemberApi{ | ||||||
|  | 		UserId: userId, | ||||||
|  | 		ApiKey: key, | ||||||
|  | 		Status: 1, // 默认启用 | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var count int64 | ||||||
|  | 	e.Orm.Model(user).Where("api_key =?", key).Count(&count) | ||||||
|  |  | ||||||
|  | 	if count > 0 && retryCount < 1000 { | ||||||
|  | 		return e.CreateApi(userId, user, retryCount+1) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := e.Orm.Create(&apiEntity).Error; err != nil { | ||||||
|  | 		e.Log.Errorf("初始化用户API失败, %s", err) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
							
								
								
									
										1
									
								
								app/admin/service/open_common.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								app/admin/service/open_common.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | package service | ||||||
							
								
								
									
										355
									
								
								app/admin/service/sms_daisysms.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										355
									
								
								app/admin/service/sms_daisysms.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,355 @@ | |||||||
|  | package service | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"go-admin/app/admin/models" | ||||||
|  | 	"go-admin/app/admin/service/dto" | ||||||
|  | 	"go-admin/common/global" | ||||||
|  | 	"go-admin/common/statuscode" | ||||||
|  | 	"go-admin/config" | ||||||
|  | 	"go-admin/utils/httphelper" | ||||||
|  | 	"net/http" | ||||||
|  | 	"strconv" | ||||||
|  | 	"strings" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/bytedance/sonic" | ||||||
|  | 	"github.com/go-admin-team/go-admin-core/sdk/service" | ||||||
|  | 	"github.com/shopspring/decimal" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type SmsDaisysms struct { | ||||||
|  | 	service.Service | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 同步价格 | ||||||
|  | func (e SmsDaisysms) SyncPrices() { | ||||||
|  | 	prices, err := e.GetPrices() | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Log.Errorf("GetPrices error: %v", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, price := range prices { | ||||||
|  | 		if err := e.Orm.Model(&models.SmsServices{}).Where("code =? and platform_code=?", price.Key, global.SmsPlatformDaisysms).Update("price", price.Cost).Error; err != nil { | ||||||
|  | 			e.Log.Errorf("Update price error: %v", err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetNumberForApi 获取短期或长期租赁号码 | ||||||
|  | // getType 0-短效 1-长效 | ||||||
|  | // service 服务code | ||||||
|  | // maxPrice 最大价格 | ||||||
|  | // period 时长(月) | ||||||
|  | func (e *SmsDaisysms) GetNumberForApi(getType int, serviceCode string, maxPrice decimal.Decimal, period int) (int, string, int) { | ||||||
|  | 	acitvationId := 0 | ||||||
|  | 	result := "" | ||||||
|  | 	resultCode := statuscode.Success | ||||||
|  |  | ||||||
|  | 	configResp, code := GetApiKey(e) | ||||||
|  | 	if code != statuscode.Success { | ||||||
|  | 		return acitvationId, result, code | ||||||
|  | 	} | ||||||
|  | 	url := fmt.Sprintf("?api_key=%s&action=getNumber&service=%s", configResp.ConfigValue, serviceCode) | ||||||
|  |  | ||||||
|  | 	if getType == 1 { | ||||||
|  | 		url = fmt.Sprintf("%s&duration=%dM", url, period) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil) | ||||||
|  | 	//ACCESS_NUMBER:999999:13476711222 | ||||||
|  | 	bytes, _, err := client.GetRaw(url, nil) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Log.Errorf("租赁请求失败 %s", err.Error()) | ||||||
|  |  | ||||||
|  | 		if strings.Contains(err.Error(), "NO_NUMBERS") { | ||||||
|  | 			return acitvationId, result, statuscode.SmsOutOfStockOrUnavailable | ||||||
|  | 		} else { | ||||||
|  | 			return acitvationId, result, statuscode.ServerError | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	content := string(bytes) | ||||||
|  |  | ||||||
|  | 	if strings.Contains(content, "ACCESS_NUMBER:") { | ||||||
|  | 		if len(strings.Split(content, ":")) < 2 { | ||||||
|  | 			e.Log.Errorf("租赁请求失败 %s", content) | ||||||
|  | 			return acitvationId, result, statuscode.ServerError | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		acitvationId, _ = strconv.Atoi(strings.Split(content, ":")[1]) | ||||||
|  | 		result = strings.Split(content, ":")[2] | ||||||
|  | 	} else if strings.Contains(content, "MAX_PRICE_EXCEEDED:") { | ||||||
|  | 		e.Log.Errorf("租赁价格超过最大价格") | ||||||
|  | 		resultCode = statuscode.NoNumbers | ||||||
|  | 	} else if strings.Contains(content, "NO_NUMBERS") { | ||||||
|  | 		e.Log.Error("平台号码不足") | ||||||
|  | 		resultCode = statuscode.NoNumbers | ||||||
|  | 	} else if strings.Contains(content, "TOO_MANY_ACTIVE_RENTALS") { | ||||||
|  | 		e.Log.Error("租赁数量超过限制") | ||||||
|  | 		resultCode = statuscode.ServerError | ||||||
|  | 	} else if strings.Contains(content, "NO_MONEY") { | ||||||
|  | 		e.Log.Error("余额不足") | ||||||
|  | 		resultCode = statuscode.BalanceNotEnough | ||||||
|  | 	} else { | ||||||
|  | 		e.Log.Errorf("租赁请求失败 %s", content) | ||||||
|  | 		resultCode = statuscode.ServerError | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return acitvationId, result, resultCode | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 设置租赁结束 | ||||||
|  | func (e *SmsDaisysms) setStatus(id int) int { | ||||||
|  | 	configResp, code := GetApiKey(e) | ||||||
|  | 	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) | ||||||
|  | 	bytes, _, err := client.GetRaw(url, nil) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Log.Errorf("租赁请求失败 %s", err.Error()) | ||||||
|  | 		return statuscode.ServerError | ||||||
|  | 	} | ||||||
|  | 	content := string(bytes) | ||||||
|  |  | ||||||
|  | 	if content == "ACCESS_ACTIVATION" { | ||||||
|  | 		return statuscode.Success | ||||||
|  | 	} else { | ||||||
|  | 		e.Log.Errorf("租赁请求失败 %s", content) | ||||||
|  | 		return statuscode.ServerError | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | 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 获取验证码 | ||||||
|  | // messageId 短信id | ||||||
|  | // return 验证码, 状态码 | ||||||
|  | func (e *SmsDaisysms) GetCodeForApi(messageId string) (string, int) { | ||||||
|  | 	result := "" | ||||||
|  | 	key, code := GetApiKey(e) | ||||||
|  |  | ||||||
|  | 	if code != statuscode.Success { | ||||||
|  | 		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) | ||||||
|  | 	bytes, _, err := client.GetRaw(url, nil) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Log.Errorf("租赁请求失败 %s", err.Error()) | ||||||
|  | 		return result, statuscode.ServerError | ||||||
|  | 	} | ||||||
|  | 	content := string(bytes) | ||||||
|  |  | ||||||
|  | 	if strings.Contains(content, "STATUS_OK:") && content != "STATUS_OK:KEEP" { | ||||||
|  | 		result = strings.Split(content, ":")[1] | ||||||
|  | 	} else if content == "NO_ACTIVATION" { | ||||||
|  | 		code = statuscode.SmsNoActivation | ||||||
|  | 	} else if content == "STATUS_WAIT_CODE" || content == "STATUS_OK:KEEP" { | ||||||
|  | 		code = statuscode.SmsWaitCode | ||||||
|  | 	} else if content == "STATUS_CANCEL" { | ||||||
|  | 		code = statuscode.SmsCancel | ||||||
|  | 	} else { | ||||||
|  | 		e.Log.Errorf("租赁请求失败 %s", content) | ||||||
|  | 		return result, statuscode.ServerError | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return result, code | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // getExtraActivation 获取额外的激活 | ||||||
|  | // messageId 短信id | ||||||
|  | // return 验证码, 状态码 | ||||||
|  | func (e *SmsDaisysms) getExtraActivation(activationId string) (int, int) { | ||||||
|  | 	result := 0 | ||||||
|  | 	key, err := GetApiKey(e) | ||||||
|  | 	if err != statuscode.Success { | ||||||
|  | 		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) | ||||||
|  | 	bytes, _, err1 := client.GetRaw(url, nil) | ||||||
|  |  | ||||||
|  | 	if err1 != nil { | ||||||
|  | 		e.Log.Errorf("租赁请求失败 %s", err1.Error()) | ||||||
|  | 		return 0, statuscode.ServerError | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	content := string(bytes) | ||||||
|  |  | ||||||
|  | 	if strings.Contains(content, "ASLEEP:") { | ||||||
|  | 		message := strings.Split(content, ":")[1] | ||||||
|  | 		result, _ = strconv.Atoi(message) | ||||||
|  |  | ||||||
|  | 		if result > 0 { | ||||||
|  | 			return result, statuscode.Success | ||||||
|  | 		} | ||||||
|  | 	} else if strings.Contains(content, "STATUS_OK:") { | ||||||
|  | 		return 0, statuscode.SmsLongNumWaitCode | ||||||
|  | 	} else if strings.Contains(content, "ACCESS_NUMBER:") { | ||||||
|  | 		message := strings.Split(content, ":")[1] | ||||||
|  | 		result, _ = strconv.Atoi(message) | ||||||
|  |  | ||||||
|  | 		if result > 0 { | ||||||
|  | 			return result, statuscode.Success | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	e.Log.Errorf("激活长效号码失败,%s", content) | ||||||
|  |  | ||||||
|  | 	return 0, statuscode.ServerError | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // KeepLongTerm 长期租赁 | ||||||
|  | func (e *SmsDaisysms) KeepLongTerm(activationId string) int { | ||||||
|  | 	key, code := GetApiKey(e) | ||||||
|  |  | ||||||
|  | 	if code != statuscode.Success { | ||||||
|  | 		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) | ||||||
|  | 	bytes, _, err := client.GetRaw(url, nil) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Log.Errorf("租赁请求失败 %s", err.Error()) | ||||||
|  | 		return statuscode.ServerError | ||||||
|  | 	} | ||||||
|  | 	content := string(bytes) | ||||||
|  |  | ||||||
|  | 	if content == "OK" { | ||||||
|  | 		return statuscode.Success | ||||||
|  | 	} else { | ||||||
|  | 		e.Log.Errorf("租赁请求失败 %s", content) | ||||||
|  | 		return statuscode.ServerError | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 取消租赁 | ||||||
|  | func (e *SmsDaisysms) CancelRental(activationId string) int { | ||||||
|  | 	key, code := GetApiKey(e) | ||||||
|  |  | ||||||
|  | 	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) | ||||||
|  | 	bytes, _, err := client.GetRaw(url, nil) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Log.Errorf("租赁请求失败 %s", err.Error()) | ||||||
|  | 		return statuscode.ServerError | ||||||
|  | 	} | ||||||
|  | 	content := string(bytes) | ||||||
|  |  | ||||||
|  | 	if content == "ACCESS_CANCEL" { | ||||||
|  | 		return statuscode.Success | ||||||
|  | 	} else { | ||||||
|  | 		e.Log.Errorf("租赁请求失败 %s", content) | ||||||
|  | 		return statuscode.ServerError | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 获取价格 | ||||||
|  | func (e *SmsDaisysms) GetPrices() ([]dto.DaisysmsPriceResp, error) { | ||||||
|  | 	result := make([]dto.DaisysmsPriceResp, 0) | ||||||
|  | 	key, code := GetApiKey(e) | ||||||
|  |  | ||||||
|  | 	if code != statuscode.Success { | ||||||
|  | 		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) | ||||||
|  | 	bytes, status, err := client.GetRaw(url, nil) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Log.Errorf("租赁请求失败 %s", err.Error()) | ||||||
|  | 		return result, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	mapData := make(map[string]map[string]dto.DaisysmsPriceResp) | ||||||
|  | 	err = sonic.Unmarshal(bytes, &mapData) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Log.Errorf("解析失败 %s", err.Error()) | ||||||
|  | 		return result, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	rawData, ok := mapData["187"] | ||||||
|  |  | ||||||
|  | 	if !ok { | ||||||
|  | 		e.Log.Errorf("获取 187数据失败") | ||||||
|  |  | ||||||
|  | 		return result, errors.New("获取 187数据失败") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if status != http.StatusOK { | ||||||
|  | 		return result, fmt.Errorf("获取价格失败 %d", status) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for key, v := range rawData { | ||||||
|  | 		v.Key = key | ||||||
|  |  | ||||||
|  | 		result = append(result, v) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return result, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ChangeAutoRenew 修改自动续期 | ||||||
|  | // activationId 短信id | ||||||
|  | // status 状态 | ||||||
|  | func (e *SmsDaisysms) ChangeAutoRenewForApi(activationId string, status bool) int { | ||||||
|  | 	key, err := GetApiKey(e) | ||||||
|  |  | ||||||
|  | 	if err != statuscode.Success { | ||||||
|  | 		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) | ||||||
|  | 	bytes, _, err1 := client.GetRaw(url, nil) | ||||||
|  |  | ||||||
|  | 	if err1 != nil { | ||||||
|  | 		e.Log.Errorf("租赁请求失败 %s", err1.Error()) | ||||||
|  | 		return statuscode.ServerError | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	content := string(bytes) | ||||||
|  |  | ||||||
|  | 	if content == "OK" { | ||||||
|  | 		return statuscode.Success | ||||||
|  | 	} else { | ||||||
|  | 		e.Log.Errorf("修改自动续期请求失败 id:%d, status:%t, %s", activationId, status, content) | ||||||
|  | 		return statuscode.ServerError | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -2,9 +2,8 @@ package service | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"fmt" | 	"net/http" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"strings" |  | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/go-admin-team/go-admin-core/sdk/service" | 	"github.com/go-admin-team/go-admin-core/sdk/service" | ||||||
| @ -17,28 +16,55 @@ 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/config" |  | ||||||
| 	"go-admin/utils/httphelper" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type SmsPhone struct { | type SmsPhone struct { | ||||||
| 	service.Service | 	service.Service | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // open-API 获取电话号码 | ||||||
|  | func (e SmsPhone) OpenGetNumber(req *dto.GetNumberReq, userId int) (dto.OpenGetNumberResp, int) { | ||||||
|  | 	resp := dto.OpenGetNumberResp{} | ||||||
|  | 	balanceService := MemberBalance{Service: e.Service} | ||||||
|  | 	balance, smsPhone, i := e.DoGetNumber(&balanceService, req, userId) | ||||||
|  |  | ||||||
|  | 	resp.Blance = balance | ||||||
|  | 	if i != statuscode.Success { | ||||||
|  | 		return resp, i | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	resp.ActivationId = smsPhone.NewActivationId | ||||||
|  | 	resp.ExpireTime = smsPhone.ExpireTime | ||||||
|  | 	resp.Number = smsPhone.Phone | ||||||
|  |  | ||||||
|  | 	return resp, statuscode.Success | ||||||
|  | } | ||||||
|  |  | ||||||
| // 取消等待 | // 取消等待 | ||||||
| func (e *SmsPhone) CancelNumber(req *dto.SmsPhoneCancelNumberReq, userId int) int { | func (e *SmsPhone) CancelNumber(req *dto.SmsPhoneCancelNumberReq, userId int) int { | ||||||
| 	var data models.SmsPhone | 	var data models.SmsPhone | ||||||
| 	if err := e.Orm.Model(data).Where("id =? and user_id= ?", req.Id, userId).First(&data).Error; err != nil { |  | ||||||
| 		e.Log.Errorf("获取短信号码失败, %s", err) | 	if req.Id > 0 { | ||||||
| 		return statuscode.SmsNotExisted | 		if err := e.Orm.Model(data).Where("id =? and user_id= ?", req.Id, userId).First(&data).Error; err != nil { | ||||||
|  | 			e.Log.Errorf("获取短信号码失败, %s", err) | ||||||
|  | 			return statuscode.SmsNotExisted | ||||||
|  | 		} | ||||||
|  | 	} else if req.ActivationId != "" { | ||||||
|  | 		if err := e.Orm.Model(data).Where("new_activation_id =? and user_id= ?", req.ActivationId, userId).First(&data).Error; err != nil { | ||||||
|  | 			e.Log.Errorf("获取短信号码失败, %s", err) | ||||||
|  | 			return statuscode.SmsNotExisted | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		e.Log.Errorf("id和activationId 都是空") | ||||||
|  | 		return statuscode.ServerError | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if data.Actived == 1 { | 	if data.Actived == 1 { | ||||||
| 		code := e.CancelRental(data.NewActivationId) | 		code := e.CancelNumberManage(&data) | ||||||
|  |  | ||||||
| 		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"}).Error; err1 != nil { | 				if err1 := e.Orm.Model(data).Updates(map[string]interface{}{"status": 3, "code": "", "actived": "3", "auto_renewal": 2}).Error; err1 != nil { | ||||||
| 					e.Log.Errorf("更新短信号码失败, %s", err1) | 					e.Log.Errorf("更新短信号码失败, %s", err1) | ||||||
| 					return err1 | 					return err1 | ||||||
| 				} | 				} | ||||||
| @ -63,6 +89,23 @@ func (e *SmsPhone) CancelNumber(req *dto.SmsPhoneCancelNumberReq, userId int) in | |||||||
| 	return statuscode.Success | 	return statuscode.Success | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // 聚合取消 | ||||||
|  | func (e *SmsPhone) CancelNumberManage(data *models.SmsPhone) int { | ||||||
|  | 	switch data.PlatformCode { | ||||||
|  | 	case global.SmsPlatformDaisysms: | ||||||
|  | 		service := SmsDaisysms{Service: e.Service} | ||||||
|  |  | ||||||
|  | 		return service.CancelRental(data.NewActivationId) | ||||||
|  | 	case global.SmsPlatformTextVerified: | ||||||
|  | 		service := SmsTextVerified{Service: e.Service} | ||||||
|  |  | ||||||
|  | 		return service.CancelRental(data.NewActivationId, data.Type) | ||||||
|  | 	default: | ||||||
|  | 		return statuscode.SmsPlatformUnavailable | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
| // 删除断信号码 | // 删除断信号码 | ||||||
| func (e *SmsPhone) DeleteMyNumber(req *dto.DeleteMyNumberReq, userId int) int { | func (e *SmsPhone) DeleteMyNumber(req *dto.DeleteMyNumberReq, userId int) int { | ||||||
| 	var data models.SmsPhone | 	var data models.SmsPhone | ||||||
| @ -91,35 +134,56 @@ func (e *SmsPhone) DeleteMyNumber(req *dto.DeleteMyNumberReq, userId int) int { | |||||||
| } | } | ||||||
|  |  | ||||||
| // 唤醒长效号码 | // 唤醒长效号码 | ||||||
| func (e *SmsPhone) WeakUp(req *dto.WeakUpReq, userId int) int { | func (e *SmsPhone) WeakUp(req *dto.WeakUpReq, userId int, defult bool) (dto.WeakUpResp, int) { | ||||||
| 	smsPhone := models.SmsPhone{} | 	smsPhone := models.SmsPhone{} | ||||||
|  | 	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 statuscode.ServerError | 		return result, statuscode.ServerError | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if smsPhone.Status != 1 { | 	if smsPhone.Status != 1 || defult { | ||||||
| 		newActivationId, code := e.getExtraActivation(smsPhone.ActivationId) | 		newActivationId, messageId, code := e.WeakUpManage(&smsPhone) | ||||||
|  |  | ||||||
| 		if code == statuscode.ServerError { | 		if code == statuscode.ServerError { | ||||||
| 			return 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": ""}).Error; err != nil { | 			if err := e.Orm.Model(smsPhone).Updates(map[string]interface{}{"new_activation_id": newActivationId, "status": 1, "code": "", "message_id": messageId}).Error; err != nil { | ||||||
| 				e.Log.Errorf("更新短信号码失败, %s", err) | 				e.Log.Errorf("更新短信号码失败, %s", err) | ||||||
| 				return statuscode.ServerError | 				return result, statuscode.ServerError | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|  | 			result.ActivationId = newActivationId | ||||||
|  | 			result.MessageId = messageId | ||||||
| 		} else if code == statuscode.SmsLongNumWaitCode { | 		} else if code == statuscode.SmsLongNumWaitCode { | ||||||
| 			e.Log.Info("无须唤醒") | 			e.Log.Info("无须唤醒") | ||||||
| 			if err := e.Orm.Model(smsPhone).Updates(map[string]interface{}{"status": 1, "code": ""}).Error; err != nil { | 			if err := e.Orm.Model(smsPhone).Updates(map[string]interface{}{"status": 1, "code": ""}).Error; err != nil { | ||||||
| 				e.Log.Errorf("更新短信号码失败, %s", err) | 				e.Log.Errorf("更新短信号码失败, %s", err) | ||||||
| 				return statuscode.ServerError | 				return result, statuscode.ServerError | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			return statuscode.Success | 			return result, statuscode.Success | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return statuscode.Success | 	return result, statuscode.Success | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 唤醒号码 | ||||||
|  | // returns newActivationId,messageId, code | ||||||
|  | func (e *SmsPhone) WeakUpManage(req *models.SmsPhone) (string, string, int) { | ||||||
|  | 	switch req.PlatformCode { | ||||||
|  | 	case global.SmsPlatformDaisysms: | ||||||
|  | 		service := SmsDaisysms{Service: e.Service} | ||||||
|  | 		id, code := service.getExtraActivation(req.ActivationId) | ||||||
|  | 		return strconv.Itoa(id), "", code | ||||||
|  | 	case global.SmsPlatformTextVerified: | ||||||
|  | 		service := SmsTextVerified{Service: e.Service} | ||||||
|  |  | ||||||
|  | 		return service.getExtraActivation(req.NewActivationId) | ||||||
|  | 	default: | ||||||
|  | 		return "", "", statuscode.SmsPlatformUnavailable | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // 分页查询自己的号码列表 | // 分页查询自己的号码列表 | ||||||
| @ -142,64 +206,89 @@ func (e *SmsPhone) GetMyPage(req *dto.SmsPhoneGetPageReq, userId int, phone *[]m | |||||||
|  |  | ||||||
| // 租赁号码 | // 租赁号码 | ||||||
| func (e *SmsPhone) GetNumber(req *dto.GetNumberReq, userId int) (decimal.Decimal, int) { | func (e *SmsPhone) GetNumber(req *dto.GetNumberReq, userId int) (decimal.Decimal, int) { | ||||||
|  | 	balanceService := MemberBalance{Service: e.Service} | ||||||
|  | 	balance, _, i := e.DoGetNumber(&balanceService, req, userId) | ||||||
|  | 	if i != statuscode.Success { | ||||||
|  | 		return balance, i | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	balance = balanceService.GetBalance(userId) | ||||||
|  |  | ||||||
|  | 	return balance, statuscode.Success | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *SmsPhone) DoGetNumber(balanceService *MemberBalance, req *dto.GetNumberReq, userId int) (decimal.Decimal, models.SmsPhone, int) { | ||||||
| 	config := dto.GetSysConfigByKEYForServiceResp{} | 	config := dto.GetSysConfigByKEYForServiceResp{} | ||||||
| 	configReq := dto.SysConfigByKeyReq{} | 	configReq := dto.SysConfigByKeyReq{} | ||||||
| 	configService := SysConfig{Service: e.Service} | 	configService := SysConfig{Service: e.Service} | ||||||
|  | 	smsServices := SmsServices{Service: e.Service} | ||||||
| 	if strings.ToLower(req.ServiceCode) != "wa" { | 	var price decimal.Decimal | ||||||
| 		return decimal.Zero, statuscode.SmsServiceUnavailable | 	serviceItem, err := smsServices.GetByCode(req.PlatformCode, req.ServiceCode) | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if req.Type == 0 { |  | ||||||
| 		configReq.ConfigKey = "number_fee_short_term" |  | ||||||
| 	} else { |  | ||||||
| 		configReq.ConfigKey = "number_fee_long_term" |  | ||||||
| 	} |  | ||||||
| 	err := configService.GetWithKey(&configReq, &config) |  | ||||||
|  |  | ||||||
| 	if err != nil { |  | ||||||
| 		return decimal.Zero, statuscode.ServerError |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if config.ConfigValue == "" { |  | ||||||
| 		e.Log.Errorf("短期或长期租赁费用不能为空") |  | ||||||
| 		return decimal.Zero, statuscode.ServerError |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	price, err := decimal.NewFromString(config.ConfigValue) |  | ||||||
| 	if err != nil { |  | ||||||
| 		e.Log.Errorf("短期或长期租赁费用格式错误") |  | ||||||
| 		return decimal.Zero, statuscode.ServerError |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	smsService := SmsServices{Service: e.Service} |  | ||||||
| 	balanceService := MemberBalance{Service: e.Service} |  | ||||||
| 	balance := balanceService.GetBalance(userId) |  | ||||||
| 	serviceItem, err := smsService.GetByCode(req.ServiceCode) |  | ||||||
|  |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		e.Log.Errorf("短信服务报错:%v", err) | 		e.Log.Errorf("短信服务报错:%v", err) | ||||||
| 		return decimal.Zero, statuscode.ServerError | 		return decimal.Decimal{}, models.SmsPhone{}, statuscode.ServerError | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if serviceItem.Code == "" { | 	if serviceItem.Code == "" { | ||||||
| 		e.Log.Error("短信服务不存在") | 		e.Log.Error("短信服务不存在") | ||||||
| 		return decimal.Zero, statuscode.ServerError | 		return decimal.Decimal{}, models.SmsPhone{}, statuscode.ServerError | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	switch { | ||||||
|  | 	case req.Type == 0 && req.PlatformCode == global.SmsPlatformDaisysms: | ||||||
|  | 		price = serviceItem.Price | ||||||
|  | 		configReq.ConfigKey = "number_premium_daisysms" | ||||||
|  | 	case req.Type == 0 && req.PlatformCode == global.SmsPlatformTextVerified: | ||||||
|  | 		price = serviceItem.Price | ||||||
|  | 		configReq.ConfigKey = "number_premium_textverified" | ||||||
|  | 	case req.Type == 1 && req.PlatformCode == global.SmsPlatformDaisysms: | ||||||
|  | 		price = serviceItem.LongPrice | ||||||
|  | 		configReq.ConfigKey = "long_number_premium_daisysms" | ||||||
|  | 	case req.Type == 1 && req.PlatformCode == global.SmsPlatformTextVerified: | ||||||
|  | 		price = serviceItem.LongPrice | ||||||
|  | 		configReq.ConfigKey = "long_number_premium_textverified" | ||||||
|  | 	} | ||||||
|  | 	err = configService.GetWithKey(&configReq, &config) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		return decimal.Decimal{}, models.SmsPhone{}, statuscode.ServerError | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if config.ConfigValue == "" { | ||||||
|  | 		e.Log.Errorf("短期长期租赁浮动百分比不能为空") | ||||||
|  | 		return decimal.Decimal{}, models.SmsPhone{}, statuscode.ServerError | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	percent, err := decimal.NewFromString(config.ConfigValue) | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Log.Errorf("短期或长期租赁费用格式错误") | ||||||
|  | 		return decimal.Decimal{}, models.SmsPhone{}, 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 decimal.Decimal{}, models.SmsPhone{}, statuscode.ServerError | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	balance := balanceService.GetBalance(userId) | ||||||
|  |  | ||||||
| 	if balance.LessThan(price) { | 	if balance.LessThan(price) { | ||||||
| 		e.Log.Error("余额不足") | 		e.Log.Error("余额不足") | ||||||
| 		return decimal.Zero, statuscode.BalanceNotEnough | 		return decimal.Decimal{}, models.SmsPhone{}, statuscode.BalanceNotEnough | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	now := time.Now() | 	now := time.Now() | ||||||
| 	activationId, phone, code := e.GetNumberForApi(req.Type, req.ServiceCode, price, req.Period) | 	activationId, phone, code, expireTime := e.GetNumberManage(req.PlatformCode, req.Type, req.ServiceCode, price, req.Period) | ||||||
|  |  | ||||||
| 	if code != statuscode.Success { | 	if code != statuscode.Success { | ||||||
| 		return decimal.Zero, code | 		return decimal.Decimal{}, models.SmsPhone{}, code | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	smsPhone := models.SmsPhone{} | 	smsPhone := models.SmsPhone{} | ||||||
|  | 	smsPhone.PlatformCode = req.PlatformCode | ||||||
| 	smsPhone.Phone = phone | 	smsPhone.Phone = phone | ||||||
| 	smsPhone.UserId = userId | 	smsPhone.UserId = userId | ||||||
| 	smsPhone.Service = serviceItem.Name | 	smsPhone.Service = serviceItem.Name | ||||||
| @ -213,30 +302,45 @@ func (e *SmsPhone) GetNumber(req *dto.GetNumberReq, userId int) (decimal.Decimal | |||||||
|  |  | ||||||
| 	smsPhone.Price = price | 	smsPhone.Price = price | ||||||
| 	if req.Type == 1 { | 	if req.Type == 1 { | ||||||
| 		days := req.Period * 30 | 		if expireTime != nil { | ||||||
| 		now = now.AddDate(0, 0, days) | 			smsPhone.ExpireTime = expireTime | ||||||
| 		smsPhone.ExpireTime = &now | 		} else { | ||||||
|  | 			days := req.Period * 30 | ||||||
|  | 			now = now.AddDate(0, 0, days) | ||||||
|  | 			smsPhone.ExpireTime = &now | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 	} else { | 	} else { | ||||||
| 		now = now.Add(time.Minute * time.Duration(serviceItem.ExpirationMinutes)) | 		if expireTime != nil { | ||||||
| 		smsPhone.ExpireTime = &now | 			smsPhone.ExpireTime = expireTime | ||||||
| 		smsPhone.AutoRenewal = 2 | 		} else { | ||||||
|  | 			now = now.Add(time.Minute * time.Duration(serviceItem.ExpirationMinutes)) | ||||||
|  | 			smsPhone.ExpireTime = &now | ||||||
|  | 			smsPhone.AutoRenewal = 2 | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	smsPhone.Status = 1 | 	smsPhone.Status = 1 | ||||||
|  |  | ||||||
| 	if err := e.Orm.Save(&smsPhone).Error; err != nil { | 	if err := e.Orm.Save(&smsPhone).Error; err != nil { | ||||||
| 		e.Log.Errorf("获取手机号失败", err) | 		e.Log.Errorf("获取手机号失败", err) | ||||||
| 		return decimal.Zero, statuscode.ServerError | 		return decimal.Decimal{}, models.SmsPhone{}, statuscode.ServerError | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if err := e.Orm.Exec("UPDATE member_balance SET balance = balance -? WHERE user_id =?", price, userId).Error; err != nil { | 	if err := e.Orm.Exec("UPDATE member_balance SET balance = balance -? WHERE user_id =?", price, userId).Error; err != nil { | ||||||
| 		e.Log.Errorf("更新余额失败", err) | 		e.Log.Errorf("更新余额失败", err) | ||||||
| 		return decimal.Zero, statuscode.ServerError | 		return decimal.Decimal{}, models.SmsPhone{}, statuscode.ServerError | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	//长效号码默认开启续费 | 	//长效号码默认开启续费 | ||||||
| 	if req.Type == 1 { | 	if req.Type == 1 { | ||||||
| 		code := e.ChangeAutoRenewForApi(smsPhone.NewActivationId, true) | 		var code int | ||||||
|  | 		if req.PlatformCode == global.SmsPlatformDaisysms { | ||||||
|  | 			code = e.ChangeAutoRenewManage(smsPhone.PlatformCode, smsPhone.NewActivationId, true) | ||||||
|  | 		} else { | ||||||
|  | 			code = http.StatusOK | ||||||
|  | 			e.WeakUp(&dto.WeakUpReq{ActivationId: smsPhone.NewActivationId}, userId, true) | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		if code == statuscode.Success { | 		if code == statuscode.Success { | ||||||
| 			smsPhone.AutoRenewal = 1 | 			smsPhone.AutoRenewal = 1 | ||||||
| @ -247,116 +351,36 @@ func (e *SmsPhone) GetNumber(req *dto.GetNumberReq, userId int) (decimal.Decimal | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	balance = balanceService.GetBalance(userId) | 	return balance, smsPhone, statuscode.Success | ||||||
|  |  | ||||||
| 	return balance, statuscode.Success |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // GetNumberForApi 获取短期或长期租赁号码 | // 租赁号码 | ||||||
| // getType 0-短效 1-长效 | func (e *SmsPhone) GetNumberManage(platformCode string, typ int, serviceCode string, price decimal.Decimal, period int) (string, string, int, *time.Time) { | ||||||
| // service 服务code | 	switch platformCode { | ||||||
| // maxPrice 最大价格 | 	case global.SmsPlatformDaisysms: | ||||||
| // period 时长(月) | 		service := SmsDaisysms{Service: e.Service} | ||||||
| func (e *SmsPhone) GetNumberForApi(getType int, serviceCode string, maxPrice decimal.Decimal, period int) (int, string, int) { | 		activationId, phone, code := service.GetNumberForApi(typ, serviceCode, price, period) | ||||||
| 	acitvationId := 0 |  | ||||||
| 	result := "" |  | ||||||
| 	resultCode := statuscode.Success |  | ||||||
|  |  | ||||||
| 	configResp, code := GetApiKey(e) | 		return strconv.Itoa(activationId), phone, code, nil | ||||||
| 	if code != statuscode.Success { | 	case global.SmsPlatformTextVerified: | ||||||
| 		return acitvationId, result, code | 		service := SmsTextVerified{Service: e.Service} | ||||||
| 	} | 		resp, code := service.GetNumberForApi(typ, serviceCode, price, period) | ||||||
| 	url := fmt.Sprintf("?api_key=%s&action=getNumber&service=%s", configResp.ConfigValue, serviceCode) |  | ||||||
|  |  | ||||||
| 	if getType == 1 { | 		if code != statuscode.Success { | ||||||
| 		url = fmt.Sprintf("%s&duration=%dM", url, period) | 			return "", "", code, nil | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil) |  | ||||||
| 	//ACCESS_NUMBER:999999:13476711222 |  | ||||||
| 	bytes, err := client.GetRaw(url, nil) |  | ||||||
|  |  | ||||||
| 	if err != nil { |  | ||||||
| 		e.Log.Errorf("租赁请求失败 %s", err.Error()) |  | ||||||
| 		return acitvationId, result, statuscode.ServerError |  | ||||||
| 	} |  | ||||||
| 	content := string(bytes) |  | ||||||
|  |  | ||||||
| 	if strings.Contains(content, "ACCESS_NUMBER:") { |  | ||||||
| 		if len(strings.Split(content, ":")) < 2 { |  | ||||||
| 			e.Log.Errorf("租赁请求失败 %s", content) |  | ||||||
| 			return acitvationId, result, statuscode.ServerError |  | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		acitvationId, _ = strconv.Atoi(strings.Split(content, ":")[1]) | 		return resp.Id, resp.Phone, code, resp.EndAt | ||||||
| 		result = strings.Split(content, ":")[2] | 	default: | ||||||
| 	} else if strings.Contains(content, "MAX_PRICE_EXCEEDED:") { | 		return "", "", statuscode.SmsPlatformUnavailable, nil | ||||||
| 		e.Log.Errorf("租赁价格超过最大价格") |  | ||||||
| 		resultCode = statuscode.NoNumbers |  | ||||||
| 	} else if strings.Contains(content, "NO_NUMBERS") { |  | ||||||
| 		e.Log.Error("平台号码不足") |  | ||||||
| 		resultCode = statuscode.NoNumbers |  | ||||||
| 	} else if strings.Contains(content, "TOO_MANY_ACTIVE_RENTALS") { |  | ||||||
| 		e.Log.Error("租赁数量超过限制") |  | ||||||
| 		resultCode = statuscode.ServerError |  | ||||||
| 	} else if strings.Contains(content, "NO_MONEY") { |  | ||||||
| 		e.Log.Error("余额不足") |  | ||||||
| 		resultCode = statuscode.BalanceNotEnough |  | ||||||
| 	} else { |  | ||||||
| 		e.Log.Errorf("租赁请求失败 %s", content) |  | ||||||
| 		resultCode = statuscode.ServerError |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return acitvationId, result, resultCode |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // 设置租赁结束 |  | ||||||
| func (e *SmsPhone) setStatus(id int) int { |  | ||||||
| 	configResp, code := GetApiKey(e) |  | ||||||
| 	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) |  | ||||||
| 	bytes, err := client.GetRaw(url, nil) |  | ||||||
|  |  | ||||||
| 	if err != nil { |  | ||||||
| 		e.Log.Errorf("租赁请求失败 %s", err.Error()) |  | ||||||
| 		return statuscode.ServerError |  | ||||||
| 	} |  | ||||||
| 	content := string(bytes) |  | ||||||
|  |  | ||||||
| 	if content == "ACCESS_ACTIVATION" { |  | ||||||
| 		return statuscode.Success |  | ||||||
| 	} else { |  | ||||||
| 		e.Log.Errorf("租赁请求失败 %s", content) |  | ||||||
| 		return statuscode.ServerError |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func GetApiKey(e *SmsPhone) (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 |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (e *SmsPhone) GetCodeByActivationId(req *dto.GetCodeReq, userId int) ([]dto.GetCodeResp, int) { | func (e *SmsPhone) GetCodeByActivationId(req *dto.GetCodeReq, userId int) ([]dto.GetCodeResp, int) { | ||||||
| 	var smsPhone []models.SmsPhone | 	var smsPhone []models.SmsPhone | ||||||
| 	result := []dto.GetCodeResp{} | 	result := []dto.GetCodeResp{} | ||||||
|  |  | ||||||
| 	if err := e.Orm.Model(models.SmsPhone{}).Where("user_id =? and activation_id in ?", userId, req.ActivationIds).Find(&smsPhone).Error; err != nil { | 	if err := e.Orm.Model(models.SmsPhone{}).Where("user_id =? and new_activation_id in ?", userId, req.ActivationIds).Find(&smsPhone).Error; err != nil { | ||||||
| 		return result, statuscode.ServerError | 		return result, statuscode.ServerError | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @ -392,7 +416,18 @@ func (e *SmsPhone) SyncCodes() error { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for _, item := range phones { | 	for _, item := range phones { | ||||||
| 		code, codeStatus := e.GetCodeForApi(item.NewActivationId) | 		code, codeStatus := e.GetCodeManage(item.PlatformCode, item.NewActivationId, item.Type, item.UpdatedAt.Unix()) | ||||||
|  |  | ||||||
|  | 		if code == "" { | ||||||
|  | 			expireTime := item.UpdatedAt.Add(time.Second * 150) | ||||||
|  | 			if 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 { | ||||||
|  | 					e.Log.Errorf("手机号一睡眠 %s", item.Phone) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
| 		mapData := make(map[string]interface{}) | 		mapData := make(map[string]interface{}) | ||||||
| 		logMapData := make(map[string]interface{}) | 		logMapData := make(map[string]interface{}) | ||||||
|  |  | ||||||
| @ -437,135 +472,18 @@ func (e *SmsPhone) SyncCodes() error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // GetCodeForApi 获取验证码 | // 获取验证码 | ||||||
| // messageId 短信id | func (e *SmsPhone) GetCodeManage(platformCode string, messageId string, typ int, unixTime int64) (string, int) { | ||||||
| // return 验证码, 状态码 | 	switch platformCode { | ||||||
| func (e *SmsPhone) GetCodeForApi(messageId int) (string, int) { | 	case global.SmsPlatformDaisysms: | ||||||
| 	result := "" | 		service := SmsDaisysms{Service: e.Service} | ||||||
| 	key, code := GetApiKey(e) |  | ||||||
|  |  | ||||||
| 	if code != statuscode.Success { | 		return service.GetCodeForApi(messageId) | ||||||
| 		return result, code | 	case global.SmsPlatformTextVerified: | ||||||
| 	} | 		service := SmsTextVerified{Service: e.Service} | ||||||
| 	url := fmt.Sprintf("?api_key=%s&action=getStatus&id=%d", key.ConfigValue, messageId) | 		return service.GetCode(messageId, typ, unixTime) | ||||||
| 	client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil) | 	default: | ||||||
| 	bytes, err := client.GetRaw(url, nil) | 		return "", statuscode.SmsPlatformUnavailable | ||||||
|  |  | ||||||
| 	if err != nil { |  | ||||||
| 		e.Log.Errorf("租赁请求失败 %s", err.Error()) |  | ||||||
| 		return result, statuscode.ServerError |  | ||||||
| 	} |  | ||||||
| 	content := string(bytes) |  | ||||||
|  |  | ||||||
| 	if strings.Contains(content, "STATUS_OK:") && content != "STATUS_OK:KEEP" { |  | ||||||
| 		result = strings.Split(content, ":")[1] |  | ||||||
| 	} else if content == "NO_ACTIVATION" { |  | ||||||
| 		code = statuscode.SmsNoActivation |  | ||||||
| 	} else if content == "STATUS_WAIT_CODE" || content == "STATUS_OK:KEEP" { |  | ||||||
| 		code = statuscode.SmsWaitCode |  | ||||||
| 	} else if content == "STATUS_CANCEL" { |  | ||||||
| 		code = statuscode.SmsCancel |  | ||||||
| 	} else { |  | ||||||
| 		e.Log.Errorf("租赁请求失败 %s", content) |  | ||||||
| 		return result, statuscode.ServerError |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return result, code |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // getExtraActivation 获取额外的激活 |  | ||||||
| // messageId 短信id |  | ||||||
| // return 验证码, 状态码 |  | ||||||
| func (e *SmsPhone) getExtraActivation(activationId int) (int, int) { |  | ||||||
| 	result := 0 |  | ||||||
| 	key, err := GetApiKey(e) |  | ||||||
| 	if err != statuscode.Success { |  | ||||||
| 		return 0, statuscode.ServerError |  | ||||||
| 	} |  | ||||||
| 	url := fmt.Sprintf("?api_key=%s&action=getExtraActivation&activationId=%d", key.ConfigValue, activationId) |  | ||||||
| 	client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil) |  | ||||||
| 	bytes, err1 := client.GetRaw(url, nil) |  | ||||||
|  |  | ||||||
| 	if err1 != nil { |  | ||||||
| 		e.Log.Errorf("租赁请求失败 %s", err1.Error()) |  | ||||||
| 		return 0, statuscode.ServerError |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	content := string(bytes) |  | ||||||
|  |  | ||||||
| 	if strings.Contains(content, "ASLEEP:") { |  | ||||||
| 		message := strings.Split(content, ":")[1] |  | ||||||
| 		result, _ = strconv.Atoi(message) |  | ||||||
|  |  | ||||||
| 		if result > 0 { |  | ||||||
| 			return result, statuscode.Success |  | ||||||
| 		} |  | ||||||
| 	} else if strings.Contains(content, "STATUS_OK:") { |  | ||||||
| 		return 0, statuscode.SmsLongNumWaitCode |  | ||||||
| 	} else if strings.Contains(content, "ACCESS_NUMBER:") { |  | ||||||
| 		message := strings.Split(content, ":")[1] |  | ||||||
| 		result, _ = strconv.Atoi(message) |  | ||||||
|  |  | ||||||
| 		if result > 0 { |  | ||||||
| 			return result, statuscode.Success |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	e.Log.Errorf("激活长效号码失败,%s", content) |  | ||||||
|  |  | ||||||
| 	return 0, statuscode.ServerError |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // KeepLongTerm 长期租赁 |  | ||||||
| func (e *SmsPhone) KeepLongTerm(activationId int) int { |  | ||||||
| 	key, code := GetApiKey(e) |  | ||||||
|  |  | ||||||
| 	if code != statuscode.Success { |  | ||||||
| 		return statuscode.ServerError |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	url := fmt.Sprintf("?api_key=%s&action=keep&id=%d", key.ConfigValue, activationId) |  | ||||||
| 	client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil) |  | ||||||
| 	bytes, err := client.GetRaw(url, nil) |  | ||||||
|  |  | ||||||
| 	if err != nil { |  | ||||||
| 		e.Log.Errorf("租赁请求失败 %s", err.Error()) |  | ||||||
| 		return statuscode.ServerError |  | ||||||
| 	} |  | ||||||
| 	content := string(bytes) |  | ||||||
|  |  | ||||||
| 	if content == "OK" { |  | ||||||
| 		return statuscode.Success |  | ||||||
| 	} else { |  | ||||||
| 		e.Log.Errorf("租赁请求失败 %s", content) |  | ||||||
| 		return statuscode.ServerError |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // 取消租赁 |  | ||||||
| func (e *SmsPhone) CancelRental(activationId int) int { |  | ||||||
| 	key, code := GetApiKey(e) |  | ||||||
|  |  | ||||||
| 	if code != statuscode.Success { |  | ||||||
| 		e.Log.Errorf("租赁api请求失败 %s") |  | ||||||
| 		return statuscode.ServerError |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	url := fmt.Sprintf("?api_key=%s&action=setStatus&id=%d&status=8", key.ConfigValue, activationId) |  | ||||||
| 	client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil) |  | ||||||
| 	bytes, err := client.GetRaw(url, nil) |  | ||||||
|  |  | ||||||
| 	if err != nil { |  | ||||||
| 		e.Log.Errorf("租赁请求失败 %s", err.Error()) |  | ||||||
| 		return statuscode.ServerError |  | ||||||
| 	} |  | ||||||
| 	content := string(bytes) |  | ||||||
|  |  | ||||||
| 	if content == "ACCESS_CANCEL" { |  | ||||||
| 		return statuscode.Success |  | ||||||
| 	} else { |  | ||||||
| 		e.Log.Errorf("租赁请求失败 %s", content) |  | ||||||
| 		return statuscode.ServerError |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -637,64 +555,74 @@ func (e SmsServices) AutoRenewal() error { | |||||||
| 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 err := e.Orm.Model(data).Where("id =? and user_id =?", req.Id, userId).First(&data).Error; err != nil { | 	if req.Id > 0 { | ||||||
| 		e.Log.Errorf("修改自动续期 数据不存在 id:%d err:%s", req.Id, err.Error()) | 		if err := e.Orm.Model(data).Where("id =? and user_id =?", req.Id, userId).First(&data).Error; err != nil { | ||||||
| 		return statuscode.SmsNotExisted | 			e.Log.Errorf("修改自动续期 数据不存在 id:%d err:%s", req.Id, err.Error()) | ||||||
|  | 			return statuscode.SmsNotExisted | ||||||
|  | 		} | ||||||
|  | 	} else if req.ActivationId != "" { | ||||||
|  | 		if err := e.Orm.Model(data).Where("activation_id =? and user_id =?", req.ActivationId, userId).First(&data).Error; err != nil { | ||||||
|  | 			e.Log.Errorf("修改自动续期 数据不存在 id:%d err:%s", req.Id, err.Error()) | ||||||
|  | 			return statuscode.SmsNotExisted | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		return statuscode.InvalidParams | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// 短期租赁不支持自动续期 | 	// 短期租赁不支持自动续期 | ||||||
| 	if data.Type == 0 { | 	shouldReturn, i := e.DoChangeAutoRenewal(data, req.AutoRenew) | ||||||
| 		return statuscode.SmsNotAutoRenew | 	if shouldReturn { | ||||||
| 	} | 		return i | ||||||
|  |  | ||||||
| 	status := false |  | ||||||
| 	changeAutoRenewal := 2 |  | ||||||
| 	if req.AutoRenew == 1 { |  | ||||||
| 		status = true |  | ||||||
| 		changeAutoRenewal = 1 |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	data.AutoRenewal = req.AutoRenew |  | ||||||
| 	code := e.ChangeAutoRenewForApi(data.ActivationId, status) |  | ||||||
|  |  | ||||||
| 	if code != statuscode.Success { |  | ||||||
| 		return statuscode.ServerError |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if err := e.Orm.Model(&data).Update("auto_renewal", changeAutoRenewal).Error; err != nil { |  | ||||||
| 		e.Log.Errorf("修改自动续期失败 id:%d, status:%t", data.ActivationId, status) |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return statuscode.Success | 	return statuscode.Success | ||||||
| } | } | ||||||
|  |  | ||||||
| // ChangeAutoRenew 修改自动续期 | // DoChangeAutoRenewal 执行修改自动续期 | ||||||
| // activationId 短信id | func (e *SmsPhone) DoChangeAutoRenewal(data models.SmsPhone, autoRenewal int) (bool, int) { | ||||||
| // status 状态 | 	if data.Type == 0 { | ||||||
| func (e *SmsPhone) ChangeAutoRenewForApi(activationId int, status bool) int { | 		return true, statuscode.SmsNotAutoRenew | ||||||
| 	key, err := GetApiKey(e) |  | ||||||
|  |  | ||||||
| 	if err != statuscode.Success { |  | ||||||
| 		e.Log.Errorf("查询sms api请求失败 %d", activationId) |  | ||||||
| 		return statuscode.ServerError |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	url := fmt.Sprintf("?api_key=%s&action=setAutoRenew&id=%d&value=%t", key.ConfigValue, activationId, status) | 	status := false | ||||||
| 	client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil) | 	changeAutoRenewal := 2 | ||||||
| 	bytes, err1 := client.GetRaw(url, nil) | 	if autoRenewal == 1 { | ||||||
|  | 		status = true | ||||||
| 	if err1 != nil { | 		changeAutoRenewal = 1 | ||||||
| 		e.Log.Errorf("租赁请求失败 %s", err1.Error()) |  | ||||||
| 		return statuscode.ServerError |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	content := string(bytes) | 	var code int | ||||||
|  | 	data.AutoRenewal = autoRenewal | ||||||
|  |  | ||||||
| 	if content == "OK" { | 	if data.Actived == 3 { | ||||||
| 		return statuscode.Success | 		code = http.StatusOK | ||||||
| 	} else { | 	} else { | ||||||
| 		e.Log.Errorf("修改自动续期请求失败 id:%d, status:%t, %s", activationId, status, content) | 		code = e.ChangeAutoRenewManage(data.PlatformCode, data.ActivationId, status) | ||||||
| 		return statuscode.ServerError |  | ||||||
|  | 		if code != statuscode.Success { | ||||||
|  | 			return true, statuscode.ServerError | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := e.Orm.Model(&data).Update("auto_renewal", changeAutoRenewal).Error; err != nil { | ||||||
|  | 		e.Log.Errorf("修改自动续期失败 id:%d, status:%t", data.ActivationId, status) | ||||||
|  | 	} | ||||||
|  | 	return false, 0 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ChangeAutoRenewManage 修改自动续期管理 | ||||||
|  | func (e *SmsPhone) ChangeAutoRenewManage(platform string, activationId string, status bool) int { | ||||||
|  | 	switch platform { | ||||||
|  | 	case global.SmsPlatformDaisysms: | ||||||
|  | 		service := SmsDaisysms{Service: e.Service} | ||||||
|  |  | ||||||
|  | 		return service.ChangeAutoRenewForApi(activationId, status) | ||||||
|  | 	case global.SmsPlatformTextVerified: | ||||||
|  | 		service := SmsTextVerified{Service: e.Service} | ||||||
|  |  | ||||||
|  | 		return service.Renew(activationId, status) | ||||||
|  | 	default: | ||||||
|  | 		return statuscode.SmsPlatformUnavailable | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | |||||||
							
								
								
									
										65
									
								
								app/admin/service/sms_phone_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								app/admin/service/sms_phone_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,65 @@ | |||||||
|  | package service | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"go-admin/app/admin/service/dto" | ||||||
|  | 	"go-admin/common/global" | ||||||
|  | 	"go-admin/common/statuscode" | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"github.com/go-admin-team/go-admin-core/logger" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestVerifiedGetNumber(t *testing.T) { | ||||||
|  | 	db := initSetting() | ||||||
|  |  | ||||||
|  | 	service := SmsPhone{} | ||||||
|  | 	service.Orm = db | ||||||
|  | 	service.Log = logger.NewHelper(logger.DefaultLogger) | ||||||
|  |  | ||||||
|  | 	if _, err := service.GetNumber(&dto.GetNumberReq{ | ||||||
|  | 		PlatformCode: global.SmsPlatformTextVerified, | ||||||
|  | 		Type:         1, | ||||||
|  | 		ServiceCode:  "cocacola", | ||||||
|  | 		Period:       1, | ||||||
|  | 	}, 1); err != statuscode.Success { | ||||||
|  | 		t.Error(err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestVerifiedCancel(t *testing.T) { | ||||||
|  | 	db := initSetting() | ||||||
|  |  | ||||||
|  | 	service := SmsPhone{} | ||||||
|  | 	service.Orm = db | ||||||
|  | 	service.Log = logger.NewHelper(logger.DefaultLogger) | ||||||
|  |  | ||||||
|  | 	if err := service.CancelNumber(&dto.SmsPhoneCancelNumberReq{ | ||||||
|  | 		Id: 129, | ||||||
|  | 	}, 1); err != statuscode.Success { | ||||||
|  | 		t.Error(err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestVerifiedWake(t *testing.T) { | ||||||
|  | 	db := initSetting() | ||||||
|  |  | ||||||
|  | 	service := SmsPhone{} | ||||||
|  | 	service.Orm = db | ||||||
|  | 	service.Log = logger.NewHelper(logger.DefaultLogger) | ||||||
|  |  | ||||||
|  | 	service.WeakUp(&dto.WeakUpReq{ | ||||||
|  | 		ActivationId: "lr_01K21NVPVDEXTS1C3S8HPEC612", | ||||||
|  | 	}, 1, false) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGetPrices(t *testing.T) { | ||||||
|  | 	db := initSetting() | ||||||
|  |  | ||||||
|  | 	service := SmsServices{} | ||||||
|  | 	service.Orm = db | ||||||
|  | 	service.Log = logger.NewHelper(logger.DefaultLogger) | ||||||
|  |  | ||||||
|  | 	if err := service.SyncPrices(); err != nil { | ||||||
|  | 		t.Error(err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -48,7 +48,7 @@ func (e SmsReceiveLog) WebHook(req *dto.SmsReceiveWebHookReq) error { | |||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		phoneService := SmsPhone{Service: e.Service} | 		phoneService := SmsDaisysms{Service: e.Service} | ||||||
| 		if code := phoneService.setStatus(req.ActivationID); code != statuscode.Success { | 		if code := phoneService.setStatus(req.ActivationID); code != statuscode.Success { | ||||||
| 			e.Log.Errorf("接受验证码回调后修改状态失败 %d", code) | 			e.Log.Errorf("接受验证码回调后修改状态失败 %d", code) | ||||||
| 		} | 		} | ||||||
|  | |||||||
| @ -12,14 +12,19 @@ import ( | |||||||
| 	"gorm.io/gorm" | 	"gorm.io/gorm" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func initSetting() { | 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?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" | ||||||
| 	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.UserName = "webspan@proton.me" | ||||||
|  | 	config.ExtConfig.SmsTextVerified.Url = "https://www.textverified.com" | ||||||
| 	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") | ||||||
|  |  | ||||||
|  | 	return db | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestSmsReceiveLog(t *testing.T) { | func TestSmsReceiveLog(t *testing.T) { | ||||||
|  | |||||||
| @ -11,21 +11,75 @@ import ( | |||||||
| 	"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" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type SmsServices struct { | type SmsServices struct { | ||||||
| 	service.Service | 	service.Service | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (e SmsServices) OpenGetByCode(code string, param2 string, apiKey string) (any, error) { | ||||||
|  | 	panic("unimplemented") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 获取平台列表 | ||||||
|  | func (e SmsServices) GetPlatform(resp *[]dto.SmsPlatformGetListResp) error { | ||||||
|  | 	var datas []models.SysDictData | ||||||
|  |  | ||||||
|  | 	if err := e.Orm.Model(&models.SysDictData{}).Where("dict_type = 'sms_platform'").Find(&datas).Error; err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, v := range datas { | ||||||
|  | 		var respDto dto.SmsPlatformGetListResp | ||||||
|  | 		respDto.Label = v.DictLabel | ||||||
|  | 		respDto.Value = v.DictValue | ||||||
|  |  | ||||||
|  | 		*resp = append(*resp, respDto) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
| // GetPrice 获取SmsServices价格 | // GetPrice 获取SmsServices价格 | ||||||
| func (e SmsServices) GetPrice(req *dto.SmsGetPriceReq) (decimal.Decimal, error) { | func (e SmsServices) GetPrice(req *dto.SmsGetPriceReq) (decimal.Decimal, error) { | ||||||
| 	key := "number_fee_short_term" | 	var service models.SmsServices | ||||||
|  |  | ||||||
| 	if req.Type == 1 { | 	if err := e.Orm.Model(&service).Where("platform_code =? and code =?", req.PlatformCode, req.ServiceCode).First(&service).Error; err != nil { | ||||||
| 		key = "number_fee_long_term" | 		return decimal.Decimal{}, errors.New("获取价格失败") | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	var price decimal.Decimal | 	var price decimal.Decimal | ||||||
|  | 	var key string | ||||||
|  |  | ||||||
|  | 	if req.Type == 1 { | ||||||
|  | 		price = service.Price | ||||||
|  |  | ||||||
|  | 		switch req.PlatformCode { | ||||||
|  | 		case global.SmsPlatformDaisysms: | ||||||
|  | 			key = "long_number_premium_daisysms" | ||||||
|  | 		case global.SmsPlatformTextVerified: | ||||||
|  | 			key = "long_number_premium_textverified" | ||||||
|  | 		default: | ||||||
|  | 			return price, errors.New("获取价格失败") | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		price = service.LongPrice | ||||||
|  |  | ||||||
|  | 		switch req.PlatformCode { | ||||||
|  | 		case global.SmsPlatformDaisysms: | ||||||
|  | 			key = "number_premium_daisysms" | ||||||
|  | 		case global.SmsPlatformTextVerified: | ||||||
|  | 			key = "number_premium_textverified" | ||||||
|  | 		default: | ||||||
|  | 			return price, errors.New("获取价格失败") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if price.IsZero() { | ||||||
|  | 		return price, errors.New("获取价格失败") | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	configService := SysConfig{Service: e.Service} | 	configService := SysConfig{Service: e.Service} | ||||||
| 	configResp := dto.GetSysConfigByKEYForServiceResp{} | 	configResp := dto.GetSysConfigByKEYForServiceResp{} | ||||||
| 	err := configService.GetWithKey(&dto.SysConfigByKeyReq{ConfigKey: key}, &configResp) | 	err := configService.GetWithKey(&dto.SysConfigByKeyReq{ConfigKey: key}, &configResp) | ||||||
| @ -34,29 +88,96 @@ func (e SmsServices) GetPrice(req *dto.SmsGetPriceReq) (decimal.Decimal, error) | |||||||
| 		return price, err | 		return price, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	price, err = decimal.NewFromString(configResp.ConfigValue) | 	percent, err := decimal.NewFromString(configResp.ConfigValue) | ||||||
|  |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return price, err | 		return price, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if percent.IsZero() { | ||||||
|  | 		return price, nil | ||||||
|  | 	} else { | ||||||
|  | 		price = price.Mul((decimal.NewFromInt(100).Add(percent)).Div(decimal.NewFromInt(100))).Truncate(2) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	return price, nil | 	return price, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // GetList 获取SmsServices列表 | // GetList 获取SmsServices列表 | ||||||
| func (e SmsServices) GetList(resp *[]dto.SmsServicesGetListResp) error { | func (e SmsServices) GetList(resp *[]dto.SmsServicesGetListResp) error { | ||||||
| 	var data []models.SmsServices | 	var data []models.SmsServices | ||||||
| 	err := e.Orm.Find(&data).Error | 	err := e.Orm.Model(&models.SmsServices{}).Where("status =1").Find(&data).Error | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		e.Log.Errorf("SmsServicesService GetList error:%s \r\n", err) | 		e.Log.Errorf("SmsServicesService GetList error:%s \r\n", err) | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	dictService := SysDictData{Service: e.Service} | ||||||
|  | 	dictDatas, _ := dictService.GetMapByType("sms_platform") | ||||||
|  |  | ||||||
|  | 	keys := []string{"number_premium_daisysms", "number_premium_textverified", "long_number_premium_daisysms", "long_number_premium_textverified"} | ||||||
|  | 	configService := SysConfig{Service: e.Service} | ||||||
|  | 	mapConfigs, err := configService.GetWithKeyForMap(keys) | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Log.Errorf("获取配置失败,\r\n失败信息 %s", err.Error()) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	for _, item := range data { | 	for _, item := range data { | ||||||
| 		respItem := dto.SmsServicesGetListResp{ | 		respItem := dto.SmsServicesGetListResp{ | ||||||
| 			Name:   item.Name, | 			Name:         item.Name, | ||||||
| 			Code:   item.Code, | 			Code:         item.Code, | ||||||
| 			Status: item.Status, | 			Status:       item.Status, | ||||||
|  | 			PlatformCode: item.PlatformCode, | ||||||
|  | 			Price:        item.Price, | ||||||
|  | 			LongPrice:    item.LongPrice, | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		if dict, ok := dictDatas[respItem.PlatformCode]; ok { | ||||||
|  | 			respItem.PlatformName = dict.DictLabel | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		switch item.PlatformCode { | ||||||
|  | 		case global.SmsPlatformDaisysms: | ||||||
|  | 			if config, ok := mapConfigs["number_premium_daisysms"]; ok { | ||||||
|  | 				premium, err := decimal.NewFromString(config.ConfigValue) | ||||||
|  |  | ||||||
|  | 				if err != nil || premium.IsZero() { | ||||||
|  | 					e.Log.Errorf("浮动百分比为0,或者是转换错误 %s", config.ConfigValue) | ||||||
|  | 				} else { | ||||||
|  | 					respItem.Price = respItem.Price.Mul(decimal.NewFromInt(100).Add(premium).Div(decimal.NewFromInt(100))).Truncate(2) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			if config, ok := mapConfigs["long_number_premium_daisysms"]; ok { | ||||||
|  | 				premium, err := decimal.NewFromString(config.ConfigValue) | ||||||
|  |  | ||||||
|  | 				if err != nil || premium.IsZero() { | ||||||
|  | 					e.Log.Errorf("浮动百分比为0,或者是转换错误 %s", config.ConfigValue) | ||||||
|  | 				} else { | ||||||
|  | 					respItem.LongPrice = respItem.LongPrice.Mul(decimal.NewFromInt(100).Add(premium).Div(decimal.NewFromInt(100))).Truncate(2) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		case global.SmsPlatformTextVerified: | ||||||
|  | 			if config, ok := mapConfigs["number_premium_textverified"]; ok { | ||||||
|  | 				premium, err := decimal.NewFromString(config.ConfigValue) | ||||||
|  |  | ||||||
|  | 				if err != nil || premium.IsZero() { | ||||||
|  | 					e.Log.Errorf("浮动百分比为0,或者是转换错误 %s", config.ConfigValue) | ||||||
|  | 				} else { | ||||||
|  | 					respItem.Price = respItem.Price.Mul(decimal.NewFromInt(100).Add(premium).Div(decimal.NewFromInt(100))).Truncate(2) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			if config, ok := mapConfigs["long_number_premium_textverified"]; ok { | ||||||
|  | 				premium, err := decimal.NewFromString(config.ConfigValue) | ||||||
|  |  | ||||||
|  | 				if err != nil || premium.IsZero() { | ||||||
|  | 					e.Log.Errorf("浮动百分比为0,或者是转换错误 %s", config.ConfigValue) | ||||||
|  | 				} else { | ||||||
|  | 					respItem.LongPrice = respItem.LongPrice.Mul(decimal.NewFromInt(100).Add(premium).Div(decimal.NewFromInt(100))).Truncate(2) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		*resp = append(*resp, respItem) | 		*resp = append(*resp, respItem) | ||||||
| 	} | 	} | ||||||
| 	return nil | 	return nil | ||||||
| @ -66,13 +187,17 @@ func (e SmsServices) GetList(resp *[]dto.SmsServicesGetListResp) error { | |||||||
| func (e *SmsServices) GetPage(c *dto.SmsServicesGetPageReq, p *actions.DataPermission, list *[]models.SmsServices, count *int64) error { | func (e *SmsServices) GetPage(c *dto.SmsServicesGetPageReq, p *actions.DataPermission, list *[]models.SmsServices, count *int64) error { | ||||||
| 	var err error | 	var err error | ||||||
| 	var data models.SmsServices | 	var data models.SmsServices | ||||||
|  | 	query := e.Orm.Model(&data). | ||||||
| 	err = 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), | ||||||
| 		). | 		) | ||||||
|  |  | ||||||
|  | 	if c.Status > 0 { | ||||||
|  | 		query = query.Where("status = ?", c.Status) | ||||||
|  | 	} | ||||||
|  | 	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 { | ||||||
| @ -154,12 +279,30 @@ func (e *SmsServices) Remove(d *dto.SmsServicesDeleteReq, p *actions.DataPermiss | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (e *SmsServices) GetByCode(code string) (models.SmsServices, error) { | func (e *SmsServices) GetByCode(platformCode, code string) (models.SmsServices, error) { | ||||||
| 	var data models.SmsServices | 	var data models.SmsServices | ||||||
|  |  | ||||||
| 	if err := e.Orm.Model(data).Where("code =?", code).First(&data).Error; err != nil { | 	if err := e.Orm.Model(data).Where("platform_code =? and  code =?", platformCode, code).First(&data).Error; err != nil { | ||||||
| 		return data, err | 		return data, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return data, nil | 	return data, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // 同步第三方价格 | ||||||
|  | func (e *SmsServices) SyncPrices() error { | ||||||
|  | 	var datas []models.SmsServices | ||||||
|  |  | ||||||
|  | 	if err := e.Orm.Model(&models.SmsServices{}).Where("platform_code =?", global.SmsPlatformTextVerified).Find(&datas).Error; err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	daisysmsService := SmsDaisysms{Service: e.Service} | ||||||
|  | 	textVerifiedService := SmsTextVerified{Service: e.Service} | ||||||
|  |  | ||||||
|  | 	//daisysms 同步价格 | ||||||
|  | 	daisysmsService.SyncPrices() | ||||||
|  | 	//textVerified 同步价格 | ||||||
|  | 	textVerifiedService.SyncPrices() | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | |||||||
							
								
								
									
										855
									
								
								app/admin/service/sms_text_verified.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										855
									
								
								app/admin/service/sms_text_verified.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,855 @@ | |||||||
|  | package service | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"go-admin/app/admin/models" | ||||||
|  | 	"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" | ||||||
|  | 	"go-admin/utils/utility" | ||||||
|  | 	"net/http" | ||||||
|  | 	"net/url" | ||||||
|  | 	"path" | ||||||
|  | 	"regexp" | ||||||
|  | 	"strings" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/bytedance/sonic" | ||||||
|  | 	"github.com/go-admin-team/go-admin-core/sdk/service" | ||||||
|  | 	"github.com/go-redis/redis/v8" | ||||||
|  | 	"github.com/shopspring/decimal" | ||||||
|  | 	"gorm.io/gorm" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type SmsTextVerified struct { | ||||||
|  | 	service.Service | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 获取收到的验证码 | ||||||
|  | // messageId 短效或长效号码的ID | ||||||
|  | // typ 0-短效 1-长效 | ||||||
|  | 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 ( | ||||||
|  | 	loginUrl                = "/api/pub/v2/auth" | ||||||
|  | 	getAreas                = "/api/pub/v2/area-codes" | ||||||
|  | 	getServices             = "/api/pub/v2/services" | ||||||
|  | 	createRental            = "/api/pub/v2/reservations/rental"                     //长效收码 | ||||||
|  | 	createVerification      = "/api/pub/v2/verifications"                           //单次收码 | ||||||
|  | 	cancelVerification      = "/api/pub/v2/verifications/%s/cancel"                 //取消收码 | ||||||
|  | 	cancelRental            = "/api/pub/v2/reservations/rental/renewable/%s/refund" //取消长效号码 | ||||||
|  | 	wakeUp                  = "/api/pub/v2/wake-requests"                           //唤醒号码 | ||||||
|  | 	rentalPrice             = "/api/pub/v2/pricing/rentals"                         //长效收码价格 | ||||||
|  | 	verificationPrice       = "/api/pub/v2/pricing/verifications"                   //单次收码价格 | ||||||
|  | 	rentalDetail            = "/api/pub/v2/reservations/rental/renewable/%s"        //长效号码详情 | ||||||
|  | 	renewRental             = "/api/pub/v2/reservations/rental/renewable/%s/renew"  //长效续期 | ||||||
|  | 	updateRentalRenewStatus = "/api/pub/v2/reservations/rental/renewable/%s"        // 更改续租状态 | ||||||
|  | 	getSmsCode              = "/api/pub/v2/sms?reservationId=%s&reservationType=%s" //获取短信验证码 | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var idPattern = regexp.MustCompile(`^[a-zA-Z0-9_]{28}$`) | ||||||
|  |  | ||||||
|  | // 获取授权失败 | ||||||
|  | var ErrUnAuth = errors.New("未授权,请检查配置的账号和密码") | ||||||
|  | var ErrOutOfStockOrUnavailable = errors.New("缺货或服务不可用") | ||||||
|  |  | ||||||
|  | // Login 登录 | ||||||
|  | func (e *SmsTextVerified) Login() (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": config.ExtConfig.SmsTextVerified.UserName, | ||||||
|  | 		"X-API-KEY":      config.ExtConfig.SmsTextVerified.ApiKey, | ||||||
|  | 	} | ||||||
|  | 	_, err1 := client.Post(loginUrl, 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秒过期 | ||||||
|  | 		if err := redishelper.DefaultRedis.SetStringExpire(global.TextVerifiedToken, token, time.Duration(resp.ExpiresIn)*time.Second); err != nil { | ||||||
|  | 			e.Log.Errorf("TextVerified登录失败,缓存Token失败 error: %v", err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return token, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 获取token | ||||||
|  | func (e *SmsTextVerified) GetToken() (string, error) { | ||||||
|  | 	token, err := redishelper.DefaultRedis.GetString(global.TextVerifiedToken) | ||||||
|  | 	if err != nil && errors.Is(err, redis.Nil) { | ||||||
|  | 		// token不存在,重新登录获取 | ||||||
|  | 		return e.Login() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if token == "" { | ||||||
|  | 		return e.Login() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return token, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 获取区域 | ||||||
|  | func (e *SmsTextVerified) GetAreas() ([]string, error) { | ||||||
|  |  | ||||||
|  | 	// 这里可以实现获取地区的逻辑 | ||||||
|  | 	// 例如从数据库或配置文件中读取地区信息 | ||||||
|  | 	areas := []string{"Area1", "Area2", "Area3"} // 示例数据 | ||||||
|  | 	return areas, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 获取服务列表 | ||||||
|  | func (e *SmsTextVerified) GetServices() ([]dto.TextVerifiedServeResp, error) { | ||||||
|  | 	client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.SmsTextVerified.Url, nil) | ||||||
|  | 	headers, err := e.GetAuthHeader() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	resp := []dto.TextVerifiedServeResp{} | ||||||
|  | 	url := fmt.Sprintf("%s?numberType=mobile&reservationType=%s", getServices, "verification") | ||||||
|  | 	_, err1 := client.Get(url, headers, &resp) | ||||||
|  | 	if err1 != nil { | ||||||
|  | 		e.Log.Errorf("TextVerified获取服务列表失败 error: %v", err1) | ||||||
|  | 		return nil, err1 | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return resp, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 同步服务列表 | ||||||
|  | func (e *SmsTextVerified) SyncServices() error { | ||||||
|  | 	services, err := e.GetServices() | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Log.Errorf("获取服务列表失败: %v", err) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 提取所有服务的 Capability 作为查询条件 | ||||||
|  | 	capabilities := make([]string, 0, len(services)) | ||||||
|  | 	for _, service := range services { | ||||||
|  | 		if strings.ToLower(service.Capability) != "sms" { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if utility.ContainsString(capabilities, service.ServiceName) { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		capabilities = append(capabilities, service.ServiceName) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 批量查询已存在的服务 | ||||||
|  | 	var existingServices []models.SmsServices | ||||||
|  | 	err = e.Orm.Model(&models.SmsServices{}). | ||||||
|  | 		Where("platform_code = ? AND code IN ?", global.SmsPlatformTextVerified, capabilities). | ||||||
|  | 		Find(&existingServices).Error | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Log.Errorf("批量查询服务失败: %v", err) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 创建现有服务的 Code 集合,便于快速查找 | ||||||
|  | 	existingServiceCodes := make(map[string]struct{}, len(existingServices)) | ||||||
|  | 	for _, svc := range existingServices { | ||||||
|  | 		existingServiceCodes[svc.Code] = struct{}{} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 筛选出需要新增的服务 | ||||||
|  | 	entitys := make([]models.SmsServices, 0) | ||||||
|  | 	for _, service := range services { | ||||||
|  | 		if _, exists := existingServiceCodes[service.ServiceName]; !exists && strings.ToLower(service.Capability) == "sms" { | ||||||
|  | 			entity := models.SmsServices{ | ||||||
|  | 				Name:              service.ServiceName, | ||||||
|  | 				Code:              service.ServiceName, | ||||||
|  | 				PlatformCode:      global.SmsPlatformTextVerified, | ||||||
|  | 				ExpirationMinutes: 3, | ||||||
|  | 				Status:            2, | ||||||
|  | 			} | ||||||
|  | 			entitys = append(entitys, entity) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 批量创建新服务 | ||||||
|  | 	if len(entitys) > 0 { | ||||||
|  | 		err = e.Orm.Transaction(func(tx *gorm.DB) error { | ||||||
|  | 			if err := tx.CreateInBatches(&entitys, 500).Error; err != nil { | ||||||
|  | 				e.Log.Errorf("同步服务列表失败: %v", err) | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 			return nil | ||||||
|  | 		}) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 同步价格 | ||||||
|  | func (e *SmsTextVerified) SyncPrices() error { | ||||||
|  | 	var services []models.SmsServices | ||||||
|  | 	var entity models.SmsServices | ||||||
|  |  | ||||||
|  | 	if err := e.Orm.Model(&entity).Where("platform_code = ? and status =1", global.SmsPlatformTextVerified).Find(&services).Error; err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, v := range services { | ||||||
|  | 		params := map[string]interface{}{} | ||||||
|  |  | ||||||
|  | 		price, code := e.GetPrice(0, v.Code) | ||||||
|  |  | ||||||
|  | 		if code == http.StatusOK { | ||||||
|  | 			params["price"] = price | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		time.Sleep(time.Microsecond * 500) // 避免请求过快被限制 | ||||||
|  | 		longPrice, code := e.GetPrice(1, v.Code) | ||||||
|  | 		if code == http.StatusOK { | ||||||
|  | 			params["long_price"] = longPrice | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if len(params) > 0 { | ||||||
|  | 			if err := e.Orm.Model(&entity).Where("platform_code = ? and code = ?", global.SmsPlatformTextVerified, v.Code).Updates(params).Error; err != nil { | ||||||
|  | 				e.Log.Errorf("同步服务价格失败: %v", err) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 号码租赁 | ||||||
|  | // getType 0-短效 1-长效 | ||||||
|  | // service 服务code | ||||||
|  | // maxPrice 最大价格 | ||||||
|  | // period 时长(月=30天) | ||||||
|  | func (e SmsTextVerified) GetNumberForApi(typ int, serviceCode string, price decimal.Decimal, period int) (dto.SmsPhoneGetPhoneResp, int) { | ||||||
|  | 	//这个平台的所有号码都是美国号码 默认国家代码都是 +1 | ||||||
|  | 	var err error | ||||||
|  | 	var createResp dto.TextVerifiedResp | ||||||
|  | 	var result dto.SmsPhoneGetPhoneResp | ||||||
|  | 	switch typ { | ||||||
|  | 	case 0: | ||||||
|  | 		var code int | ||||||
|  | 		createResp, code = e.CreateVerification(serviceCode) | ||||||
|  |  | ||||||
|  | 		if code == statuscode.Success && createResp.Href != "" { | ||||||
|  | 			bytes, err := e.doRequest(&createResp) | ||||||
|  |  | ||||||
|  | 			if err != nil { | ||||||
|  | 				e.Log.Errorf("短效号码 获取号码失败:%v", e) | ||||||
|  | 				return result, 0 | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			resp := dto.VerificationDTO{} | ||||||
|  |  | ||||||
|  | 			if err := sonic.Unmarshal(bytes, &resp); err != nil { | ||||||
|  | 				e.Log.Errorf("短效号码 获取号码失败:%v", e) | ||||||
|  | 				return result, 0 | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			result.Id = resp.ID | ||||||
|  | 			result.Phone = "1" + resp.Number | ||||||
|  | 			result.EndAt = &resp.EndsAt | ||||||
|  |  | ||||||
|  | 			return result, statuscode.Success | ||||||
|  | 		} else if code == statuscode.Success && createResp.Href == "" { | ||||||
|  | 			e.Log.Errorf("获取号码没有 返回值") | ||||||
|  | 			return result, statuscode.ServerError | ||||||
|  | 		} else { | ||||||
|  | 			e.Log.Errorf("获取号码失败:%v", err) | ||||||
|  | 			return result, statuscode.ServerError | ||||||
|  | 		} | ||||||
|  | 	case 1: | ||||||
|  | 		createResp, err = e.CreateRental(serviceCode) | ||||||
|  |  | ||||||
|  | 		if err == nil && createResp.Href != "" { | ||||||
|  | 			saleId := getIdByUrl(createResp.Href) | ||||||
|  | 			bytes, err := e.doRequest(&createResp) | ||||||
|  |  | ||||||
|  | 			if err != nil { | ||||||
|  | 				e.Log.Errorf("通过销售id [%s] 获取号码失败:%v", saleId, err) | ||||||
|  | 				return result, statuscode.ServerError | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			resp := dto.SaleResponse{} | ||||||
|  | 			if err := sonic.Unmarshal(bytes, &resp); err != nil { | ||||||
|  | 				e.Log.Errorf("反序列化失败:%v", err) | ||||||
|  |  | ||||||
|  | 				return result, statuscode.ServerError | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if len(resp.Reservations) > 0 { | ||||||
|  | 				for _, v := range resp.Reservations { | ||||||
|  | 					if v.ID != "" { | ||||||
|  | 						result.Id = v.ID | ||||||
|  | 						continue | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				if result.Id == "" { | ||||||
|  | 					e.Log.Errorf("获取长效号码失败,没有返回值") | ||||||
|  | 					return result, statuscode.ServerError | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				detail, code := e.GetRentalDetail(result.Id) | ||||||
|  |  | ||||||
|  | 				if code != statuscode.Success { | ||||||
|  | 					return result, code | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				result.Phone = "1" + detail.Number | ||||||
|  | 				endTime := resp.UpdatedAt.AddDate(0, 0, 30) | ||||||
|  | 				result.EndAt = &(endTime) // 30天后过期 | ||||||
|  |  | ||||||
|  | 				return result, statuscode.Success | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 		} else if err == nil && createResp.Href == "" { | ||||||
|  | 			e.Log.Errorf("获取长效号码失败,没有返回值") | ||||||
|  | 			return result, statuscode.ServerError | ||||||
|  | 		} else { | ||||||
|  | 			if err == ErrOutOfStockOrUnavailable { | ||||||
|  | 				return result, statuscode.SmsOutOfStockOrUnavailable | ||||||
|  | 			} | ||||||
|  | 			e.Log.Errorf("获取长效号码失败:%v", err) | ||||||
|  | 			return result, statuscode.ServerError | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 	default: | ||||||
|  | 		return result, statuscode.SmsPlatformUnavailable | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		return result, statuscode.ServerError | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return result, statuscode.Success | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 执行查询请求 | ||||||
|  | func (e *SmsTextVerified) doRequest(resp *dto.TextVerifiedResp) ([]byte, error) { | ||||||
|  | 	bytes := []byte{} | ||||||
|  | 	if resp.Href == "" { | ||||||
|  | 		return bytes, errors.New("成功请求没有返回url") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	header, err := e.GetAuthHeader() | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		return bytes, fmt.Errorf("获取授权失败:%v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	client := httphelper.NewHTTPClient(10*time.Second, "", header) | ||||||
|  |  | ||||||
|  | 	_, err = client.DoRequest(resp.Method, resp.Href, nil, nil, nil, &bytes) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		return bytes, fmt.Errorf("请求失败:%v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return bytes, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 长号码租赁 | ||||||
|  | func (e *SmsTextVerified) CreateRental(serviceCode string) (dto.TextVerifiedResp, error) { | ||||||
|  | 	req := dto.TextVerifiedCreateRewalReq{ | ||||||
|  | 		ServiceName: serviceCode, | ||||||
|  | 		Capability:  "sms", | ||||||
|  | 		NumberType:  "mobile", | ||||||
|  | 		IsRenewable: true, | ||||||
|  | 		AlwaysOn:    false, | ||||||
|  | 		Duration:    "thirtyDay", | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	resp := dto.TextVerifiedResp{} | ||||||
|  |  | ||||||
|  | 	client, code := e.GetTextVerifiedAuthClient() | ||||||
|  |  | ||||||
|  | 	if code != statuscode.Success { | ||||||
|  | 		e.Log.Errorf("获取头信息失败 error: %d", code) | ||||||
|  | 		return resp, ErrUnAuth | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	headers := map[string]string{ | ||||||
|  | 		"Content-Type": "application/json", | ||||||
|  | 	} | ||||||
|  | 	status, err := client.Post(createRental, req, headers, &resp) | ||||||
|  |  | ||||||
|  | 	// 201 成功 | ||||||
|  | 	if status == http.StatusCreated || resp.Href != "" { | ||||||
|  | 		return resp, nil | ||||||
|  | 	} else if err != nil { | ||||||
|  | 		if strings.Contains(err.Error(), "Out of stock or unavailable") { | ||||||
|  | 			e.Log.Errorf("缺货或服务不可用 %v", err) | ||||||
|  | 			return resp, ErrOutOfStockOrUnavailable | ||||||
|  | 		} | ||||||
|  | 		e.Log.Errorf("短信平台不可用 error: %v", err) | ||||||
|  | 		return resp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return resp, fmt.Errorf("短信平台不可用 status: %v, error: %v", status, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 获取长效号码详情 | ||||||
|  | func (e *SmsTextVerified) GetRentalDetail(id string) (dto.VerificationRentalDetailResp, int) { | ||||||
|  | 	result := dto.VerificationRentalDetailResp{} | ||||||
|  |  | ||||||
|  | 	client, code := e.GetTextVerifiedAuthClient() | ||||||
|  | 	if code != statuscode.Success { | ||||||
|  | 		return result, code | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	_, err := client.Get(fmt.Sprintf(rentalDetail, id), nil, &result) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Log.Errorf("查询长效详情失败 id:%s error: %v", id, err) | ||||||
|  | 		return result, statuscode.ServerError | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return result, statuscode.Success | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 唤醒号码 | ||||||
|  | // returns activationId,messageId, code | ||||||
|  | func (e SmsTextVerified) getExtraActivation(id string) (string, string, int) { | ||||||
|  | 	client, code := e.GetTextVerifiedAuthClient() | ||||||
|  |  | ||||||
|  | 	if code != statuscode.Success { | ||||||
|  | 		return "", "", code | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req := dto.TextVerifiedWakeUpReq{ | ||||||
|  | 		RevervationId: id, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	resp := dto.TextVerifiedResp{} | ||||||
|  | 	headers := map[string]string{ | ||||||
|  | 		"Content-Type": "application/json", | ||||||
|  | 	} | ||||||
|  | 	status, err := client.Post(wakeUp, req, headers, &resp) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Log.Errorf("唤醒号码失败 id:%s error: %v", id, err) | ||||||
|  | 		return "", "", statuscode.ServerError | ||||||
|  | 	} else if status == http.StatusCreated || status == http.StatusOK { | ||||||
|  | 		if resp.Method != "" && resp.Href != "" { | ||||||
|  | 			bytes, err := e.doRequest(&resp) | ||||||
|  |  | ||||||
|  | 			if err != nil { | ||||||
|  | 				e.Log.Errorf("唤醒号码失败 id:%s error: %v", id, err) | ||||||
|  | 				return "", "", statuscode.ServerError | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			detailResp := dto.TextVerifiedWakeUpResp{} | ||||||
|  |  | ||||||
|  | 			if err := sonic.Unmarshal(bytes, &detailResp); err != nil { | ||||||
|  | 				e.Log.Errorf("唤醒号码反序列化失败 id:%s error: %v", id, err) | ||||||
|  | 				return "", "", statuscode.ServerError | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			return detailResp.ReservationId, detailResp.Id, statuscode.Success | ||||||
|  | 		} else { | ||||||
|  | 			e.Log.Errorf("唤醒号码失败 id:%s error: %v", id, err) | ||||||
|  | 			return "", "", statuscode.ServerError | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return "", "", statuscode.ServerError | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 获取价格 | ||||||
|  | // getType 0-短效 1-长效 | ||||||
|  | // returns decimal.Decimal(单价), int(状态code) | ||||||
|  | func (e *SmsTextVerified) GetPrice(typ int, serviceName string) (decimal.Decimal, int) { | ||||||
|  | 	switch typ { | ||||||
|  | 	case 1: | ||||||
|  | 		return e.GetRentalPrice(serviceName) | ||||||
|  | 	case 0: | ||||||
|  | 		return e.GetVerificationPrice(serviceName) | ||||||
|  | 	default: | ||||||
|  | 		return decimal.Zero, statuscode.SmsInvalidType | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 获取长租价格 | ||||||
|  | func (e *SmsTextVerified) GetRentalPrice(serviceName string) (decimal.Decimal, int) { | ||||||
|  | 	req := dto.TextVerifiedPriceReq{ | ||||||
|  | 		ServiceName: serviceName, | ||||||
|  | 		Capability:  "sms", | ||||||
|  | 		NumberType:  "mobile", | ||||||
|  | 		AreaCode:    false, | ||||||
|  | 		Duration:    "thirtyDay", | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	client, code := e.GetTextVerifiedAuthClient() | ||||||
|  |  | ||||||
|  | 	if code != statuscode.Success { | ||||||
|  | 		e.Log.Errorf("获取授权请求失败,status %d", code) | ||||||
|  | 		return decimal.Zero, code | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	resp := dto.TextVerifiedPriceResp{} | ||||||
|  | 	headers := map[string]string{ | ||||||
|  | 		"Content-Type": "application/json", | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	_, err := client.Post(rentalPrice, req, headers, &resp) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Log.Errorf("获取价格失败, error: %v", err) | ||||||
|  | 		return decimal.Zero, statuscode.ServerError | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return resp.Price, statuscode.Success | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 获取单次验证码价格 | ||||||
|  | func (e *SmsTextVerified) GetVerificationPrice(sericeName string) (decimal.Decimal, int) { | ||||||
|  | 	params := map[string]interface{}{ | ||||||
|  | 		"serviceName": sericeName, | ||||||
|  | 		"areaCode":    false, | ||||||
|  | 		"carrier":     false, | ||||||
|  | 		"capability":  "sms", | ||||||
|  | 		"numberType":  "mobile", | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	client, code := e.GetTextVerifiedAuthClient() | ||||||
|  |  | ||||||
|  | 	if code != statuscode.Success { | ||||||
|  | 		e.Log.Errorf("获取授权请求失败,status %d", code) | ||||||
|  | 		return decimal.Zero, code | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	resp := dto.TextVerifiedPriceResp{} | ||||||
|  | 	headers := map[string]string{ | ||||||
|  | 		"Content-Type": "application/json", | ||||||
|  | 	} | ||||||
|  | 	_, err := client.Post(verificationPrice, params, headers, &resp) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Log.Errorf("获取价格失败, error: %v", err) | ||||||
|  | 		return decimal.Zero, statuscode.ServerError | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return resp.Price, statuscode.Success | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 单次接收 | ||||||
|  | func (e *SmsTextVerified) CreateVerification(serviceCode string) (dto.TextVerifiedResp, int) { | ||||||
|  | 	req := dto.TextVerifiedCreateRewalReq{ | ||||||
|  | 		ServiceName: serviceCode, | ||||||
|  | 		Capability:  "sms", | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	resp := dto.TextVerifiedResp{} | ||||||
|  | 	client, code := e.GetTextVerifiedAuthClient() | ||||||
|  |  | ||||||
|  | 	if code != statuscode.Success { | ||||||
|  | 		return resp, code | ||||||
|  | 	} | ||||||
|  | 	headers := map[string]string{ | ||||||
|  | 		"Content-Type": "application/json", | ||||||
|  | 	} | ||||||
|  | 	status, err := client.Post(createVerification, req, headers, &resp) | ||||||
|  |  | ||||||
|  | 	// 201 成功 | ||||||
|  | 	if status == http.StatusCreated { | ||||||
|  | 		return resp, statuscode.Success | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Log.Errorf("TextVerified创建单次收码失败 error: %v", err) | ||||||
|  | 		return resp, statuscode.ServerError | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return resp, statuscode.Success | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 取消验证码 | ||||||
|  | // typ 0-短效 1-长效 | ||||||
|  | func (e SmsTextVerified) CancelRental(id string, typ int) int { | ||||||
|  | 	client, code := e.GetTextVerifiedAuthClient() | ||||||
|  |  | ||||||
|  | 	if code != statuscode.Success { | ||||||
|  | 		return code | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var url string | ||||||
|  |  | ||||||
|  | 	if typ == 0 { | ||||||
|  | 		url = fmt.Sprintf(cancelVerification, id) | ||||||
|  | 	} else { | ||||||
|  | 		url = fmt.Sprintf(cancelRental, id) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	headers := map[string]string{ | ||||||
|  | 		"Content-Type": "application/json", | ||||||
|  | 	} | ||||||
|  | 	_, code, err := client.PostRaw(url, headers) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		if strings.Contains(err.Error(), "Self service refund not permitted for this rental") { | ||||||
|  | 			return statuscode.SmsRentalRefundNotPermitted | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		e.Log.Errorf("TextVerified取消验证码失败 error: %v", err) | ||||||
|  | 		return statuscode.ServerError | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if code != http.StatusOK { | ||||||
|  | 		e.Log.Errorf("TextVerified取消验证码失败,返回状态码 %d", code) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return statuscode.Success | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *SmsTextVerified) GetTextVerifiedAuthClient() (*httphelper.HTTPClient, int) { | ||||||
|  | 	header, err := e.GetAuthHeader() | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Log.Errorf("取消验证码获取token失败 error: %v", err) | ||||||
|  | 		return nil, statuscode.ServerError | ||||||
|  | 	} | ||||||
|  | 	client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.SmsTextVerified.Url, header) | ||||||
|  |  | ||||||
|  | 	return client, statuscode.Success | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TextVerifiedWebHook TextVerified短信发送记录回调 | ||||||
|  | func (e *SmsTextVerified) TextVerifiedWebHook(req *dto.TextVerifiedWebHookReq) error { | ||||||
|  | 	entity := &models.SmsPhone{} | ||||||
|  |  | ||||||
|  | 	switch req.Event { | ||||||
|  | 	//续订回调 | ||||||
|  | 	case "v2.rental.billingcycle.renewed": | ||||||
|  | 		e.Log.Infof("续订回调 %v", req) | ||||||
|  | 		data := dto.TextVerifiedWebHookRenewedReq{} | ||||||
|  | 		val, err := sonic.MarshalString(req.Data) | ||||||
|  |  | ||||||
|  | 		if err != nil { | ||||||
|  | 			e.Log.Errorf("数据类型错误 %v", req) | ||||||
|  | 			return fmt.Errorf("数据类型错误 %s", req.Data) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		err = sonic.UnmarshalString(val, &data) | ||||||
|  |  | ||||||
|  | 		if err != nil { | ||||||
|  | 			e.Log.Errorf("数据类型错误 %v", req) | ||||||
|  | 			return fmt.Errorf("数据类型错误 %s", req.Data) | ||||||
|  | 		} | ||||||
|  | 	//过期回调 | ||||||
|  | 	case "v2.rental.billingcycle.expired": | ||||||
|  | 		e.Log.Infof("过期 %v", req) | ||||||
|  | 		// data := dto.TextVerifiedWebHookBillingCycleExpiredReq{} | ||||||
|  | 		// val, err := sonic.MarshalString(req.Data) | ||||||
|  |  | ||||||
|  | 		// if err != nil { | ||||||
|  | 		// 	e.Log.Errorf("数据类型错误 %v", req) | ||||||
|  | 		// 	return fmt.Errorf("数据类型错误 %s", req.Data) | ||||||
|  | 		// } | ||||||
|  |  | ||||||
|  | 		// err = sonic.UnmarshalString(val, &data) | ||||||
|  |  | ||||||
|  | 		// if err != nil { | ||||||
|  | 		// 	e.Log.Errorf("数据类型错误 %v", req) | ||||||
|  | 		// 	return fmt.Errorf("数据类型错误 %s", req.Data) | ||||||
|  | 		// } | ||||||
|  |  | ||||||
|  | 		// if err := e.Orm.Model(&entity).Where("new_activation_id =? and status ='1'", data.RevervationId).Updates(map[string]interface{}{"status": 3}).Error; err != nil { | ||||||
|  | 		// 	return err | ||||||
|  | 		// } | ||||||
|  | 	//收到短信回调 | ||||||
|  | 	case "v2.sms.received": | ||||||
|  | 		e.Log.Infof("收到短信回调 %v", req) | ||||||
|  | 		data := dto.TextVerifiedWebHookSmsReceivedReq{} | ||||||
|  |  | ||||||
|  | 		val, err := sonic.MarshalString(req.Data) | ||||||
|  |  | ||||||
|  | 		if err != nil { | ||||||
|  | 			e.Log.Errorf("数据类型错误 %v", req) | ||||||
|  | 			return fmt.Errorf("数据类型错误 %s", req.Data) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		err = sonic.UnmarshalString(val, &data) | ||||||
|  |  | ||||||
|  | 		if err != nil { | ||||||
|  | 			e.Log.Errorf("数据类型错误 %v", req) | ||||||
|  | 			return fmt.Errorf("数据类型错误 %s", req.Data) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if err := e.Orm.Model(&entity).Where("new_activation_id =? and status =1", data.ReservationId).Updates(map[string]interface{}{"status": "2", "actived": 2, "code": data.ParsedCode}).Error; err != nil { | ||||||
|  | 			e.Log.Errorf("数据类型错误 %v", req) | ||||||
|  | 			return fmt.Errorf("数据类型错误 %s", req.Data) | ||||||
|  | 		} | ||||||
|  | 	//预定订单完成回调 | ||||||
|  | 	case "v2.rental.backorder.fulfilled": | ||||||
|  | 		e.Log.Infof("预定订单完成回调 %v", req) | ||||||
|  | 		data := dto.TextVerifiedWebHookFulfilledReq{} | ||||||
|  | 		val, err := sonic.MarshalString(req.Data) | ||||||
|  |  | ||||||
|  | 		if err != nil { | ||||||
|  | 			e.Log.Errorf("数据类型错误 %v", req) | ||||||
|  | 			return fmt.Errorf("数据类型错误 %s", req.Data) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		err = sonic.UnmarshalString(val, &data) | ||||||
|  |  | ||||||
|  | 		if err != nil { | ||||||
|  | 			e.Log.Errorf("数据类型错误 %v", req) | ||||||
|  | 			return fmt.Errorf("数据类型错误 %s", req.Data) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// if err:=e.Orm.Model(&entity).Where("new_activation_id =? ",data.) | ||||||
|  | 	// 预定订单创建 | ||||||
|  | 	case "v2.reservation.created": | ||||||
|  | 		// e.Log.Infof("预定订单创建 %v", req) | ||||||
|  | 		// data := dto.TextVerifiedWebHookReservationCreatedReq{} | ||||||
|  | 		// val, err := sonic.MarshalString(req.Data) | ||||||
|  |  | ||||||
|  | 		// if err != nil { | ||||||
|  | 		// 	e.Log.Errorf("数据类型错误 %v", req) | ||||||
|  | 		// 	return fmt.Errorf("数据类型错误 %s", req.Data) | ||||||
|  | 		// } | ||||||
|  |  | ||||||
|  | 		// err = sonic.UnmarshalString(val, &data) | ||||||
|  |  | ||||||
|  | 		// if err != nil { | ||||||
|  | 		// 	e.Log.Errorf("数据类型错误 %v", req) | ||||||
|  | 		// 	return fmt.Errorf("数据类型错误 %s", req.Data) | ||||||
|  | 		// } | ||||||
|  | 	default: | ||||||
|  | 		e.Log.Errorf("未知的事件类型 %s", req.Event) | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 续期 | ||||||
|  | // typ 0-短效 1-长效 | ||||||
|  | func (e *SmsTextVerified) Renew(activationId string, status bool) int { | ||||||
|  | 	url := fmt.Sprintf(updateRentalRenewStatus, activationId) | ||||||
|  |  | ||||||
|  | 	client, code := e.GetTextVerifiedAuthClient() | ||||||
|  |  | ||||||
|  | 	if code != statuscode.Success { | ||||||
|  | 		e.Log.Errorf("获取长效续期失败 %d", code) | ||||||
|  | 		return statuscode.ServerError | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	params := map[string]interface{}{ | ||||||
|  | 		"includeForRenewal": status, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	headers := map[string]string{ | ||||||
|  | 		"Content-Type": "application/json", | ||||||
|  | 	} | ||||||
|  | 	mapData := map[string]interface{}{} | ||||||
|  | 	statuCode, err := client.Post(url, params, headers, &mapData) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Log.Errorf("修改长效续期状态失败 %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if statuCode == http.StatusOK { | ||||||
|  | 		return statuscode.Success | ||||||
|  | 	} else { | ||||||
|  | 		e.Log.Errorf("修改长效续期状态失败 %d", statuCode) | ||||||
|  | 		return statuscode.ServerError | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 获取授权header头 | ||||||
|  | func (e *SmsTextVerified) GetAuthHeader() (map[string]string, error) { | ||||||
|  | 	token, err := e.GetToken() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	headers := map[string]string{ | ||||||
|  | 		"Authorization": "Bearer " + token, | ||||||
|  | 	} | ||||||
|  | 	return headers, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 获取id 根据url (https://www.textverified.com/api/pub/v2/sales/{id}}") | ||||||
|  | func getIdByUrl(URLString string) string { | ||||||
|  | 	parsedNewURL, _ := url.Parse(URLString) | ||||||
|  | 	newID := path.Base(parsedNewURL.Path) | ||||||
|  |  | ||||||
|  | 	if isValidID(newID) { | ||||||
|  | 		return newID | ||||||
|  | 	} else { | ||||||
|  | 		return "" | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func isValidID(s string) bool { | ||||||
|  | 	return idPattern.MatchString(s) | ||||||
|  | } | ||||||
							
								
								
									
										53
									
								
								app/admin/service/sms_text_verified_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								app/admin/service/sms_text_verified_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,53 @@ | |||||||
|  | package service | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"github.com/go-admin-team/go-admin-core/logger" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestSmsTextVerifiedLogin(t *testing.T) { | ||||||
|  | 	db := initSetting() | ||||||
|  |  | ||||||
|  | 	s := SmsTextVerified{} | ||||||
|  | 	s.Orm = db | ||||||
|  | 	s.Log = logger.NewHelper(logger.DefaultLogger) | ||||||
|  |  | ||||||
|  | 	token, err := s.Login() | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Login failed: %v", err) | ||||||
|  | 	} else { | ||||||
|  | 		t.Logf("Login succeeded, token: %s", token) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestSmsTextVerifiedGetServices(t *testing.T) { | ||||||
|  | 	db := initSetting() | ||||||
|  |  | ||||||
|  | 	s := SmsTextVerified{} | ||||||
|  | 	s.Orm = db | ||||||
|  | 	s.Log = logger.NewHelper(logger.DefaultLogger) | ||||||
|  |  | ||||||
|  | 	// Now, test GetServices with the valid token | ||||||
|  | 	servicesResp, err := s.GetServices() | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("GetServices failed: %v", err) | ||||||
|  | 	} else { | ||||||
|  | 		t.Logf("GetServices succeeded, services: %+v", servicesResp) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestSyncTextVerifiedServices(t *testing.T) { | ||||||
|  | 	db := initSetting() | ||||||
|  |  | ||||||
|  | 	s := SmsTextVerified{} | ||||||
|  | 	s.Orm = db | ||||||
|  | 	s.Log = logger.NewHelper(logger.DefaultLogger) | ||||||
|  |  | ||||||
|  | 	err := s.SyncServices() | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("SyncTextVerifiedServices failed: %v", err) | ||||||
|  | 	} else { | ||||||
|  | 		t.Log("SyncTextVerifiedServices succeeded") | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -175,6 +175,31 @@ func (e *SysConfig) GetWithKey(c *dto.SysConfigByKeyReq, resp *dto.GetSysConfigB | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (e *SysConfig) GetWithKeyForList(keys []string, list *[]dto.GetSysConfigByKEYForServiceResp) error { | ||||||
|  | 	if err := e.Orm.Model(&models.SysConfig{}).Where("config_key in ?", keys). | ||||||
|  | 		Find(list).Error; err != nil { | ||||||
|  | 		e.Log.Errorf("Service GetSysConfigByKey error:%s", err) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *SysConfig) GetWithKeyForMap(keys []string) (map[string]dto.GetSysConfigByKEYForServiceResp, error) { | ||||||
|  | 	listData := make([]dto.GetSysConfigByKEYForServiceResp, 0) | ||||||
|  | 	err := e.GetWithKeyForList(keys, &listData) | ||||||
|  | 	result := make(map[string]dto.GetSysConfigByKEYForServiceResp) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		return result, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, v := range listData { | ||||||
|  | 		result[v.ConfigKey] = v | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return result, nil | ||||||
|  | } | ||||||
|  |  | ||||||
| func (e *SysConfig) GetWithKeyList(c *dto.SysConfigGetToSysAppReq, list *[]models.SysConfig) error { | func (e *SysConfig) GetWithKeyList(c *dto.SysConfigGetToSysAppReq, list *[]models.SysConfig) error { | ||||||
| 	var err error | 	var err error | ||||||
| 	err = e.Orm. | 	err = e.Orm. | ||||||
|  | |||||||
| @ -15,6 +15,33 @@ type SysDictData struct { | |||||||
| 	service.Service | 	service.Service | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (e *SysDictData) GetMapByType(typ string) (map[string]models.SysDictData, error) { | ||||||
|  | 	datas, err := e.GetByType(typ) | ||||||
|  | 	result := make(map[string]models.SysDictData) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, data := range datas { | ||||||
|  | 		result[data.DictValue] = data | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return result, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 根据 type获取字典数据 | ||||||
|  | func (e SysDictData) GetByType(typ string) ([]models.SysDictData, error) { | ||||||
|  | 	var datas []models.SysDictData | ||||||
|  |  | ||||||
|  | 	if err := e.Orm.Model(&models.SysDictData{}).Where("dict_type = ?", typ).Find(&datas).Error; err != nil { | ||||||
|  | 		e.Log.Errorf("根据type获取字典数据失败,\r\n失败信息 %s", err.Error()) | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return datas, nil | ||||||
|  | } | ||||||
|  |  | ||||||
| // GetPage 获取列表 | // GetPage 获取列表 | ||||||
| func (e *SysDictData) GetPage(c *dto.SysDictDataGetPageReq, list *[]models.SysDictData, count *int64) error { | func (e *SysDictData) GetPage(c *dto.SysDictDataGetPageReq, list *[]models.SysDictData, count *int64) error { | ||||||
| 	var err error | 	var err error | ||||||
|  | |||||||
| @ -50,6 +50,9 @@ func (e SysUser) Register(req *dto.RegisterReq) (dto.SysUserRegisterResp, int) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	data.RoleId = role.RoleId | 	data.RoleId = role.RoleId | ||||||
|  | 	memberApiService := MemberApi{} | ||||||
|  | 	memberApiService.Log = e.Log | ||||||
|  | 	var memberApi models.MemberApi | ||||||
|  |  | ||||||
| 	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 { | ||||||
| @ -67,6 +70,13 @@ func (e SysUser) Register(req *dto.RegisterReq) (dto.SysUserRegisterResp, int) { | |||||||
| 		result.Token = token | 		result.Token = token | ||||||
| 		result.Expire = int(expire.Unix()) | 		result.Expire = int(expire.Unix()) | ||||||
|  |  | ||||||
|  | 		memberApiService.Orm = tx | ||||||
|  |  | ||||||
|  | 		if err1 := memberApiService.CreateApi(data.UserId, &memberApi, 0); err1 != nil { | ||||||
|  | 			e.Log.Errorf("创建用户失败 %v", err1) | ||||||
|  | 			return err1 | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		return nil | 		return nil | ||||||
| 	}) | 	}) | ||||||
|  |  | ||||||
| @ -170,11 +180,27 @@ func (e *SysUser) Insert(c *dto.SysUserInsertReq) error { | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	c.Generate(&data) | 	c.Generate(&data) | ||||||
| 	err = e.Orm.Create(&data).Error |  | ||||||
| 	if err != nil { | 	memberApiService := MemberApi{} | ||||||
| 		e.Log.Errorf("db error: %s", err) | 	memberApiService.Log = e.Log | ||||||
| 		return err |  | ||||||
| 	} | 	err = e.Orm.Transaction(func(tx *gorm.DB) error { | ||||||
|  | 		err = tx.Create(&data).Error | ||||||
|  | 		if err != nil { | ||||||
|  | 			e.Log.Errorf("db error: %s", err) | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		memberApiService.Orm = tx | ||||||
|  |  | ||||||
|  | 		if err1 := memberApiService.CreateApi(data.UserId, &models.MemberApi{}, 0); err1 != nil { | ||||||
|  | 			e.Log.Errorf("创建用户API失败, %s", err1) | ||||||
|  | 			return err1 | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return nil | ||||||
|  | 	}) | ||||||
|  |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | |||||||
| @ -19,6 +19,7 @@ func InitJob() { | |||||||
| 		"SmsJob":               SmsJob{},               //短信定时查询验证码 | 		"SmsJob":               SmsJob{},               //短信定时查询验证码 | ||||||
| 		"SmsRenewalJob":        SmsRenewalJob{},        //短信定时自动续期 | 		"SmsRenewalJob":        SmsRenewalJob{},        //短信定时自动续期 | ||||||
| 		"AutoDeleteJob":        AutoDeleteJob{},        //定时删除任务 | 		"AutoDeleteJob":        AutoDeleteJob{},        //定时删除任务 | ||||||
|  | 		"SmsPriceJob":          SmsPriceJob{},          // 短信价格定时同步 | ||||||
| 		// ... | 		// ... | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -7,6 +7,7 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| type SmsJob struct{} | type SmsJob struct{} | ||||||
|  | type SmsPriceJob struct{} | ||||||
|  |  | ||||||
| // 定时查询结果 | // 定时查询结果 | ||||||
| func (j SmsJob) Exec(args interface{}) error { | func (j SmsJob) Exec(args interface{}) error { | ||||||
| @ -16,3 +17,17 @@ func (j SmsJob) Exec(args interface{}) error { | |||||||
|  |  | ||||||
| 	return phoneService.SyncCodes() | 	return phoneService.SyncCodes() | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // 定时同步价格 | ||||||
|  | func (j SmsPriceJob) Exec(args interface{}) error { | ||||||
|  | 	smsService := service.SmsServices{} | ||||||
|  | 	smsService.Orm = GetDb() | ||||||
|  | 	smsService.Log = logger.NewHelper(logger.DefaultLogger) | ||||||
|  |  | ||||||
|  | 	// 同步价格 | ||||||
|  | 	if err := smsService.SyncPrices(); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | |||||||
| @ -25,3 +25,13 @@ func TestSmsRenew(t *testing.T) { | |||||||
| 		t.Error(err) | 		t.Error(err) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestSmsPriceJob(t *testing.T) { | ||||||
|  | 	initSetting() | ||||||
|  | 	config.ExtConfig.DaisysmsUrl = "https://daisysms.com/stubs/handler_api.php" | ||||||
|  | 	job := SmsPriceJob{} | ||||||
|  |  | ||||||
|  | 	if err := job.Exec(nil); err != nil { | ||||||
|  | 		t.Error(err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | |||||||
| @ -5,8 +5,9 @@ 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/config" | 	"go-admin/config" | ||||||
|  | 	"go-admin/utils/redishelper" | ||||||
| 	"go-admin/utils/utility" | 	"go-admin/utils/utility" | ||||||
| 	"io/ioutil" | 	"io" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| @ -26,6 +27,19 @@ const ( | |||||||
|  |  | ||||||
| // trx 链上支付定时查询 | // trx 链上支付定时查询 | ||||||
| func (j TrxPaymentJob) Exec(arg interface{}) error { | func (j TrxPaymentJob) Exec(arg interface{}) error { | ||||||
|  | 	key := fmt.Sprintf("pre_order:%s", "*") | ||||||
|  | 	keys, err := redishelper.DefaultRedis.ScanKeys(key) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		logger.Error("查询redis key失败", err) | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if len(keys) == 0 { | ||||||
|  | 		logger.Info("没有待处理订单") | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	configService := service.SysConfig{} | 	configService := service.SysConfig{} | ||||||
| 	configService.Orm = GetDb() | 	configService.Orm = GetDb() | ||||||
| 	req := dto.SysConfigByKeyReq{} | 	req := dto.SysConfigByKeyReq{} | ||||||
| @ -82,19 +96,31 @@ func (j TrxPaymentJob) Exec(arg interface{}) error { | |||||||
| // GetTRC20Transfers 获取指定 TRC20 代币的交易记录 | // GetTRC20Transfers 获取指定 TRC20 代币的交易记录 | ||||||
| func GetTRC20Transfers(contractAddress, accountAddress string, minTimestamp, maxTimestamp int64) ([]dto.TRC20Transfer, error) { | func GetTRC20Transfers(contractAddress, accountAddress string, minTimestamp, maxTimestamp int64) ([]dto.TRC20Transfer, error) { | ||||||
| 	url := fmt.Sprintf("%s/v1/accounts/%s/transactions/trc20?contract_address=%s", config.ExtConfig.TrxGridUrl, accountAddress, contractAddress) | 	url := fmt.Sprintf("%s/v1/accounts/%s/transactions/trc20?contract_address=%s", config.ExtConfig.TrxGridUrl, accountAddress, contractAddress) | ||||||
|  |  | ||||||
| 	if minTimestamp > 0 { | 	if minTimestamp > 0 { | ||||||
| 		url += fmt.Sprintf("&min_timestamp=%d", minTimestamp) | 		url += fmt.Sprintf("&min_timestamp=%d", minTimestamp) | ||||||
| 	} | 	} | ||||||
| 	if maxTimestamp > 0 { | 	if maxTimestamp > 0 { | ||||||
| 		url += fmt.Sprintf("&max_timestamp=%d", maxTimestamp) | 		url += fmt.Sprintf("&max_timestamp=%d", maxTimestamp) | ||||||
| 	} | 	} | ||||||
| 	resp, err := http.Get(url) | 	// logger.Info("查询地址:", url) | ||||||
|  | 	req, err := http.NewRequest("GET", url, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, fmt.Errorf("failed to create request: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 设置请求头(包含 TronGrid API Key) | ||||||
|  | 	req.Header.Set("Accept", "*/*") | ||||||
|  | 	req.Header.Set("TRON-PRO-API-KEY", config.ExtConfig.TronApiKey) // 从配置读取 API Key | ||||||
|  |  | ||||||
|  | 	client := &http.Client{} | ||||||
|  | 	resp, err := client.Do(req) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, fmt.Errorf("failed to send request: %v", err) | 		return nil, fmt.Errorf("failed to send request: %v", err) | ||||||
| 	} | 	} | ||||||
| 	defer resp.Body.Close() | 	defer resp.Body.Close() | ||||||
|  |  | ||||||
| 	body, err := ioutil.ReadAll(resp.Body) | 	body, err := io.ReadAll(resp.Body) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, fmt.Errorf("failed to read response body: %v", err) | 		return nil, fmt.Errorf("failed to read response body: %v", err) | ||||||
| 	} | 	} | ||||||
| @ -102,6 +128,7 @@ func GetTRC20Transfers(contractAddress, accountAddress string, minTimestamp, max | |||||||
| 	var result struct { | 	var result struct { | ||||||
| 		Data []dto.TRC20Transfer `json:"data"` | 		Data []dto.TRC20Transfer `json:"data"` | ||||||
| 	} | 	} | ||||||
|  | 	// logger.Info("查询结果:", string(body)) | ||||||
| 	if err := sonic.Unmarshal(body, &result); err != nil { | 	if err := sonic.Unmarshal(body, &result); err != nil { | ||||||
| 		return nil, fmt.Errorf("failed to unmarshal response: %v", err) | 		return nil, fmt.Errorf("failed to unmarshal response: %v", err) | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -27,6 +27,10 @@ func initSetting() { | |||||||
| 	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" | ||||||
|  | 	config.ExtConfig.DaisysmsUrl = "https://daisysms.com/stubs/handler_api.php" | ||||||
|  | 	config.ExtConfig.SmsTextVerified.ApiKey = "ZQ0swXnsaPpeGdwa3c7gT9U9I1Oh9WoDHx0amuYovvaHuqd5u6B4NBBUSUBjR" | ||||||
|  | 	config.ExtConfig.SmsTextVerified.UserName = "webspan@proton.me" | ||||||
|  | 	config.ExtConfig.SmsTextVerified.Url = "https://www.textverified.com" | ||||||
| 	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") | ||||||
| } | } | ||||||
|  | |||||||
| @ -186,9 +186,8 @@ func initRouter() { | |||||||
| 	if config.SslConfig.Enable { | 	if config.SslConfig.Enable { | ||||||
| 		r.Use(handler.TlsHandler()) | 		r.Use(handler.TlsHandler()) | ||||||
| 	} | 	} | ||||||
| 	//r.Use(middleware.Metrics()) | 	//r.Use(middleware.Metrics()).Use(common.Sentinel()). | ||||||
| 	r.Use(common.Sentinel()). | 	r.Use(common.RequestId(pkg.TrafficKey)). | ||||||
| 		Use(common.RequestId(pkg.TrafficKey)). |  | ||||||
| 		Use(api.SetRequestLogger) | 		Use(api.SetRequestLogger) | ||||||
|  |  | ||||||
| 	common.InitMiddleware(r) | 	common.InitMiddleware(r) | ||||||
| @ -218,4 +217,10 @@ func initBusinesses() { | |||||||
| 	if _, err := cliProxyService.GetTrafficInfo(); err != nil { | 	if _, err := cliProxyService.GetTrafficInfo(); err != nil { | ||||||
| 		os.Exit(-1) | 		os.Exit(-1) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	memberApiService := service.MemberApi{} | ||||||
|  | 	memberApiService.Orm = cliProxyService.Orm | ||||||
|  | 	memberApiService.Log = cliProxyService.Log | ||||||
|  |  | ||||||
|  | 	memberApiService.InitApis() | ||||||
| } | } | ||||||
|  | |||||||
| @ -2,9 +2,16 @@ package global | |||||||
|  |  | ||||||
| //ConfigKey | //ConfigKey | ||||||
| const ( | const ( | ||||||
| 	TrafficProxyEffectiveDay         = "traffic_proxy_effective_day"          //流量代理有效天数 | 	//流量代理有效天数 | ||||||
| 	LongNumberRenewDeductionStandard = "long_number_renew_deduction_standard" //长效号码续费标准 | 	TrafficProxyEffectiveDay = "traffic_proxy_effective_day" | ||||||
| 	IPRenewDeductionStandard         = "ip_renew_deduction_standard"          //长效IP续费kk扣除标准 | 	//短期号码扣除标准 | ||||||
|  | 	ShortNumberDeductionStandard = "number_fee_short_term" | ||||||
|  | 	//长效号码扣除标准 | ||||||
|  | 	LongNumberDeductionStandard = "number_fee_long_term" | ||||||
|  | 	//长效号码续费标准 | ||||||
|  | 	LongNumberRenewDeductionStandard = "long_number_renew_deduction_standard" | ||||||
|  | 	//长效IP续费kk扣除标准 | ||||||
|  | 	IPRenewDeductionStandard = "ip_renew_deduction_standard" | ||||||
|  |  | ||||||
| 	ProxyExpiryTime = "proxy_expiry_time" //代理过期记录保留时长 | 	ProxyExpiryTime = "proxy_expiry_time" //代理过期记录保留时长 | ||||||
| 	SmsExpiryTime   = "sms_expiry_time"   //接码过期记录保留时长 | 	SmsExpiryTime   = "sms_expiry_time"   //接码过期记录保留时长 | ||||||
|  | |||||||
							
								
								
									
										7
									
								
								common/global/sms_services.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								common/global/sms_services.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | |||||||
|  | package global | ||||||
|  |  | ||||||
|  | //短效平台code 字典【sms_platform】 | ||||||
|  | const ( | ||||||
|  | 	SmsPlatformDaisysms     = "daisysms" | ||||||
|  | 	SmsPlatformTextVerified = "textverified" | ||||||
|  | ) | ||||||
							
								
								
									
										6
									
								
								common/global/text_verified.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								common/global/text_verified.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | |||||||
|  | package global | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	// TextVerified Token | ||||||
|  | 	TextVerifiedToken = "TextVerifiedToken" | ||||||
|  | ) | ||||||
| @ -1,13 +1,28 @@ | |||||||
| package middleware | package middleware | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"strconv" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/go-admin-team/go-admin-core/sdk/config" | 	"go-admin/app/admin/models" | ||||||
| 	jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth" |  | ||||||
| 	"go-admin/common/middleware/handler" | 	"go-admin/common/middleware/handler" | ||||||
|  | 	"go-admin/common/rediskey" | ||||||
|  | 	"go-admin/common/statuscode" | ||||||
|  | 	"go-admin/utils/redishelper" | ||||||
|  |  | ||||||
|  | 	"github.com/bytedance/sonic" | ||||||
|  | 	"github.com/gin-gonic/gin" | ||||||
|  | 	"github.com/go-admin-team/go-admin-core/sdk/config" | ||||||
|  | 	"github.com/go-admin-team/go-admin-core/sdk/pkg" | ||||||
|  | 	jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | var ErrQueryUserId = errors.New("查询用户失败") | ||||||
|  | var ErrNoAccount = errors.New("没有API用户") | ||||||
|  | var ErrApiUnActived = errors.New("API未激活") | ||||||
|  |  | ||||||
| // AuthInit jwt验证new | // AuthInit jwt验证new | ||||||
| func AuthInit() (*jwt.GinJWTMiddleware, error) { | func AuthInit() (*jwt.GinJWTMiddleware, error) { | ||||||
| 	timeout := time.Hour | 	timeout := time.Hour | ||||||
| @ -33,4 +48,108 @@ func AuthInit() (*jwt.GinJWTMiddleware, error) { | |||||||
| 		TimeFunc:        time.Now, | 		TimeFunc:        time.Now, | ||||||
| 	}) | 	}) | ||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // apikey授权认证 | ||||||
|  | func FrontedAuth(c *gin.Context) { | ||||||
|  | 	// 从请求头中获取 token 和 os | ||||||
|  | 	apikey := c.GetHeader("x-api-key") | ||||||
|  | 	// 如果 token 不存在,返回未登录的状态 | ||||||
|  | 	if len(apikey) == 0 { | ||||||
|  | 		err := ResponseWithStatus(c, statuscode.Unauthorized) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		c.Abort() // 停止后续中间件的执行 | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	// 验证 token 并获取结果 | ||||||
|  | 	key := fmt.Sprintf(rediskey.MemberApiKey, apikey) | ||||||
|  | 	val, err := redishelper.DefaultRedis.GetString(key) | ||||||
|  |  | ||||||
|  | 	if err != nil || val == "" { | ||||||
|  | 		ResponseWithStatus(c, statuscode.Unauthorized) | ||||||
|  | 		c.Abort() // 停止后续中间件的执行 | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 将解析后的 token 设置到请求头中 | ||||||
|  | 	c.Set("apiKey", apikey) | ||||||
|  | 	// 继续处理请求 | ||||||
|  | 	c.Next() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ResponseWithStatus 带状态的响应 | ||||||
|  | func ResponseWithStatus(ctx *gin.Context, code int, data ...interface{}) error { | ||||||
|  | 	resp := statuscode.Response{ | ||||||
|  | 		Code: code, | ||||||
|  | 		Msg:  "un authorized", | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// resp.RequestID = pkg.GenerateMsgIDFromContext(ctx) | ||||||
|  | 	if len(data) > 0 { | ||||||
|  | 		resp.Data = data[0] | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	switch code { | ||||||
|  | 	case 401, 500, 405, 404: | ||||||
|  | 		ctx.JSON(code, resp) | ||||||
|  | 	default: | ||||||
|  | 		ctx.JSON(200, resp) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 获取ApiKey用户id | ||||||
|  | func GetUserIdWithApiKey(ctx *gin.Context) (int, error) { | ||||||
|  | 	apikey, ok := ctx.Get("apiKey") | ||||||
|  | 	if !ok { | ||||||
|  | 		return 0, errors.New("apiKey not found") | ||||||
|  | 	} | ||||||
|  | 	return strconv.Atoi(apikey.(string)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 获取用户id 根据ApiKey | ||||||
|  | func GetUserIdByApiKey(c *gin.Context) (int, error) { | ||||||
|  | 	apiKey, ok := c.Get("apiKey") | ||||||
|  | 	if !ok { | ||||||
|  | 		return 0, errors.New("apiKey not found") | ||||||
|  | 	} | ||||||
|  | 	db, err := pkg.GetOrm(c) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		return 0, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var userId int | ||||||
|  | 	val, err := redishelper.DefaultRedis.GetString(fmt.Sprintf(rediskey.MemberApiKey, apiKey)) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		return userId, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if val == "" { | ||||||
|  | 		var user models.MemberApi | ||||||
|  |  | ||||||
|  | 		if dbErr := db.Model(&user).Where("api_key = ?", apiKey).First(&user).Error; dbErr != nil { | ||||||
|  | 			return userId, ErrNoAccount | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if user.Status != 1 { | ||||||
|  | 			return userId, ErrApiUnActived | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		userId = user.UserId | ||||||
|  | 	} else { | ||||||
|  | 		data := models.MemberApi{} | ||||||
|  | 		err = sonic.UnmarshalString(val, &data) | ||||||
|  |  | ||||||
|  | 		if err != nil { | ||||||
|  | 			return userId, err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		userId = data.UserId | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return userId, nil | ||||||
|  | } | ||||||
|  | |||||||
							
								
								
									
										6
									
								
								common/rediskey/member_api.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								common/rediskey/member_api.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | |||||||
|  | package rediskey | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	//用户api | ||||||
|  | 	MemberApiKey = "member_api:%s" | ||||||
|  | ) | ||||||
| @ -2,6 +2,14 @@ package statuscode | |||||||
|  |  | ||||||
| import "fmt" | import "fmt" | ||||||
|  |  | ||||||
|  | type Response struct { | ||||||
|  | 	Status    int         `json:"status"` | ||||||
|  | 	Code      int         `json:"code"` | ||||||
|  | 	Msg       string      `json:"msg"` | ||||||
|  | 	Data      interface{} `json:"data"` | ||||||
|  | 	RequestID string      `json:"RequestId"` | ||||||
|  | } | ||||||
|  |  | ||||||
| var StatusCodeZh = map[int]string{ | var StatusCodeZh = map[int]string{ | ||||||
| 	Success:                "成功", | 	Success:                "成功", | ||||||
| 	AccountExisted:         "账号已存在", | 	AccountExisted:         "账号已存在", | ||||||
| @ -17,15 +25,22 @@ var StatusCodeZh = map[int]string{ | |||||||
| 	MaxPriceExceeded:       "超过最大接受单价", | 	MaxPriceExceeded:       "超过最大接受单价", | ||||||
| 	NoNumbers:              "号码不足", | 	NoNumbers:              "号码不足", | ||||||
| 	RentalsNotFinished:     "需要先完成部分租赁才能继续租赁", | 	RentalsNotFinished:     "需要先完成部分租赁才能继续租赁", | ||||||
|  | 	Unauthorized:           "未授权", | ||||||
|  | 	ApiUnActived:           "API未激活", | ||||||
|  | 	InvalidParams:          "参数错误", | ||||||
|  |  | ||||||
| 	SmsCancel:             "短信验证码_手机号过期", | 	SmsCancel:                   "短信验证码_手机号过期", | ||||||
| 	SmsNoActivation:       "短信验证码_手机号不存在", | 	SmsNoActivation:             "短信验证码_手机号不存在", | ||||||
| 	SmsWaitCode:           "短信验证码_等待验证码", | 	SmsWaitCode:                 "短信验证码_等待验证码", | ||||||
| 	SmsLongNumWaitCode:    "短信验证码_长效号码已唤醒", | 	SmsLongNumWaitCode:          "短信验证码_长效号码已唤醒", | ||||||
| 	SmsNotExisted:         "号码不存在", | 	SmsNotExisted:               "号码不存在", | ||||||
| 	SmsNotExpired:         "号码未过期无法删除", | 	SmsNotExpired:               "号码未过期无法删除", | ||||||
| 	SmsNotAutoRenew:       "短效号码无法自动续期", | 	SmsNotAutoRenew:             "短效号码无法自动续期", | ||||||
| 	SmsServiceUnavailable: "%s服务暂不可用", | 	SmsServiceUnavailable:       "%s服务暂不可用", | ||||||
|  | 	SmsPlatformUnavailable:      "通道不可用", | ||||||
|  | 	SmsInvalidType:              "短信验证码_无效类型", | ||||||
|  | 	SmsOutOfStockOrUnavailable:  "短信验证码_缺货或服务不可用", | ||||||
|  | 	SmsRentalRefundNotPermitted: "短信验证码_租赁退款不允许", | ||||||
| } | } | ||||||
|  |  | ||||||
| var StatusCodeEn = map[int]string{ | var StatusCodeEn = map[int]string{ | ||||||
| @ -43,15 +58,22 @@ var StatusCodeEn = map[int]string{ | |||||||
| 	MaxPriceExceeded:       "max price exceeded", | 	MaxPriceExceeded:       "max price exceeded", | ||||||
| 	NoNumbers:              "no numbers", | 	NoNumbers:              "no numbers", | ||||||
| 	RentalsNotFinished:     "need to finish some rentals before renting more", | 	RentalsNotFinished:     "need to finish some rentals before renting more", | ||||||
|  | 	Unauthorized:           "un authorized", | ||||||
|  | 	ApiUnActived:           "API un actived.", | ||||||
|  | 	InvalidParams:          "invalid params", | ||||||
|  |  | ||||||
| 	SmsCancel:             "sms code expired", | 	SmsCancel:                   "sms code expired", | ||||||
| 	SmsNoActivation:       "sms code not exist", | 	SmsNoActivation:             "sms code not exist", | ||||||
| 	SmsWaitCode:           "sms code wait for input", | 	SmsWaitCode:                 "sms code wait for input", | ||||||
| 	SmsLongNumWaitCode:    "sms code long num wake up", | 	SmsLongNumWaitCode:          "sms code long num wake up", | ||||||
| 	SmsNotExisted:         "number not exist", | 	SmsNotExisted:               "number not exist", | ||||||
| 	SmsNotExpired:         "number not expired, can not delete", | 	SmsNotExpired:               "number not expired, can not delete", | ||||||
| 	SmsNotAutoRenew:       "num can not auto renew", | 	SmsNotAutoRenew:             "num can not auto renew", | ||||||
| 	SmsServiceUnavailable: "%s service unavailable", | 	SmsServiceUnavailable:       "%s service unavailable", | ||||||
|  | 	SmsPlatformUnavailable:      "channel unavailable", | ||||||
|  | 	SmsInvalidType:              "sms type invalid", | ||||||
|  | 	SmsOutOfStockOrUnavailable:  "sms out of stock or unavailable", | ||||||
|  | 	SmsRentalRefundNotPermitted: "sms rental refund not permitted", | ||||||
| } | } | ||||||
|  |  | ||||||
| // GetMsg 获取状态码对应的消息 | // GetMsg 获取状态码对应的消息 | ||||||
| @ -108,6 +130,12 @@ const ( | |||||||
| 	NoNumbers = 10012 | 	NoNumbers = 10012 | ||||||
| 	// Need to finish some rentals before renting more | 	// Need to finish some rentals before renting more | ||||||
| 	RentalsNotFinished = 10013 | 	RentalsNotFinished = 10013 | ||||||
|  | 	// 未授权 | ||||||
|  | 	Unauthorized = 10014 | ||||||
|  | 	// Api未激活 | ||||||
|  | 	ApiUnActived = 10015 | ||||||
|  | 	//参数错误 | ||||||
|  | 	InvalidParams = 10016 | ||||||
|  |  | ||||||
| 	//短信验证码_手机号过期 | 	//短信验证码_手机号过期 | ||||||
| 	SmsCancel = 20014 | 	SmsCancel = 20014 | ||||||
| @ -125,4 +153,12 @@ const ( | |||||||
| 	SmsNotAutoRenew = 20020 | 	SmsNotAutoRenew = 20020 | ||||||
| 	//短信验证码_服务暂不可用 | 	//短信验证码_服务暂不可用 | ||||||
| 	SmsServiceUnavailable = 20021 | 	SmsServiceUnavailable = 20021 | ||||||
|  | 	//短信-通道不可用 | ||||||
|  | 	SmsPlatformUnavailable = 20022 | ||||||
|  | 	//短信-未知类型 | ||||||
|  | 	SmsInvalidType = 20023 | ||||||
|  | 	// 短信-缺货或服务不可用 | ||||||
|  | 	SmsOutOfStockOrUnavailable = 20024 | ||||||
|  | 	// 短信-租赁退款不允许 | ||||||
|  | 	SmsRentalRefundNotPermitted = 20025 | ||||||
| ) | ) | ||||||
|  | |||||||
| @ -10,11 +10,19 @@ var ExtConfig Extend | |||||||
| // | // | ||||||
| // 使用方法: config.ExtConfig......即可!! | // 使用方法: config.ExtConfig......即可!! | ||||||
| type Extend struct { | type Extend struct { | ||||||
| 	AMap           AMap // 这里配置对应配置文件的结构即可 | 	AMap            AMap // 这里配置对应配置文件的结构即可 | ||||||
| 	TrxGridUrl     string | 	TrxGridUrl      string | ||||||
| 	CliproxyUrl    string //cliproxy服务地址 | 	TronApiKey      string          `yaml:"tronApiKey"` | ||||||
| 	CliproxyApiUrl string //cliproxy api地址 | 	CliproxyUrl     string          //cliproxy服务地址 | ||||||
| 	DaisysmsUrl    string //daisysms服务地址 | 	CliproxyApiUrl  string          //cliproxy api地址 | ||||||
|  | 	DaisysmsUrl     string          //daisysms服务地址 | ||||||
|  | 	SmsTextVerified SmsTextVerified `yaml:"smsTextVerified"` //短信验证服务配置 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type SmsTextVerified struct { | ||||||
|  | 	Url      string `yaml:"url"` | ||||||
|  | 	UserName string `yaml:"userName"` | ||||||
|  | 	ApiKey   string `yaml:"apiKey"` | ||||||
| } | } | ||||||
|  |  | ||||||
| type AMap struct { | type AMap struct { | ||||||
|  | |||||||
| @ -51,12 +51,19 @@ settings: | |||||||
|      |      | ||||||
|     #trx api |     #trx api | ||||||
|     trxGridUrl: "https://api.trongrid.io" |     trxGridUrl: "https://api.trongrid.io" | ||||||
|  |     tronApiKey: "223c129e-73f5-470f-9464-f9969846c134" | ||||||
|     #cliproxy url |     #cliproxy url | ||||||
|     cliproxyUrl: "https://f.cliproxy.com" |     cliproxyUrl: "https://f.cliproxy.com"  | ||||||
|     #cliproxy api url |     #cliproxy api url | ||||||
|     cliproxyApiUrl: "https://api.cliproxy.com" |     cliproxyApiUrl: "https://api.cliproxy.com" | ||||||
|     #daisysms api url |     #daisysms api url | ||||||
|     daisysmsUrl: "https://daisysms.com/stubs/handler_api.php" |     daisysmsUrl: "https://daisysms.com/stubs/handler_api.php" | ||||||
|  |     #sms text_verified | ||||||
|  |     smsTextVerified: | ||||||
|  |       url: "https://www.textverified.com" | ||||||
|  |       userName: "webspan@proton.me" | ||||||
|  |       apiKey: "ZQ0swXnsaPpeGdwa3c7gT9U9I1Oh9WoDHx0amuYovvaHuqd5u6B4NBBUSUBjR" | ||||||
|  |  | ||||||
|   cache: |   cache: | ||||||
|     redis: |     redis: | ||||||
|       addr: 127.0.0.1:6379 |       addr: 127.0.0.1:6379 | ||||||
|  | |||||||
							
								
								
									
										6
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								go.mod
									
									
									
									
									
								
							| @ -1,6 +1,6 @@ | |||||||
| module go-admin | module go-admin | ||||||
|  |  | ||||||
| go 1.18 | go 1.24 | ||||||
|  |  | ||||||
| require ( | require ( | ||||||
| 	github.com/alibaba/sentinel-golang v1.0.4 | 	github.com/alibaba/sentinel-golang v1.0.4 | ||||||
| @ -14,8 +14,10 @@ require ( | |||||||
| 	github.com/go-admin-team/go-admin-core v1.4.1-0.20220809101213-21187928f7d9 | 	github.com/go-admin-team/go-admin-core v1.4.1-0.20220809101213-21187928f7d9 | ||||||
| 	github.com/go-admin-team/go-admin-core/sdk v1.4.1-0.20220809101213-21187928f7d9 | 	github.com/go-admin-team/go-admin-core/sdk v1.4.1-0.20220809101213-21187928f7d9 | ||||||
| 	github.com/go-redis/redis/v8 v8.11.5 | 	github.com/go-redis/redis/v8 v8.11.5 | ||||||
|  | 	github.com/golang-jwt/jwt v3.2.2+incompatible | ||||||
| 	github.com/google/uuid v1.3.0 | 	github.com/google/uuid v1.3.0 | ||||||
| 	github.com/huaweicloud/huaweicloud-sdk-go-obs v3.21.12+incompatible | 	github.com/huaweicloud/huaweicloud-sdk-go-obs v3.21.12+incompatible | ||||||
|  | 	github.com/jinzhu/copier v0.4.0 | ||||||
| 	github.com/mssola/user_agent v0.5.2 | 	github.com/mssola/user_agent v0.5.2 | ||||||
| 	github.com/opentracing/opentracing-go v1.1.0 | 	github.com/opentracing/opentracing-go v1.1.0 | ||||||
| 	github.com/pkg/errors v0.9.1 | 	github.com/pkg/errors v0.9.1 | ||||||
| @ -77,7 +79,6 @@ require ( | |||||||
| 	github.com/go-redis/redis/v7 v7.4.0 // indirect | 	github.com/go-redis/redis/v7 v7.4.0 // indirect | ||||||
| 	github.com/go-sql-driver/mysql v1.6.0 // indirect | 	github.com/go-sql-driver/mysql v1.6.0 // indirect | ||||||
| 	github.com/goccy/go-json v0.10.2 // indirect | 	github.com/goccy/go-json v0.10.2 // indirect | ||||||
| 	github.com/golang-jwt/jwt v3.2.2+incompatible // indirect |  | ||||||
| 	github.com/golang-jwt/jwt/v4 v4.4.2 // indirect | 	github.com/golang-jwt/jwt/v4 v4.4.2 // indirect | ||||||
| 	github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect | 	github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect | ||||||
| 	github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188 // indirect | 	github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188 // indirect | ||||||
| @ -97,7 +98,6 @@ require ( | |||||||
| 	github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect | 	github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect | ||||||
| 	github.com/jackc/pgtype v1.11.0 // indirect | 	github.com/jackc/pgtype v1.11.0 // indirect | ||||||
| 	github.com/jackc/pgx/v4 v4.16.1 // indirect | 	github.com/jackc/pgx/v4 v4.16.1 // indirect | ||||||
| 	github.com/jinzhu/copier v0.4.0 // indirect |  | ||||||
| 	github.com/jinzhu/inflection v1.0.0 // indirect | 	github.com/jinzhu/inflection v1.0.0 // indirect | ||||||
| 	github.com/jinzhu/now v1.1.5 // indirect | 	github.com/jinzhu/now v1.1.5 // indirect | ||||||
| 	github.com/josharian/intern v1.0.0 // indirect | 	github.com/josharian/intern v1.0.0 // indirect | ||||||
|  | |||||||
| @ -50,13 +50,13 @@ func (c *HTTPClient) applyHeaders(req *http.Request, customHeaders map[string]st | |||||||
| // customHeaders: 自定义请求头,将覆盖默认请求头 | // customHeaders: 自定义请求头,将覆盖默认请求头 | ||||||
| // responseData: 用于存储响应数据的目标结构体(指针类型),如果为 nil 则表示不需要 JSON 解码 | // responseData: 用于存储响应数据的目标结构体(指针类型),如果为 nil 则表示不需要 JSON 解码 | ||||||
| // rawResponse: 用于存储原始响应体字节切片(*[]byte),如果为 nil 则表示不需要原始响应 | // rawResponse: 用于存储原始响应体字节切片(*[]byte),如果为 nil 则表示不需要原始响应 | ||||||
| func (c *HTTPClient) doRequest( | func (c *HTTPClient) DoRequest( | ||||||
| 	method, path string, | 	method, path string, | ||||||
| 	requestBody interface{}, | 	requestBody interface{}, | ||||||
| 	customHeaders map[string]string, | 	customHeaders map[string]string, | ||||||
| 	responseData interface{}, | 	responseData interface{}, | ||||||
| 	rawResponse *[]byte, // 新增参数:指向字节切片的指针,用于存储原始响应 | 	rawResponse *[]byte, // 新增参数:指向字节切片的指针,用于存储原始响应 | ||||||
| ) error { | ) (int, error) { | ||||||
| 	// 拼接完整的 URL | 	// 拼接完整的 URL | ||||||
| 	url := c.BaseURL + path | 	url := c.BaseURL + path | ||||||
|  |  | ||||||
| @ -65,7 +65,7 @@ func (c *HTTPClient) doRequest( | |||||||
| 		// 将请求体编码为 JSON | 		// 将请求体编码为 JSON | ||||||
| 		jsonBody, err := json.Marshal(requestBody) | 		jsonBody, err := json.Marshal(requestBody) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return fmt.Errorf("json marshal request body failed: %w", err) | 			return -1, fmt.Errorf("json marshal request body failed: %w", err) | ||||||
| 		} | 		} | ||||||
| 		reqBodyReader = bytes.NewBuffer(jsonBody) | 		reqBodyReader = bytes.NewBuffer(jsonBody) | ||||||
| 	} | 	} | ||||||
| @ -73,7 +73,7 @@ func (c *HTTPClient) doRequest( | |||||||
| 	// 创建新的 HTTP 请求 | 	// 创建新的 HTTP 请求 | ||||||
| 	req, err := http.NewRequest(method, url, reqBodyReader) | 	req, err := http.NewRequest(method, url, reqBodyReader) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return fmt.Errorf("create http request failed: %w", err) | 		return -1, fmt.Errorf("create http request failed: %w", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// 应用请求头 | 	// 应用请求头 | ||||||
| @ -85,14 +85,14 @@ func (c *HTTPClient) doRequest( | |||||||
| 	// 发送请求 | 	// 发送请求 | ||||||
| 	resp, err := c.Client.Do(req) | 	resp, err := c.Client.Do(req) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return fmt.Errorf("send http request failed: %w", err) | 		return -1, fmt.Errorf("send http request failed: %w", err) | ||||||
| 	} | 	} | ||||||
| 	defer resp.Body.Close() | 	defer resp.Body.Close() | ||||||
|  |  | ||||||
| 	// 检查 HTTP 状态码 | 	// 检查 HTTP 状态码 | ||||||
| 	if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusBadRequest { | 	if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusBadRequest { | ||||||
| 		bodyBytes, _ := io.ReadAll(resp.Body) // 读取错误响应体 | 		bodyBytes, _ := io.ReadAll(resp.Body) // 读取错误响应体 | ||||||
| 		return fmt.Errorf("http request failed with status: %d, body: %s", resp.StatusCode, string(bodyBytes)) | 		return resp.StatusCode, fmt.Errorf("http request failed with status: %d, body: %s", resp.StatusCode, string(bodyBytes)) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// 解码响应(支持 gzip) | 	// 解码响应(支持 gzip) | ||||||
| @ -100,7 +100,7 @@ func (c *HTTPClient) doRequest( | |||||||
| 	if resp.Header.Get("Content-Encoding") == "gzip" { | 	if resp.Header.Get("Content-Encoding") == "gzip" { | ||||||
| 		gzipReader, err := gzip.NewReader(resp.Body) | 		gzipReader, err := gzip.NewReader(resp.Body) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return fmt.Errorf("create gzip reader failed: %w", err) | 			return http.StatusOK, fmt.Errorf("create gzip reader failed: %w", err) | ||||||
| 		} | 		} | ||||||
| 		defer gzipReader.Close() | 		defer gzipReader.Close() | ||||||
| 		reader = gzipReader | 		reader = gzipReader | ||||||
| @ -109,7 +109,7 @@ func (c *HTTPClient) doRequest( | |||||||
| 	// 首先读取整个响应体,然后决定如何处理 | 	// 首先读取整个响应体,然后决定如何处理 | ||||||
| 	bodyBytes, err := io.ReadAll(reader) | 	bodyBytes, err := io.ReadAll(reader) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return fmt.Errorf("read response body failed: %w", err) | 		return http.StatusOK, fmt.Errorf("read response body failed: %w", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// 如果提供了原始响应目标,则填充它 | 	// 如果提供了原始响应目标,则填充它 | ||||||
| @ -121,19 +121,19 @@ func (c *HTTPClient) doRequest( | |||||||
| 	if responseData != nil { | 	if responseData != nil { | ||||||
| 		err = json.Unmarshal(bodyBytes, responseData) // 直接对字节切片使用 Unmarshal | 		err = json.Unmarshal(bodyBytes, responseData) // 直接对字节切片使用 Unmarshal | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return fmt.Errorf("json decode response body failed: %w", err) | 			return http.StatusOK, fmt.Errorf("json decode response body failed: %w", err) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return nil | 	return http.StatusOK, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // Get 发送 GET 请求 | // Get 发送 GET 请求 | ||||||
| // path: 请求路径 | // path: 请求路径 | ||||||
| // customHeaders: 自定义请求头 | // customHeaders: 自定义请求头 | ||||||
| // responseData: 用于存储响应数据的目标结构体(指针类型) | // responseData: 用于存储响应数据的目标结构体(指针类型) | ||||||
| func (c *HTTPClient) Get(path string, customHeaders map[string]string, responseData interface{}) error { | func (c *HTTPClient) Get(path string, customHeaders map[string]string, responseData interface{}) (int, error) { | ||||||
| 	return c.doRequest(http.MethodGet, path, nil, customHeaders, responseData, nil) // rawResponse 传递 nil | 	return c.DoRequest(http.MethodGet, path, nil, customHeaders, responseData, nil) // rawResponse 传递 nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // Post 发送 POST 请求 | // Post 发送 POST 请求 | ||||||
| @ -141,8 +141,8 @@ func (c *HTTPClient) Get(path string, customHeaders map[string]string, responseD | |||||||
| // requestBody: 请求体数据 | // requestBody: 请求体数据 | ||||||
| // customHeaders: 自定义请求头 | // customHeaders: 自定义请求头 | ||||||
| // responseData: 用于存储响应数据的目标结构体(指针类型) | // responseData: 用于存储响应数据的目标结构体(指针类型) | ||||||
| func (c *HTTPClient) Post(path string, requestBody interface{}, customHeaders map[string]string, responseData interface{}) error { | func (c *HTTPClient) Post(path string, requestBody interface{}, customHeaders map[string]string, responseData interface{}) (int, error) { | ||||||
| 	return c.doRequest(http.MethodPost, path, requestBody, customHeaders, responseData, nil) // rawResponse 传递 nil | 	return c.DoRequest(http.MethodPost, path, requestBody, customHeaders, responseData, nil) // rawResponse 传递 nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // PostWithContentType 发送 POST 请求,支持自定义 Content-Type(如 application/json 或 multipart/form-data) | // PostWithContentType 发送 POST 请求,支持自定义 Content-Type(如 application/json 或 multipart/form-data) | ||||||
| @ -232,17 +232,19 @@ func (c *HTTPClient) PostWithContentType(path string, requestBody interface{}, c | |||||||
| // requestBody: 请求体数据 | // requestBody: 请求体数据 | ||||||
| // customHeaders: 自定义请求头 | // customHeaders: 自定义请求头 | ||||||
| // responseData: 用于存储响应数据的目标结构体(指针类型) | // responseData: 用于存储响应数据的目标结构体(指针类型) | ||||||
| func (c *HTTPClient) Put(path string, requestBody interface{}, customHeaders map[string]string, responseData interface{}) error { | // returns 状态码 和 错误信息 | ||||||
| 	return c.doRequest(http.MethodPut, path, requestBody, customHeaders, responseData, nil) // rawResponse 传递 nil | func (c *HTTPClient) Put(path string, requestBody interface{}, customHeaders map[string]string, responseData interface{}) (int, error) { | ||||||
|  | 	return c.DoRequest(http.MethodPut, path, requestBody, customHeaders, responseData, nil) // rawResponse 传递 nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // Delete 发送 DELETE 请求 | // Delete 发送 DELETE 请求 | ||||||
| // path: 请求路径 | // path: 请求路径 | ||||||
| // customHeaders: 自定义请求头 | // customHeaders: 自定义请求头 | ||||||
| // responseData: 用于存储响应数据的目标结构体(指针类型) | // responseData: 用于存储响应数据的目标结构体(指针类型) | ||||||
| func (c *HTTPClient) Delete(path string, customHeaders map[string]string, responseData interface{}) error { | // returns 状态码 和 错误信息 | ||||||
|  | func (c *HTTPClient) Delete(path string, customHeaders map[string]string, responseData interface{}) (int, error) { | ||||||
| 	// DELETE 请求通常没有请求体,但某些 RESTful API 可能支持 | 	// DELETE 请求通常没有请求体,但某些 RESTful API 可能支持 | ||||||
| 	return c.doRequest(http.MethodDelete, path, nil, customHeaders, responseData, nil) // rawResponse 传递 nil | 	return c.DoRequest(http.MethodDelete, path, nil, customHeaders, responseData, nil) // rawResponse 传递 nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // Patch 发送 PATCH 请求 | // Patch 发送 PATCH 请求 | ||||||
| @ -250,20 +252,39 @@ func (c *HTTPClient) Delete(path string, customHeaders map[string]string, respon | |||||||
| // requestBody: 请求体数据 | // requestBody: 请求体数据 | ||||||
| // customHeaders: 自定义请求头 | // customHeaders: 自定义请求头 | ||||||
| // responseData: 用于存储响应数据的目标结构体(指针类型) | // responseData: 用于存储响应数据的目标结构体(指针类型) | ||||||
| func (c *HTTPClient) Patch(path string, requestBody interface{}, customHeaders map[string]string, responseData interface{}) error { | // returns 状态码 和 错误信息 | ||||||
| 	return c.doRequest(http.MethodPatch, path, requestBody, customHeaders, responseData, nil) // rawResponse 传递 nil | func (c *HTTPClient) Patch(path string, requestBody interface{}, customHeaders map[string]string, responseData interface{}) (int, error) { | ||||||
|  | 	return c.DoRequest(http.MethodPatch, path, requestBody, customHeaders, responseData, nil) // rawResponse 传递 nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // GetRaw 发送 GET 请求并返回原始响应体 | // GetRaw 发送 GET 请求并返回原始响应体 | ||||||
| // path: 请求路径 | // path: 请求路径 | ||||||
| // customHeaders: 自定义请求头 | // customHeaders: 自定义请求头 | ||||||
| // 返回值: 原始响应体字节切片或错误 | // 返回值: 原始响应体字节切片或错误 | ||||||
| func (c *HTTPClient) GetRaw(path string, customHeaders map[string]string) ([]byte, error) { | // 状态码 | ||||||
|  | // 错误信息 | ||||||
|  | func (c *HTTPClient) GetRaw(path string, customHeaders map[string]string) ([]byte, int, error) { | ||||||
| 	var raw []byte | 	var raw []byte | ||||||
| 	// responseData 传递 nil,rawResponse 传递 &raw | 	// responseData 传递 nil,rawResponse 传递 &raw | ||||||
| 	err := c.doRequest(http.MethodGet, path, nil, customHeaders, nil, &raw) | 	statusCode, err := c.DoRequest(http.MethodGet, path, nil, customHeaders, nil, &raw) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, statusCode, err | ||||||
| 	} | 	} | ||||||
| 	return raw, nil | 	return raw, statusCode, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // PostRaw 发送 POST 请求并返回原始响应体 | ||||||
|  | // path: 请求路径 | ||||||
|  | // customHeaders: 自定义请求头 | ||||||
|  | // 返回值: 原始响应体字节切片或错误 | ||||||
|  | // 状态码 | ||||||
|  | // 错误信息 | ||||||
|  | func (c *HTTPClient) PostRaw(path string, customHeaders map[string]string) ([]byte, int, error) { | ||||||
|  | 	var raw []byte | ||||||
|  | 	// responseData 传递 nil,rawResponse 传递 &raw | ||||||
|  | 	statusCode, err := c.DoRequest(http.MethodPost, path, nil, customHeaders, nil, &raw) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, statusCode, err | ||||||
|  | 	} | ||||||
|  | 	return raw, statusCode, nil | ||||||
| } | } | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user