refactor: reorganize modules and add Linux build tooling
This commit is contained in:
77
module/base/sender/internal/config/config.go
Normal file
77
module/base/sender/internal/config/config.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"git.apinb.com/bsm-sdk/core/conf"
|
||||
"git.apinb.com/bsm-sdk/core/crypto/encipher"
|
||||
"git.apinb.com/bsm-sdk/core/env"
|
||||
)
|
||||
|
||||
var (
|
||||
// Spec 全局配置实例,包含所有服务配置信息
|
||||
Spec SrvConfig
|
||||
)
|
||||
|
||||
// SrvConfig 服务配置结构体,包含所有必要的配置项
|
||||
type SrvConfig struct {
|
||||
conf.Base `yaml:",inline"` // 基础配置(端口、IP等)
|
||||
Databases *conf.DBConf `yaml:"Databases"` // 数据库配置
|
||||
MicroService *conf.MicroServiceConf `yaml:"MicroService"` // 微服务配置
|
||||
Rpc map[string]conf.RpcConf `yaml:"Rpc"` // RPC服务配置
|
||||
Gateway *conf.GatewayConf `yaml:"Gateway"` // HTTP网关配置
|
||||
Apm *conf.ApmConf `yaml:"APM"` // 应用性能监控配置
|
||||
Etcd *conf.EtcdConf `yaml:"Etcd"` // Etcd配置
|
||||
SMS map[string]*SmsConf `yaml:"SMS"` // 短信服务配置
|
||||
SMTP map[string]*SmtpConf `yaml:"SMTP"` // 邮件服务配置
|
||||
Code *codeConf `yaml:"Code"` // 验证码配置
|
||||
}
|
||||
|
||||
// SmtpConf SMTP邮件服务配置
|
||||
type SmtpConf struct {
|
||||
Endpoint string `yaml:"Endpoint"` // SMTP服务器地址
|
||||
Port int `yaml:"Port"` // SMTP服务器端口
|
||||
Username string `yaml:"Username"` // 用户名
|
||||
Password string `yaml:"Password"` // 密码
|
||||
FromAddress string `yaml:"FromAddress"` // 发件人邮箱地址
|
||||
FromName string `yaml:"FromName"` // 发件人显示名称
|
||||
}
|
||||
|
||||
// SmsConf 短信服务配置
|
||||
type SmsConf struct {
|
||||
Endpoint string `yaml:"Endpoint"` // 短信服务端点
|
||||
AccessKeyId string `yaml:"AccessKeyId"` // 访问密钥ID
|
||||
AccessKeySecret string `yaml:"AccessKeySecret"` // 访问密钥Secret
|
||||
Region string `yaml:"Region"` // 服务区域
|
||||
}
|
||||
|
||||
// codeConf 验证码相关配置
|
||||
type codeConf struct {
|
||||
Length int64 `yaml:"Length"` // 验证码长度
|
||||
Expire int `yaml:"Expire"` // 验证码过期时间(秒)
|
||||
MaxSentLimit int `yaml:"MaxSentLimit"` // 最大发送次数限制
|
||||
GenerateCode bool `yaml:"GenerateCode"` // 是否生成验证码
|
||||
CokeyKey string `yaml:"CokeyKey"` // 验证码密钥
|
||||
BlackListFilter []string `yaml:"BlackListFilter"` // 黑名单过滤器
|
||||
}
|
||||
|
||||
// New 初始化配置文件并进行必要的校验
|
||||
// srvKey: 服务标识符,用于加载对应的配置文件
|
||||
func New(srvKey string) {
|
||||
// 初始化配置,创建一个新的配置实例,用于服务配置
|
||||
conf.New(srvKey, &Spec)
|
||||
|
||||
// 配置校验:服务IP和端口,如果端口不合规则随机分配端口
|
||||
Spec.Port = conf.CheckPort(Spec.Port)
|
||||
Spec.BindIP = conf.CheckIP(Spec.BindIP)
|
||||
Spec.Addr = net.JoinHostPort(Spec.BindIP, Spec.Port)
|
||||
|
||||
// 配置校验:服务名称地址及监听地址不能为空
|
||||
conf.NotNil(Spec.Service, Spec.Cache)
|
||||
|
||||
// 初始化JWT加密密钥
|
||||
encipher.New(env.Runtime.JwtSecretKey)
|
||||
|
||||
// 打印服务启动信息
|
||||
conf.PrintInfo(Spec.Addr)
|
||||
}
|
||||
17
module/base/sender/internal/excode/ex.go
Normal file
17
module/base/sender/internal/excode/ex.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package excode
|
||||
|
||||
import "git.apinb.com/bsm-sdk/core/errcode"
|
||||
|
||||
var (
|
||||
ErrNotProvider = errcode.NewError(1600, "Not Provider")
|
||||
ErrProviderIsNil = errcode.NewError(1601, "Provider Is Nil")
|
||||
ErrAppName = errcode.NewError(1602, "params app name is must required")
|
||||
ErrPhone = errcode.NewError(1603, "params phone is must required")
|
||||
ErrTemplate = errcode.NewError(1604, "params template code is must required")
|
||||
ErrMustWhiteList = errcode.NewError(1605, "params phone must in white list")
|
||||
ErrInBlackList = errcode.NewError(1606, "params phone is in black list")
|
||||
ErrSentLimit = errcode.NewError(1607, "This account has reached the sending limit today")
|
||||
ErrExpired = errcode.NewError(1608, "Not found or expired")
|
||||
ErrCode = errcode.NewError(1609, "code error")
|
||||
ErrEmail = errcode.NewError(1610, "email format error")
|
||||
)
|
||||
31
module/base/sender/internal/impl/impl.go
Normal file
31
module/base/sender/internal/impl/impl.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package impl
|
||||
|
||||
import (
|
||||
"bsm/full/module/base/sender/internal/config"
|
||||
"git.apinb.com/bsm-sdk/core/cache/redis"
|
||||
"git.apinb.com/bsm-sdk/core/with"
|
||||
cache "github.com/patrickmn/go-cache"
|
||||
clientv3 "go.etcd.io/etcd/client/v3"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var (
|
||||
RedisService *redis.RedisClient // Redis 缓存服务客户端
|
||||
EtcdService *clientv3.Client // Etcd 客户端
|
||||
DBService *gorm.DB // 数据库服务
|
||||
MemorySerice *cache.Cache // 内存缓存服务(BigCache)
|
||||
)
|
||||
|
||||
// NewImpl 初始化所有依赖服务(内存、Redis、数据库、Etcd)
|
||||
func NewImpl() {
|
||||
// 初始化内存缓存服务
|
||||
MemorySerice = with.Memory(nil)
|
||||
// 初始化 Redis 缓存服务
|
||||
RedisService = with.RedisCache(config.Spec.Cache)
|
||||
// 初始化数据库服务
|
||||
DBService = with.Databases(config.Spec.Databases, nil)
|
||||
// 初始化 Etcd 客户端
|
||||
EtcdService = with.Etcd(config.Spec.Etcd)
|
||||
// 初始化服务提供商
|
||||
withProvider()
|
||||
}
|
||||
92
module/base/sender/internal/impl/provider.go
Normal file
92
module/base/sender/internal/impl/provider.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package impl
|
||||
|
||||
import (
|
||||
"net/smtp"
|
||||
"strings"
|
||||
|
||||
"bsm/full/module/base/sender/internal/config"
|
||||
AliYunClient "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||
dysmsapi "github.com/alibabacloud-go/dysmsapi-20180501/v2/client"
|
||||
"github.com/aliyun/credentials-go/credentials"
|
||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
|
||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
|
||||
TencentCloud "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/sms/v20210111"
|
||||
)
|
||||
|
||||
var (
|
||||
Provider *ProviderClient
|
||||
)
|
||||
|
||||
type ProviderClient struct {
|
||||
Aliyun *dysmsapi.Client
|
||||
Google *smtp.Client
|
||||
QQ *smtp.Client
|
||||
Tencent *TencentCloud.Client
|
||||
}
|
||||
|
||||
func (p *ProviderClient) init() {
|
||||
Provider = &ProviderClient{}
|
||||
}
|
||||
|
||||
func withProvider() {
|
||||
Provider.init()
|
||||
for key, conf := range config.Spec.SMTP {
|
||||
switch strings.ToLower(key) {
|
||||
case "google":
|
||||
Provider.Google = NewSMTP(conf)
|
||||
case "qq":
|
||||
Provider.QQ = NewSMTP(conf)
|
||||
}
|
||||
}
|
||||
|
||||
for key, conf := range config.Spec.SMS {
|
||||
switch strings.ToLower(key) {
|
||||
case "aliyun":
|
||||
Provider.Aliyun = NewAliyun(conf)
|
||||
case "tencent":
|
||||
Provider.Tencent = NewTencent(conf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func NewSMTP(conf *config.SmtpConf) *smtp.Client {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewAliyun(conf *config.SmsConf) *dysmsapi.Client {
|
||||
|
||||
config := new(credentials.Config).
|
||||
SetType("access_key").
|
||||
SetAccessKeyId(conf.AccessKeyId).
|
||||
SetAccessKeySecret(conf.AccessKeySecret)
|
||||
|
||||
akCredential, err := credentials.NewCredential(config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
cfg := &AliYunClient.Config{
|
||||
Endpoint: &conf.Endpoint,
|
||||
Credential: akCredential,
|
||||
}
|
||||
|
||||
client, err := dysmsapi.NewClient(cfg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
func NewTencent(conf *config.SmsConf) *TencentCloud.Client {
|
||||
credential := common.NewCredential(conf.AccessKeyId, conf.AccessKeySecret)
|
||||
clientProfile := profile.NewClientProfile()
|
||||
clientProfile.HttpProfile.Endpoint = conf.Endpoint
|
||||
client, err := TencentCloud.NewClient(credential, conf.Region, clientProfile)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return client
|
||||
}
|
||||
152
module/base/sender/internal/logic/mail/send.go
Normal file
152
module/base/sender/internal/logic/mail/send.go
Normal file
@@ -0,0 +1,152 @@
|
||||
package mail
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/mail"
|
||||
"net/smtp"
|
||||
"strings"
|
||||
|
||||
"bsm/full/module/base/sender/internal/config"
|
||||
"bsm/full/module/base/sender/internal/excode"
|
||||
"bsm/full/module/base/sender/internal/impl"
|
||||
"bsm/full/module/base/sender/internal/models"
|
||||
pb "bsm/full/module/base/sender/pb"
|
||||
"git.apinb.com/bsm-sdk/core/errcode"
|
||||
"git.apinb.com/bsm-sdk/core/vars"
|
||||
)
|
||||
|
||||
func Send(ctx context.Context, in *pb.SendMailRequest) (reply *pb.SendMailReply, err error) {
|
||||
provider := strings.ToLower(in.GetProvider())
|
||||
|
||||
// 校验参数
|
||||
if in.GetTemplateKey() == "" || provider == "" || in.GetTo() == "" {
|
||||
return nil, errcode.ErrInvalidArgument
|
||||
}
|
||||
|
||||
// 校验配置
|
||||
cfg, ok := config.Spec.SMTP[provider]
|
||||
if !ok || cfg == nil {
|
||||
return nil, excode.ErrProviderIsNil
|
||||
}
|
||||
|
||||
// 检验邮箱格式
|
||||
if !ValidateEmail(in.GetTo()) {
|
||||
return nil, excode.ErrEmail
|
||||
}
|
||||
|
||||
// 获取模板
|
||||
var tplRecord models.SenderTemplate
|
||||
err = impl.DBService.Where("key=?", in.GetTemplateKey()).First(&tplRecord).Error
|
||||
if err != nil {
|
||||
return nil, errcode.ErrNotFound(1404, "template")
|
||||
}
|
||||
|
||||
// 解析模板
|
||||
tmpl, err := template.New("page").Parse(tplRecord.Body)
|
||||
if err != nil {
|
||||
return nil, excode.ErrTemplate
|
||||
}
|
||||
|
||||
// 发送邮件
|
||||
switch provider {
|
||||
case "qq":
|
||||
err = QQ(cfg, tmpl, in.GetTo(), tplRecord.Subjet, in.GetParamters())
|
||||
default:
|
||||
return nil, excode.ErrNotProvider
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pb.SendMailReply{
|
||||
Data: vars.OK,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ValidateEmail(s string) bool {
|
||||
_, err := mail.ParseAddress(s)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func QQ(cfg *config.SmtpConf, tmpl *template.Template, to string, subject string, args map[string]string) error {
|
||||
// 建立TLS加密连接
|
||||
conn, err := tls.Dial("tcp", fmt.Sprintf("%s:%d", cfg.Endpoint, cfg.Port), &tls.Config{
|
||||
ServerName: cfg.Endpoint,
|
||||
MinVersion: tls.VersionTLS12, // 强制TLS1.2+
|
||||
InsecureSkipVerify: false,
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Println("TLS连接失败: ", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// 创建SMTP客户端(带超时控制)
|
||||
client, err := smtp.NewClient(conn, cfg.Endpoint)
|
||||
if err != nil {
|
||||
client.Close()
|
||||
fmt.Println("SMTP客户端初始化失败: ", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// 设置认证
|
||||
auth := smtp.PlainAuth("", cfg.Username, cfg.Password, cfg.Endpoint)
|
||||
if err := client.Auth(auth); err != nil {
|
||||
client.Close()
|
||||
fmt.Println("认证失败: ", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// 设置发件人和收件人
|
||||
err = client.Mail(cfg.FromAddress)
|
||||
if err != nil {
|
||||
client.Close()
|
||||
fmt.Println("发件人设置失败: ", err)
|
||||
return err
|
||||
}
|
||||
err = client.Rcpt(to)
|
||||
if err != nil {
|
||||
fmt.Println("发件人设置失败: ", err)
|
||||
client.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
// 创建一个写入SMTP服务器的标准写入器
|
||||
writer, err := client.Data()
|
||||
if err != nil {
|
||||
client.Close()
|
||||
return err
|
||||
}
|
||||
defer writer.Close()
|
||||
|
||||
// 构建邮件正文
|
||||
mailBody := "From: " + cfg.FromName + "<" + cfg.FromAddress + ">\n"
|
||||
mailBody += "To: " + to + "\n"
|
||||
mailBody += "Subject: " + subject + "\n\n"
|
||||
|
||||
// 执行模板,将结果写入邮件正文
|
||||
var buf bytes.Buffer
|
||||
err = tmpl.Execute(&buf, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mailBody += buf.String()
|
||||
|
||||
// 将邮件正文发送到SMTP服务器
|
||||
if _, err := writer.Write([]byte(mailBody)); err != nil {
|
||||
client.Close()
|
||||
fmt.Printf("写入邮件内容失败: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := writer.Close(); err != nil {
|
||||
client.Close()
|
||||
fmt.Printf("关闭数据流失败: %v", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
8
module/base/sender/internal/logic/sms/const.go
Normal file
8
module/base/sender/internal/logic/sms/const.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package sms
|
||||
|
||||
const (
|
||||
FormatDay = "2006-01-02"
|
||||
KeyPrefix = "/SMS/Code/"
|
||||
BlackListCacheKey = "/SMS/BlackList/"
|
||||
LimitCacheKey = "/SMS/LimitCacheKey/"
|
||||
)
|
||||
176
module/base/sender/internal/logic/sms/send.go
Normal file
176
module/base/sender/internal/logic/sms/send.go
Normal file
@@ -0,0 +1,176 @@
|
||||
package sms
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"bsm/full/module/base/sender/internal/config"
|
||||
"bsm/full/module/base/sender/internal/excode"
|
||||
"bsm/full/module/base/sender/internal/impl"
|
||||
pb "bsm/full/module/base/sender/pb"
|
||||
"git.apinb.com/bsm-sdk/core/cache/redis"
|
||||
"git.apinb.com/bsm-sdk/core/errcode"
|
||||
AliYunClient "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||
AliYunUtil "github.com/alibabacloud-go/darabonba-openapi/v2/utils"
|
||||
"github.com/alibabacloud-go/tea/dara"
|
||||
"github.com/alibabacloud-go/tea/tea"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
func Send(ctx context.Context, in *pb.SmsSendRequest) (reply *pb.SmsReply, err error) {
|
||||
var smsCode string
|
||||
|
||||
if in.GetPhone() == "" || !VerifyPhone(in.GetPhone()) {
|
||||
return nil, excode.ErrPhone
|
||||
}
|
||||
if in.GetTemplateCode() == "" {
|
||||
return nil, excode.ErrTemplate
|
||||
}
|
||||
|
||||
// 是否验证黑名单
|
||||
if impl.RedisService.Client.SIsMember(impl.RedisService.Ctx, BlackListCacheKey, in.GetPhone()).Val() {
|
||||
return nil, excode.ErrInBlackList
|
||||
}
|
||||
|
||||
// 每天限制
|
||||
limitKey := LimitCacheKey + time.Now().Format(FormatDay) + in.GetPhone()
|
||||
|
||||
// check limit
|
||||
twice, err := impl.RedisService.Client.Get(impl.RedisService.Ctx, limitKey).Int()
|
||||
if err != nil && !errors.Is(err, redis.Nil) {
|
||||
return nil, errcode.ErrRedis
|
||||
}
|
||||
if twice > config.Spec.Code.MaxSentLimit {
|
||||
return nil, excode.ErrSentLimit
|
||||
}
|
||||
|
||||
// 从redis获取验证码,如果没有重新生成
|
||||
key := KeyPrefix + in.GetPhone()
|
||||
if in.GetIsGenCode() {
|
||||
//验证码最少4位,最大10位。
|
||||
if config.Spec.Code.Length < 4 || config.Spec.Code.Length > 10 {
|
||||
config.Spec.Code.Length = 6
|
||||
}
|
||||
|
||||
// 新生成验证码
|
||||
smsCode = GenValidateCode(config.Spec.Code.Length)
|
||||
//sms code write to redis
|
||||
expire := time.Second * time.Duration(config.Spec.Code.Expire)
|
||||
impl.RedisService.Client.SetNX(impl.RedisService.Ctx, key, smsCode, expire)
|
||||
} else {
|
||||
// 获取验证码
|
||||
if code, ok := in.Paramters["code"]; ok {
|
||||
smsCode = code
|
||||
} else {
|
||||
return nil, excode.ErrCode
|
||||
}
|
||||
}
|
||||
|
||||
var result map[string]interface{}
|
||||
switch strings.ToLower(in.GetProvider()) {
|
||||
case "aliyun":
|
||||
if impl.Provider.Aliyun == nil {
|
||||
return nil, excode.ErrProviderIsNil
|
||||
}
|
||||
result, err = AliyunSender(in, smsCode)
|
||||
case "tencent":
|
||||
if impl.Provider.Tencent == nil {
|
||||
return nil, excode.ErrProviderIsNil
|
||||
}
|
||||
result, err = TencentSender(in)
|
||||
default:
|
||||
return nil, excode.ErrNotProvider
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
jsonBytes, _ := json.Marshal(result)
|
||||
fmt.Println("短信发送结果:", string(jsonBytes))
|
||||
return &pb.SmsReply{
|
||||
Reply: string(jsonBytes),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func AliyunSender(args *pb.SmsSendRequest, code string) (map[string]any, error) {
|
||||
// 构建发送参数
|
||||
var templateParam = map[string]any{
|
||||
"code": code,
|
||||
}
|
||||
for key, val := range args.Paramters {
|
||||
templateParam[key] = val
|
||||
}
|
||||
jsonBytes, _ := json.Marshal(templateParam)
|
||||
|
||||
params := map[string]any{
|
||||
// 必填,接收短信的手机号码
|
||||
"PhoneNumbers": tea.String(cast.ToString(args.Phone)),
|
||||
// 必填,短信签名名称
|
||||
"SignName": tea.String(cast.ToString(args.SignName)),
|
||||
// 必填,短信模板ID
|
||||
"TemplateCode": tea.String(cast.ToString(args.TemplateCode)),
|
||||
// 可选,模板参数
|
||||
"TemplateParam": string(jsonBytes),
|
||||
}
|
||||
|
||||
runtime := &dara.RuntimeOptions{}
|
||||
request := &AliYunClient.OpenApiRequest{
|
||||
Query: AliYunUtil.Query(params),
|
||||
}
|
||||
|
||||
clientParams := &AliYunClient.Params{
|
||||
// 接口名称
|
||||
Action: tea.String("SendSms"),
|
||||
// 接口版本
|
||||
Version: tea.String("2017-05-25"),
|
||||
// 接口协议
|
||||
Protocol: tea.String("HTTPS"),
|
||||
// 接口 HTTP 方法
|
||||
Method: tea.String("POST"),
|
||||
AuthType: tea.String("AK"),
|
||||
Style: tea.String("RPC"),
|
||||
// 接口 PATH
|
||||
Pathname: tea.String("/"),
|
||||
// 接口请求体内容格式
|
||||
ReqBodyType: tea.String("json"),
|
||||
// 接口响应体内容格式
|
||||
BodyType: tea.String("json"),
|
||||
}
|
||||
fmt.Println("请求参数params为:", params)
|
||||
fmt.Println("请求参数clientParams为:", clientParams)
|
||||
return impl.Provider.Aliyun.CallApi(clientParams, request, runtime)
|
||||
}
|
||||
|
||||
func TencentSender(args *pb.SmsSendRequest) (map[string]any, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func VerifyPhone(phone string) bool {
|
||||
result, _ := regexp.MatchString(`^(1[3|4|5|6|7|8|9][0-9]\d{4,8})$`, phone)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// GenValidateCode .
|
||||
func GenValidateCode(width int64) string {
|
||||
if width == 0 {
|
||||
width = 4
|
||||
}
|
||||
l := 10
|
||||
numeric := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
var sb strings.Builder
|
||||
for i := int64(0); i < width; i++ {
|
||||
fmt.Fprintf(&sb, "%d", numeric[rand.Intn(l)])
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
40
module/base/sender/internal/logic/sms/verify.go
Normal file
40
module/base/sender/internal/logic/sms/verify.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package sms
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"bsm/full/module/base/sender/internal/excode"
|
||||
"bsm/full/module/base/sender/internal/impl"
|
||||
pb "bsm/full/module/base/sender/pb"
|
||||
"git.apinb.com/bsm-sdk/core/errcode"
|
||||
)
|
||||
|
||||
func Verify(ctx context.Context, in *pb.SmsVerifyRequest) (reply *pb.SmsReply, err error) {
|
||||
if in.GetCode() == "" {
|
||||
return nil, excode.ErrCode
|
||||
}
|
||||
|
||||
if in.GetPhone() == "" {
|
||||
return nil, excode.ErrPhone
|
||||
}
|
||||
|
||||
//check redis
|
||||
key := KeyPrefix + in.GetPhone()
|
||||
code, err := impl.RedisService.Client.Get(impl.RedisService.Ctx, key).Result()
|
||||
if err != nil {
|
||||
return nil, errcode.NewError(1311, err.Error())
|
||||
}
|
||||
//check code
|
||||
if code == in.Code {
|
||||
return &pb.SmsReply{
|
||||
Reply: "true",
|
||||
}, nil
|
||||
}
|
||||
|
||||
//verify pass ; delete the requestId
|
||||
impl.RedisService.Client.Del(impl.RedisService.Ctx, key)
|
||||
|
||||
return &pb.SmsReply{
|
||||
Reply: "false",
|
||||
}, nil
|
||||
}
|
||||
18
module/base/sender/internal/models/sender_template.go
Normal file
18
module/base/sender/internal/models/sender_template.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"git.apinb.com/bsm-sdk/core/database"
|
||||
"git.apinb.com/bsm-sdk/core/types"
|
||||
)
|
||||
|
||||
type SenderTemplate struct {
|
||||
types.Std_IICUDS
|
||||
Title string `gorm:"type:varchar(255);not null;default:'';comment:模板标题"`
|
||||
Key string `gorm:"type:varchar(100);not null;uniqueIndex;comment:模板标识"`
|
||||
Subjet string `gorm:"type:varchar(255);not null;default:'';comment:邮件主题"`
|
||||
Body string `gorm:"type:text;not null;comment:模板内容"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
database.AppendMigrate(&SenderTemplate{})
|
||||
}
|
||||
20
module/base/sender/internal/server/mail_server.go
Normal file
20
module/base/sender/internal/server/mail_server.go
Normal file
@@ -0,0 +1,20 @@
|
||||
// Code generated by protoc-gen-slc. DO NOT EDIT.
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"bsm/full/module/base/sender/internal/logic/mail"
|
||||
pb "bsm/full/module/base/sender/pb"
|
||||
)
|
||||
|
||||
type MailServer struct {
|
||||
pb.UnimplementedMailServer
|
||||
}
|
||||
|
||||
func NewMailServer() *MailServer {
|
||||
return &MailServer{}
|
||||
}
|
||||
|
||||
func (s *MailServer) Send(ctx context.Context, in *pb.SendMailRequest) (*pb.SendMailReply, error) {
|
||||
return mail.Send(ctx, in)
|
||||
}
|
||||
96
module/base/sender/internal/server/new.go
Normal file
96
module/base/sender/internal/server/new.go
Normal file
@@ -0,0 +1,96 @@
|
||||
// Code generated by protoc-gen-slc. DO NOT EDIT.
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
pb "bsm/full/module/base/sender/pb"
|
||||
"git.apinb.com/bsm-sdk/core/vars"
|
||||
gwRuntime "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/reflection"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
Grpc *grpc.Server
|
||||
Ctx context.Context
|
||||
Mux *gwRuntime.ServeMux
|
||||
grpcConns map[string]*grpc.ClientConn // 连接池
|
||||
}
|
||||
|
||||
func New(addr string) *Server {
|
||||
srv := &Server{
|
||||
Ctx: context.Background(),
|
||||
Grpc: grpc.NewServer(),
|
||||
Mux: gwRuntime.NewServeMux(gwRuntime.WithForwardResponseRewriter(responseEnvelope)),
|
||||
grpcConns: make(map[string]*grpc.ClientConn),
|
||||
}
|
||||
|
||||
// register service to grpc.Server
|
||||
pb.RegisterMailServer(srv.Grpc, NewMailServer())
|
||||
pb.RegisterSmsServer(srv.Grpc, NewSmsServer())
|
||||
|
||||
reflection.Register(srv.Grpc)
|
||||
|
||||
// 连接池: 只创建一次连接并复用
|
||||
conn, ok := srv.grpcConns[addr]
|
||||
if !ok {
|
||||
var err error
|
||||
conn, err = grpc.NewClient(addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
if err != nil {
|
||||
panic("failed to dial grpc server: " + err.Error())
|
||||
}
|
||||
srv.grpcConns[addr] = conn
|
||||
}
|
||||
|
||||
// 将服务注册到Gateway
|
||||
|
||||
if err := pb.RegisterMailHandler(srv.Ctx, srv.Mux, conn); err != nil {
|
||||
panic("Failed to register Mail handler: " + err.Error())
|
||||
}
|
||||
|
||||
if err := pb.RegisterSmsHandler(srv.Ctx, srv.Mux, conn); err != nil {
|
||||
panic("Failed to register Sms handler: " + err.Error())
|
||||
}
|
||||
|
||||
// Register services swagger
|
||||
srv.RegisterSwagger()
|
||||
|
||||
return srv
|
||||
}
|
||||
|
||||
// RegisterSwagger 注册swagger
|
||||
func (s *Server) RegisterSwagger() {
|
||||
srvKey := strings.ToLower(vars.ServiceKey)
|
||||
s.Mux.HandlePath("GET", "/"+srvKey+".swagger.json", func(w http.ResponseWriter, r *http.Request, pathParams map[string]string) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
bytes, err := os.ReadFile("./swagger/" + srvKey + ".swagger.json")
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
w.Write(bytes)
|
||||
return
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
// response envelope
|
||||
func responseEnvelope(_ context.Context, response proto.Message) (interface{}, error) {
|
||||
name := string(response.ProtoReflect().Descriptor().Name())
|
||||
if name == "Status" || name == "Error" || name == "StatusReply" {
|
||||
return response, nil
|
||||
}
|
||||
return map[string]any{
|
||||
"code": 0,
|
||||
"message": vars.OK,
|
||||
"details": response,
|
||||
"timeseq": time.Now().Unix(),
|
||||
}, nil
|
||||
}
|
||||
24
module/base/sender/internal/server/sms_server.go
Normal file
24
module/base/sender/internal/server/sms_server.go
Normal file
@@ -0,0 +1,24 @@
|
||||
// Code generated by protoc-gen-slc. DO NOT EDIT.
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"bsm/full/module/base/sender/internal/logic/sms"
|
||||
pb "bsm/full/module/base/sender/pb"
|
||||
)
|
||||
|
||||
type SmsServer struct {
|
||||
pb.UnimplementedSmsServer
|
||||
}
|
||||
|
||||
func NewSmsServer() *SmsServer {
|
||||
return &SmsServer{}
|
||||
}
|
||||
|
||||
func (s *SmsServer) Send(ctx context.Context, in *pb.SmsSendRequest) (*pb.SmsReply, error) {
|
||||
return sms.Send(ctx, in)
|
||||
}
|
||||
|
||||
func (s *SmsServer) Verify(ctx context.Context, in *pb.SmsVerifyRequest) (*pb.SmsReply, error) {
|
||||
return sms.Verify(ctx, in)
|
||||
}
|
||||
Reference in New Issue
Block a user