| 
									
										
										
										
											2025-07-02 16:17:59 +08:00
										 |  |  |  | package sms | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | import ( | 
					
						
							|  |  |  |  | 	"context" | 
					
						
							|  |  |  |  | 	"crypto/rand" | 
					
						
							|  |  |  |  | 	"fmt" | 
					
						
							|  |  |  |  | 	"math/big" | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	"github.com/aliyun/alibaba-cloud-sdk-go/services/dysmsapi" | 
					
						
							|  |  |  |  | 	"go.uber.org/zap" | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	"tyapi-server/internal/config" | 
					
						
							|  |  |  |  | ) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-13 16:36:20 +08:00
										 |  |  |  | // AliSMSService 阿里云短信服务 | 
					
						
							| 
									
										
										
										
											2025-07-02 16:17:59 +08:00
										 |  |  |  | type AliSMSService struct { | 
					
						
							|  |  |  |  | 	client *dysmsapi.Client | 
					
						
							|  |  |  |  | 	config config.SMSConfig | 
					
						
							|  |  |  |  | 	logger *zap.Logger | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // NewAliSMSService 创建阿里云短信服务 | 
					
						
							|  |  |  |  | func NewAliSMSService(cfg config.SMSConfig, logger *zap.Logger) (*AliSMSService, error) { | 
					
						
							|  |  |  |  | 	client, err := dysmsapi.NewClientWithAccessKey("cn-hangzhou", cfg.AccessKeyID, cfg.AccessKeySecret) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		return nil, fmt.Errorf("创建短信客户端失败: %w", err) | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	return &AliSMSService{ | 
					
						
							|  |  |  |  | 		client: client, | 
					
						
							|  |  |  |  | 		config: cfg, | 
					
						
							|  |  |  |  | 		logger: logger, | 
					
						
							|  |  |  |  | 	}, nil | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // SendVerificationCode 发送验证码 | 
					
						
							|  |  |  |  | func (s *AliSMSService) SendVerificationCode(ctx context.Context, phone string, code string) error { | 
					
						
							|  |  |  |  | 	request := dysmsapi.CreateSendSmsRequest() | 
					
						
							|  |  |  |  | 	request.Scheme = "https" | 
					
						
							|  |  |  |  | 	request.PhoneNumbers = phone | 
					
						
							|  |  |  |  | 	request.SignName = s.config.SignName | 
					
						
							|  |  |  |  | 	request.TemplateCode = s.config.TemplateCode | 
					
						
							|  |  |  |  | 	request.TemplateParam = fmt.Sprintf(`{"code":"%s"}`, code) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	response, err := s.client.SendSms(request) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		s.logger.Error("Failed to send SMS", | 
					
						
							|  |  |  |  | 			zap.String("phone", phone), | 
					
						
							|  |  |  |  | 			zap.Error(err)) | 
					
						
							|  |  |  |  | 		return fmt.Errorf("短信发送失败: %w", err) | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	if response.Code != "OK" { | 
					
						
							|  |  |  |  | 		s.logger.Error("SMS send failed", | 
					
						
							|  |  |  |  | 			zap.String("phone", phone), | 
					
						
							|  |  |  |  | 			zap.String("code", response.Code), | 
					
						
							|  |  |  |  | 			zap.String("message", response.Message)) | 
					
						
							|  |  |  |  | 		return fmt.Errorf("短信发送失败: %s - %s", response.Code, response.Message) | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	s.logger.Info("SMS sent successfully", | 
					
						
							|  |  |  |  | 		zap.String("phone", phone), | 
					
						
							|  |  |  |  | 		zap.String("bizId", response.BizId)) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	return nil | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // GenerateCode 生成验证码 | 
					
						
							|  |  |  |  | func (s *AliSMSService) GenerateCode(length int) string { | 
					
						
							|  |  |  |  | 	if length <= 0 { | 
					
						
							|  |  |  |  | 		length = 6 | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// 生成指定长度的数字验证码 | 
					
						
							|  |  |  |  | 	max := big.NewInt(int64(pow10(length))) | 
					
						
							|  |  |  |  | 	n, _ := rand.Int(rand.Reader, max) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// 格式化为指定长度,不足时前面补0 | 
					
						
							|  |  |  |  | 	format := fmt.Sprintf("%%0%dd", length) | 
					
						
							|  |  |  |  | 	return fmt.Sprintf(format, n.Int64()) | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // pow10 计算10的n次方 | 
					
						
							|  |  |  |  | func pow10(n int) int { | 
					
						
							|  |  |  |  | 	result := 1 | 
					
						
							|  |  |  |  | 	for i := 0; i < n; i++ { | 
					
						
							|  |  |  |  | 		result *= 10 | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	return result | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // MockSMSService 模拟短信服务(用于开发和测试) | 
					
						
							|  |  |  |  | type MockSMSService struct { | 
					
						
							|  |  |  |  | 	logger *zap.Logger | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // NewMockSMSService 创建模拟短信服务 | 
					
						
							|  |  |  |  | func NewMockSMSService(logger *zap.Logger) *MockSMSService { | 
					
						
							|  |  |  |  | 	return &MockSMSService{ | 
					
						
							|  |  |  |  | 		logger: logger, | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // SendVerificationCode 模拟发送验证码 | 
					
						
							|  |  |  |  | func (s *MockSMSService) SendVerificationCode(ctx context.Context, phone string, code string) error { | 
					
						
							|  |  |  |  | 	s.logger.Info("Mock SMS sent", | 
					
						
							|  |  |  |  | 		zap.String("phone", phone), | 
					
						
							|  |  |  |  | 		zap.String("code", code)) | 
					
						
							|  |  |  |  | 	return nil | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // GenerateCode 生成验证码 | 
					
						
							|  |  |  |  | func (s *MockSMSService) GenerateCode(length int) string { | 
					
						
							|  |  |  |  | 	if length <= 0 { | 
					
						
							|  |  |  |  | 		length = 6 | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// 开发环境使用固定验证码便于测试 | 
					
						
							|  |  |  |  | 	result := "" | 
					
						
							|  |  |  |  | 	for i := 0; i < length; i++ { | 
					
						
							|  |  |  |  | 		result += "1" | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	return result | 
					
						
							|  |  |  |  | } |